Compare commits
734 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a442a1f3ca | |||
| bc475373fc | |||
| 498e958eca | |||
| 82e0cfe0b4 | |||
| 31d89e3349 | |||
| a3775c1f88 | |||
| 834269da67 | |||
| a9bebf03d5 | |||
| 939f6304bf | |||
| 6e9f466570 | |||
| a0b5cc7a5c | |||
| 82708d998d | |||
| 613e444490 | |||
| f493e201d4 | |||
| 2a8013b1d6 | |||
| 54fed62507 | |||
| e51bec9ce4 | |||
| e47a83b706 | |||
| 8029c7b00c | |||
| 2b38dc2575 | |||
| 46cafb4cbe | |||
| ff96533c93 | |||
| 89e73f6383 | |||
| 069ebfe24e | |||
| 8b374c4734 | |||
| 1febade083 | |||
| deedd85914 | |||
| 218c27adae | |||
| fde48b6f1a | |||
| f2a36e73d0 | |||
| c5429ad139 | |||
| 7bc2ee296c | |||
| 4e26e4ab31 | |||
| a91685b590 | |||
| 06f73c9b0a | |||
| 062dc29fd4 | |||
| 4096a24306 | |||
| 2563de3ba3 | |||
| 4a99b42fa8 | |||
| f12f119ce2 | |||
| 775adfaad5 | |||
| 2a9c319935 | |||
| fda44c41a8 | |||
| 8247f5f215 | |||
| 7eba6848bb | |||
| 6e84cac0d2 | |||
| 2bf96ea781 | |||
| d5de4a8171 | |||
| 3cd7c7f1a3 | |||
| 1741ebf8b2 | |||
| 55776f8beb | |||
| a469770e5d | |||
| 851dabc18b | |||
| dccdbaeb73 | |||
| f4126f5378 | |||
| 97af075c7d | |||
| 1dfdd422db | |||
| 072082c56a | |||
| 43caa2a55c | |||
| b8bad9b6bf | |||
| 8afdd6ed2b | |||
| 34968a6945 | |||
| 851a79a77e | |||
| 1ca1db705f | |||
| 8f57c2cce2 | |||
| 942e3093cb | |||
| f3e65010f1 | |||
| b3cfb35fac | |||
| b60d778303 | |||
| 9ec28aa661 | |||
| 98400a9d3c | |||
| ae8658ae77 | |||
| e928fe3ecd | |||
| 963dc4a404 | |||
| 04f42999df | |||
| 730d014f13 | |||
| 1f81c50ce1 | |||
| f60c550879 | |||
| 0d556b5d95 | |||
| 70d897f09c | |||
| 9accb6d584 | |||
| 843dc580c8 | |||
| 77aa4773ef | |||
| fc3c944e3d | |||
| 7d6aecc4c7 | |||
| 528beb8384 | |||
| 5f1180a5dd | |||
| a9f1e8587c | |||
| 7a076f5509 | |||
| 3349e51655 | |||
| 77a366065d | |||
| c2d6ecaae6 | |||
| c9bc830058 | |||
| 6125031d9d | |||
| c041cde574 | |||
| bd946af229 | |||
| 559626456a | |||
| c6843c1d95 | |||
| 0d32fab434 | |||
| 94571de00f | |||
| d062eff8b4 | |||
| b1e46faf6f | |||
| 04ed5b2e03 | |||
| 15d9ff503f | |||
| 64942d77e4 | |||
| f287dbd4d3 | |||
| c387f57d9c | |||
| 2dacb91b79 | |||
| d809568cc3 | |||
| beee44df14 | |||
| f651fc4b50 | |||
| 596e4eae0e | |||
| 6e70073ae4 | |||
| 1beef2b4ad | |||
| a0e4ed8371 | |||
| f40adfd9c6 | |||
| 7fb5e95809 | |||
| 03d5cb9289 | |||
| 3b4b4d6b7e | |||
| b9a97a0fda | |||
| 0dae6942cb | |||
| 8bb6aeb526 | |||
| 32ec420248 | |||
| 1925537da0 | |||
| 3bb4c21fa2 | |||
| 5b61a98196 | |||
| 7c6eba983a | |||
| 0023ab09ed | |||
| c289f5d717 | |||
| 6df3ad722c | |||
| 56f6abd858 | |||
| c326e0ba81 | |||
| 60b28fcf2e | |||
| a9ffd3bd3b | |||
| bc72e925df | |||
| 108df99633 | |||
| ba245ea266 | |||
| ec721ce172 | |||
| 3ad2a95c10 | |||
| e22e241d06 | |||
| cd70d55b4d | |||
| b6c98d3bde | |||
| 5fe605540d | |||
| c232b64f24 | |||
| f94b6dbaa5 | |||
| 928bc5df6c | |||
| a5865b3545 | |||
| 8f15ab7ccd | |||
| 131e4124f3 | |||
| ae6333c7b5 | |||
| d38c622d9b | |||
| fe625e5b55 | |||
| 38a6ad552b | |||
| 222a008514 | |||
| 56684bdf4c | |||
| eb4525c68d | |||
| 66eb2ed0f9 | |||
| 9866842e01 | |||
| e9487b0481 | |||
| 82c5aa907b | |||
| 47b145cb90 | |||
| ff46b925b2 | |||
| 8ca7ff5ae0 | |||
| 70550a21a0 | |||
| 50f911f63c | |||
| 4b0dca5823 | |||
| 0e777f04bc | |||
| b12f27cc18 | |||
| 1a66f457af | |||
| b4dca6a1b4 | |||
| 7759a2f9ea | |||
| fe9bccb91f | |||
| fc6fd310f6 | |||
| 8be161b381 | |||
| 8eba8cb40b | |||
| 44326344e6 | |||
| 17b7ee9045 | |||
| 6a47a2150f | |||
| 13728a7540 | |||
| c51e941413 | |||
| 2a05c32a2f | |||
| 353a507914 | |||
| f3b73d54f8 | |||
| 92ed9b8070 | |||
| 9c3c37bc3e | |||
| 79fb7c654a | |||
| 520b4e0930 | |||
| 45a07206c9 | |||
| ad0b78936a | |||
| 2babae40de | |||
| 17713a9f93 | |||
| 48120a4a38 | |||
| 69ffd795c9 | |||
| 1df5dd5458 | |||
| b77acaa3b2 | |||
| d8024ab488 | |||
| 064d8b3506 | |||
| 9ca6a2116b | |||
| 449c87982c | |||
| 7edfc40b9d | |||
| 9acda97f54 | |||
| 14edd63029 | |||
| f3a1235dd3 | |||
| de93bfee2f | |||
| 6eb80ace00 | |||
| 3ac8d6a8b1 | |||
| 269c8dc4e3 | |||
| c8173005a7 | |||
| ed846ea564 | |||
| f6a8f5c6b5 | |||
| 3c60a7d842 | |||
| 3c97feeaf8 | |||
| b2ac91f7db | |||
| 1ebad39fc1 | |||
| 38a2edff06 | |||
| b2cf38798e | |||
| ed83e41f19 | |||
| 6d52cdba0a | |||
| 05449c9e86 | |||
| 33ef1297ba | |||
| 3b13786990 | |||
| 222d06898e | |||
| 4438adad24 | |||
| 986e474657 | |||
| e8288a0df9 | |||
| 74cab8f4ad | |||
| 5e35572de8 | |||
| 7e0e02bafc | |||
| a301532443 | |||
| 940448f219 | |||
| 8510deb6aa | |||
| 0d0ba5d3bf | |||
| 036371dd76 | |||
| 5aae584ffe | |||
| 201c619915 | |||
| 7cd6a3bb79 | |||
| ca36d4797d | |||
| b5fe3bcbf9 | |||
| e43f9e76f6 | |||
| b8901c3edd | |||
| d28938f8af | |||
| 9f108220f5 | |||
| ffe53a7196 | |||
| 9e12849107 | |||
| 0a8c093682 | |||
| 60056e1654 | |||
| 11a752a99d | |||
| 68097f61eb | |||
| 8b514b07dc | |||
| 8e6010bbe5 | |||
| 95eb07ca79 | |||
| e6e03e78ea | |||
| d2c572414c | |||
| d2e0d5b32b | |||
| d4ba227a44 | |||
| caba007899 | |||
| d5cfe9b8e9 | |||
| f1707236fa | |||
| c55cbdb69a | |||
| 00b69dcc68 | |||
| cd74e33c54 | |||
| 3f14e5dfa5 | |||
| 8c9e6ea79a | |||
| c71de31f57 | |||
| ac869bf06e | |||
| 3175bc0439 | |||
| 23ac6ec957 | |||
| f080a59b41 | |||
| 603200ed8b | |||
| b0774052a0 | |||
| 7e163ce626 | |||
| d19654cf15 | |||
| 838d8be08b | |||
| 2de82b1223 | |||
| 650012fb08 | |||
| a1ef3466ad | |||
| 97421feb33 | |||
| 0c90af6515 | |||
| 9cebd0298c | |||
| 9afcddca4f | |||
| 02fb7eedba | |||
| 4aa9bec15c | |||
| a6eeaa5b3e | |||
| c462325ce6 | |||
| d208b0ab19 | |||
| ab4ef429db | |||
| 86473e022e | |||
| fd89f569d0 | |||
| eefc765823 | |||
| ebccb2516b | |||
| 8c62a40da9 | |||
| d56af5c38f | |||
| 39b1ec61ba | |||
| cb613cf7df | |||
| 28e33b4c36 | |||
| 855e6b8180 | |||
| d62161f529 | |||
| 71d48411f1 | |||
| 731842e09c | |||
| 61169f87c0 | |||
| 9fb3b196d2 | |||
| 867b875cf9 | |||
| 3875c8c4ce | |||
| 89b959d3f5 | |||
| d62e50d6f4 | |||
| 16836a2b49 | |||
| f5651f26a5 | |||
| 82ff59c857 | |||
| 8af61041f0 | |||
| 2a9136b56f | |||
| 64da6c811d | |||
| e6b93e0d92 | |||
| f874219a64 | |||
| b4822740f4 | |||
| af205a50b4 | |||
| 2f6eaf79bd | |||
| 625f1e700f | |||
| 897d5b0b11 | |||
| 95641e2f4e | |||
| cd856b86c7 | |||
| c00aa6d627 | |||
| 398a3fb0bc | |||
| e0fa638ad9 | |||
| 4e42cbd4ce | |||
| b6c6be77cd | |||
| 0964293a72 | |||
| c8b6141ce0 | |||
| 948540369f | |||
| 363df0ad6f | |||
| a37e105434 | |||
| aeea0c00c3 | |||
| 137352674e | |||
| 4734552954 | |||
| 879c2f1ec4 | |||
| 7dc9d2a352 | |||
| cabc470ebd | |||
| 0bf1f493aa | |||
| 705bd14ee4 | |||
| 155955e49b | |||
| c76a793b18 | |||
| 50cc8501a0 | |||
| 209279e3e4 | |||
| 41239572a5 | |||
| 106ab47c3d | |||
| a84f9b60e5 | |||
| 4481e8634a | |||
| 3e432682fb | |||
| 05569c03a4 | |||
| 2d567b84be | |||
| e2a378250f | |||
| e2083a1836 | |||
| 334946ab59 | |||
| 8c9bb98125 | |||
| 726f0f3d3c | |||
| 50e5898692 | |||
| de05a5f674 | |||
| 31b57fae50 | |||
| 2f686057f3 | |||
| 132251341f | |||
| 2bac5f933a | |||
| 2e565aa83a | |||
| 4e9d0f4861 | |||
| 7216b193e8 | |||
| c33a5bf814 | |||
| 97756a5196 | |||
| 377f7d23e3 | |||
| 7005202384 | |||
| 99e8f57bac | |||
| afddf4168e | |||
| fbffdc0c9f | |||
| e6d3647490 | |||
| 13363ff363 | |||
| 7f98e4b1eb | |||
| 408460b0ae | |||
| b69ef5835d | |||
| 0428fa0912 | |||
| 9f3124fa56 | |||
| fbbdab73c6 | |||
| ee9441c521 | |||
| a9e0fd5d9b | |||
| 98464889ca | |||
| eed5fd60c6 | |||
| ac43cd5496 | |||
| 1f16a7c808 | |||
| 39e4c70754 | |||
| 82eb27af4c | |||
| 3aaab94b39 | |||
| 07a0779ca4 | |||
| 2adba02a38 | |||
| 9dd76db3fc | |||
| 97dacf2429 | |||
| 1c189e162a | |||
| f7a0fff869 | |||
| 2f985d0926 | |||
| 2a3c544fba | |||
| 09d133b994 | |||
| 26a4223ecf | |||
| e2943fdcaf | |||
| f1053251b4 | |||
| be1dcaf43c | |||
| a899d988fc | |||
| 06b5b2c514 | |||
| 864a19b79f | |||
| 8974323406 | |||
| 46c9e0103a | |||
| 02203466ed | |||
| 87b22ea1cc | |||
| d26327a930 | |||
| 469d2bdcb7 | |||
| 5516603a0c | |||
| b737adc3da | |||
| f3a8afeee3 | |||
| a4501f86e9 | |||
| 095fff96ff | |||
| a23211d061 | |||
| b57ea41686 | |||
| 62fb5ffb73 | |||
| 99c713967b | |||
| 9f3de07bd8 | |||
| cd74117de3 | |||
| e7d7033548 | |||
| 34db7c9dac | |||
| 272841aae9 | |||
| 389b09a5cd | |||
| 84bd876c71 | |||
| 7e45051ffd | |||
| 5570f3a313 | |||
| f4e71f7012 | |||
| 601d4e6e3a | |||
| a12092c1a1 | |||
| 94ad118c5d | |||
| 48e2978438 | |||
| 96b4c1a9e8 | |||
| cc4a69c10c | |||
| 7293677ddb | |||
| 0f2ff20375 | |||
| 7706240acb | |||
| 4cf48fd997 | |||
| 2708c1ee11 | |||
| ebb0f6ebad | |||
| 2c263a2549 | |||
| 955524c632 | |||
| 564e0d3263 | |||
| c533b2e8ea | |||
| 6073d8122a | |||
| 71ca26bba9 | |||
| 75a51be28c | |||
| a66e4ba157 | |||
| f2b9e428d3 | |||
| 5b2497b9d4 | |||
| e78424def4 | |||
| e2c94de6e6 | |||
| daa3caf684 | |||
| 5991aa42d9 | |||
| ff6a5aae69 | |||
| 80d9b4540b | |||
| 4998991ebe | |||
| 14343569fe | |||
| be6cc5ff4e | |||
| 0ad3391bea | |||
| 582d998e2e | |||
| c00ee26075 | |||
| 4c9f70a52f | |||
| 29481bc123 | |||
| e274c9e004 | |||
| 73988f0308 | |||
| c4a9e7a2a7 | |||
| af6dca6e5e | |||
| a49720a221 | |||
| 12a66e70c9 | |||
| 00d8aa356b | |||
| d40d94a565 | |||
| c1f798793e | |||
| 8fe4ad454c | |||
| 17022f2df2 | |||
| 7fa4bc35f6 | |||
| 85df9c5ef4 | |||
| 45d4f390a9 | |||
| e7b60b7562 | |||
| 2615177907 | |||
| a83d7e2a26 | |||
| 8a2182e238 | |||
| d45455092c | |||
| da18469fd4 | |||
| 6b5bae9bee | |||
| e29a7786e4 | |||
| 55a837ca5e | |||
| 94cba6cf67 | |||
| 294685df00 | |||
| 2642b7a9a4 | |||
| 45594e4e47 | |||
| 54cd1a2e48 | |||
| a20fb982ec | |||
| 184d61e637 | |||
| 06ea56767f | |||
| 1f6e137759 | |||
| c7cf7885ae | |||
| 8e98444887 | |||
| 9bfe2e8233 | |||
| 21f4adc769 | |||
| 3b10ca5809 | |||
| 6cc8284747 | |||
| 6254f7156f | |||
| 0fa03701a4 | |||
| 49125cae47 | |||
| 3298857d0c | |||
| d939cbeb96 | |||
| 54d254be73 | |||
| d433fdea62 | |||
| ffa1c54ff3 | |||
| 019ac6dec3 | |||
| 08d3da47f4 | |||
| 348ac2b734 | |||
| fe014b4985 | |||
| b7f6f3b900 | |||
| 3c76ed71d8 | |||
| 5de1998913 | |||
| 05c0f030cb | |||
| bd85329589 | |||
| da0f4ae326 | |||
| b37e568372 | |||
| b1c6a5c1d4 | |||
| 8222126e8f | |||
| b5b888c69f | |||
| 85f6b8320b | |||
| 6c4740e8aa | |||
| ffda83c25d | |||
| 18859d22a8 | |||
| befa3b375e | |||
| a96c08cad4 | |||
| d7f789c402 | |||
| ed28bcd0ba | |||
| 982ae0c0a0 | |||
| d17897f276 | |||
| c0ccef7e82 | |||
| 55e5c64c68 | |||
| da72f783ed | |||
| 8a9bfa3d33 | |||
| 82937d840a | |||
| c1bd358502 | |||
| a8a22fd9fe | |||
| adb70857fe | |||
| a680596b3e | |||
| b2986ec782 | |||
| f40f7afab3 | |||
| 59555d1ca8 | |||
| 6e2eb7d1ac | |||
| 69483067b4 | |||
| c815591565 | |||
| f82b7ec608 | |||
| 23a107682c | |||
| 26da69c875 | |||
| 8f2df2396d | |||
| dbc9cbb418 | |||
| 09c788e495 | |||
| 9af71ac0ea | |||
| 2db20f8f24 | |||
| 2c9827c227 | |||
| 825b1ab4db | |||
| c61faac06c | |||
| 6b442f03c1 | |||
| 10ab638643 | |||
| d349d0c453 | |||
| e5c948ce9c | |||
| c51255f379 | |||
| 5bbeceee56 | |||
| ee78920a88 | |||
| 138972cf18 | |||
| 1bda767cd7 | |||
| cf1402edb9 | |||
| 778144a553 | |||
| fcca51a8d9 | |||
| 3cc663ee95 | |||
| 52f15a86fd | |||
| 734bb4afb8 | |||
| 9cd48fb5d7 | |||
| cd5c3d9f13 | |||
| 73ba1c8b56 | |||
| 8199b4408a | |||
| c00e3d7393 | |||
| 5d60251da0 | |||
| 855d707b3e | |||
| 592a1c3601 | |||
| da280db0f8 | |||
| 0ee6673e68 | |||
| 8bedb3dbaa | |||
| ea0e24b430 | |||
| cd35461df6 | |||
| 40580e81c2 | |||
| 1a279b90be | |||
| e34203fe3d | |||
| 870c0f68d3 | |||
| 004059dd9f | |||
| 4290cdf8c6 | |||
| 4d038fc5e6 | |||
| 0146d62c2a | |||
| 4e6255dc2b | |||
| 4d73c3ecb8 | |||
| d0dc3ec9bc | |||
| ee922236a0 | |||
| bf088ab29c | |||
| 40102521a1 | |||
| eb491144f3 | |||
| a14748ac75 | |||
| 0c5f38a00b | |||
| feb6dd41b7 | |||
| a093524939 | |||
| 5cc7bebbe5 | |||
| 32a256619f | |||
| f93e648f69 | |||
| 41f022df99 | |||
| c84aac7e45 | |||
| 4e4fbbe48c | |||
| 7265e2b631 | |||
| 0a11f310cf | |||
| a6143780fa | |||
| 761f802ced | |||
| 2e7cc9f4b6 | |||
| 4e6e727799 | |||
| 2da444d03c | |||
| f4f234a159 | |||
| e87823aa29 | |||
| 56167a137e | |||
| 91ac4309df | |||
| 8a5bca3136 | |||
| ec8d1b5538 | |||
| 0ebe8db268 | |||
| 3bba08723f | |||
| 6a6a87a3f7 | |||
| 0ccdcfbb6d | |||
| aa084c885d | |||
| e3a8a7782e | |||
| c578ae0fa4 | |||
| f17bc1eccd | |||
| 08c31e5999 | |||
| dd341c9a22 | |||
| 733fb8e871 | |||
| 4764f0969a | |||
| 41f8c8cfa4 | |||
| 42bcc28d3e | |||
| b878faac96 | |||
| 32c1cc29f8 | |||
| 838d82589b | |||
| b62af66f4b | |||
| 794f524ae3 | |||
| a38551b3d0 | |||
| 03c4926b09 | |||
| 982bf951e1 | |||
| a887e35285 | |||
| 172f2b088d | |||
| 6d7b557c36 | |||
| 9959ebc196 | |||
| d868b8fc72 | |||
| 297c8a1a1e | |||
| e809429a8c | |||
| 200ad05f4c | |||
| 9b4276c29b | |||
| 038073d34d | |||
| 8d32ab9bdb | |||
| 9390b8bc4d | |||
| 0d165860fb | |||
| 4135fa6211 | |||
| 1d89467022 | |||
| e21ac626b3 | |||
| aa73a30ac4 | |||
| 9a8f14c7d3 | |||
| d7618d73c3 | |||
| 5aca47b357 | |||
| 666ab1319b | |||
| 50663edc76 | |||
| 1fbc37f8e7 | |||
| 5f437f8a4e | |||
| 6130c65c48 | |||
| 1e19dfd6e8 | |||
| f866243d5c | |||
| af04c6d995 | |||
| 46bf8d0188 | |||
| fc62c78136 | |||
| 93c2bf530f | |||
| 51b543a23e | |||
| dab5373231 | |||
| 17f274a7b4 | |||
| 841e5ba492 | |||
| 50339c94e7 | |||
| d2ad35ad05 | |||
| 0d65578e6a | |||
| 526df4f184 | |||
| aa6cbd1b7d | |||
| 92f0703723 | |||
| 064241333b | |||
| 39b77c783b | |||
| 0c8717a0da | |||
| 7f89a1a2cc | |||
| 5f16f81d58 | |||
| 10bbcc79d3 | |||
| ffc9771b17 | |||
| 091b115aad | |||
| c3bdc22e28 | |||
| 28d4cc86a9 | |||
| bb6e29f254 | |||
| ea0d4ba7d8 | |||
| 0504882afd | |||
| 0156f03e91 | |||
| d2acaba5c7 | |||
| 60e4128316 | |||
| dc8aa7624b | |||
| 941aeedee0 | |||
| 4d8ce3b5ea | |||
| 6044d24a48 | |||
| d597634ac6 | |||
| cf8b0329bb | |||
| 24520824e9 | |||
| 0d7b0f9fe4 | |||
| 61460f9ac0 | |||
| 14d64d535a | |||
| b00c252f17 | |||
| 0fe017df74 | |||
| 4ae7083dcf | |||
| 7d5357dec8 | |||
| 2bb2f5a233 | |||
| fee1c98a34 | |||
| e787d7d317 | |||
| ed52efa72b | |||
| 963d22b2f5 | |||
| 8714be1dc7 | |||
| 04ddd83519 | |||
| 5b81ca2716 | |||
| 6f8c7e8249 | |||
| fabad7158e | |||
| bae7e44dd8 | |||
| 926c7924df | |||
| 704a2ff217 | |||
| 871c6031b8 | |||
| afb0a57920 |
+1
-1
@@ -688,7 +688,7 @@ ij_markdown_wrap_text_inside_blockquotes = true
|
||||
ij_toml_keep_indents_on_empty_lines = false
|
||||
|
||||
[{*.yaml,*.yml}]
|
||||
indent_size = 4
|
||||
indent_size = 2
|
||||
ij_yaml_align_values_properties = do_not_align
|
||||
ij_yaml_autoinsert_sequence_marker = true
|
||||
ij_yaml_block_mapping_on_new_line = false
|
||||
|
||||
@@ -34,3 +34,9 @@ build.properties
|
||||
|
||||
# Sqlite databases
|
||||
*.sqlite
|
||||
*.sqlite-journal
|
||||
*.sqlite-shm
|
||||
*.sqlite-wal
|
||||
|
||||
# Don't add access transformers to git as they're dynamically generated
|
||||
accesstransformer.cfg
|
||||
|
||||
+18
-6
@@ -1,6 +1,6 @@
|
||||
# use Eclipse's JDK
|
||||
# The ci should always use a unix(-like) OS to work
|
||||
image: eclipse-temurin:17
|
||||
image: eclipse-temurin:21
|
||||
|
||||
# all stages need to be defined here
|
||||
# TODO: Make stages depend on what is in versionProperties
|
||||
@@ -18,19 +18,24 @@ variables:
|
||||
.build_java:
|
||||
#image: eclipse-temurin:17
|
||||
cache:
|
||||
key: "gradleCache"
|
||||
key: "gradleCache_$CI_JOB_NAME_SLUG"
|
||||
policy: pull-push
|
||||
paths:
|
||||
- .gradle
|
||||
- cache/
|
||||
allow_failure: true
|
||||
retry:
|
||||
max: 2
|
||||
when:
|
||||
- runner_system_failure
|
||||
- stuck_or_timeout_failure
|
||||
|
||||
|
||||
build:
|
||||
stage: build
|
||||
parallel:
|
||||
matrix:
|
||||
- MC_VER: ["1.16.5", "1.17.1", "1.18.2", "1.19.2", "1.19.4", "1.20.1", "1.20.2"]
|
||||
- MC_VER: ["1.16.5", "1.17.1", "1.18.2", "1.19.2", "1.19.4", "1.20.1", "1.20.2", "1.20.4", "1.20.6", "1.21.1", "1.21.3", "1.21.4"]
|
||||
script:
|
||||
# this both runs the unit tests and assembles the code
|
||||
- ./gradlew clean -PmcVer="${MC_VER}" -PinfoGitCommit="${CI_COMMIT_SHA}" -PinfoGitBranch="${CI_COMMIT_BRANCH}" -PinfoBuildSource="GitLab CI (${CI_PIPELINE_ID})" --gradle-user-home cache/;
|
||||
@@ -40,17 +45,24 @@ build:
|
||||
name: "NightlyBuild_${MC_VER}-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
|
||||
paths:
|
||||
- Merged/*.jar
|
||||
- quilt/build/libs/*.jar
|
||||
- fabric/build/libs/*.jar
|
||||
- forge/build/libs/*.jar
|
||||
- quilt/build/libs/*.jar
|
||||
- neoforge/build/libs/*.jar
|
||||
exclude:
|
||||
# TODO: There is a lot of duplicate stuff here, try to maybe make it smaller
|
||||
- fabric/build/libs/*-all.jar
|
||||
- fabric/build/libs/*-dev.jar
|
||||
- fabric/build/libs/*-sources.jar
|
||||
- forge/build/libs/*-all.jar
|
||||
- forge/build/libs/*-sources.jar
|
||||
- quilt/build/libs/*-all.jar
|
||||
- quilt/build/libs/*-dev.jar
|
||||
- quilt/build/libs/*-sources.jar
|
||||
- forge/build/libs/*-all.jar
|
||||
- forge/build/libs/*-dev.jar
|
||||
- forge/build/libs/*-sources.jar
|
||||
- neoforge/build/libs/*-all.jar
|
||||
- neoforge/build/libs/*-dev.jar
|
||||
- neoforge/build/libs/*-sources.jar
|
||||
expire_in: 14 days
|
||||
when: always
|
||||
extends: .build_java
|
||||
|
||||
@@ -6,13 +6,13 @@ Or click the checkbox once the issue has been created.
|
||||
-->
|
||||
|
||||
1. [ ] Check the FAQ to see if your issue has already been reported and has a solution:
|
||||
[Problems-and-solutions](https://gitlab.com/jeseibel/minecraft-lod-mod/-/wikis/2-frequently-asked-questions/2-problems-and-solutions/Problems-and-Solutions)
|
||||
[Problems-and-solutions](https://gitlab.com/jeseibel/distant-horizons/-/wikis/2-frequently-asked-questions/2-problems-and-solutions/Problems-and-Solutions)
|
||||
|
||||
2. [ ] Make sure you are not using any mods on the incompatible list:
|
||||
[Mod support FAQ](https://gitlab.com/jeseibel/minecraft-lod-mod/-/wikis/2-frequently-asked-questions/4-mod-support/Mod-Support)
|
||||
[Mod support FAQ](https://gitlab.com/jeseibel/distant-horizons/-/wikis/2-frequently-asked-questions/4-mod-support/Mod-Support)
|
||||
|
||||
3. [ ] Check the existing issues to verify that your bug hasn't already been submitted:
|
||||
[Issues](https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/)
|
||||
[Issues](https://gitlab.com/jeseibel/distant-horizons/-/issues/)
|
||||
|
||||
4. [ ] Upload Minecraft's crash report and/or log. \
|
||||
Minecraft crash reports are located in: `.minecraft/crash-reports` \
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
- [ ] Check the existing [feature requests](https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/?sort=updated_desc&state=opened&label_name%5B%5D=Feature) to verify that your feature hasn't already been suggested.
|
||||
- [ ] Check the existing [feature requests](https://gitlab.com/jeseibel/distant-horizons/-/issues/?sort=updated_desc&state=opened&label_name%5B%5D=Feature) to verify that your feature hasn't already been suggested.
|
||||
|
||||
1. **Describe the feature**:
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
1. Check the existing [improvement requests](https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/?sort=updated_desc&state=all&label_name%5B%5D=Improvement) to verify that your improvement hasn't already been suggested.
|
||||
1. Check the existing [improvement requests](https://gitlab.com/jeseibel/distant-horizons/-/issues/?sort=updated_desc&state=all&label_name%5B%5D=Improvement) to verify that your improvement hasn't already been suggested.
|
||||
|
||||
2. **Describe the improvement**:
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Fabric Client & Server" type="CompoundRunConfigurationType">
|
||||
<toRun name="Fabric Client (:fabric)" type="Application" />
|
||||
<toRun name="Fabric Server (:fabric)" type="Application" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1,7 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Forge Client & Server" type="CompoundRunConfigurationType">
|
||||
<toRun name="Forge Client (gradle)" type="GradleRunConfiguration" />
|
||||
<toRun name="Forge Server (gradle)" type="GradleRunConfiguration" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1,24 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Forge Client (gradle)" type="GradleRunConfiguration" factoryName="Gradle">
|
||||
<ExternalSystemSettings>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value="forge:runClient" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<RunAsTest>false</RunAsTest>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1,24 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Forge Server (gradle)" type="GradleRunConfiguration" factoryName="Gradle">
|
||||
<ExternalSystemSettings>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value="forge:runServer" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<RunAsTest>false</RunAsTest>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1,7 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Neoforge Client & Server" type="CompoundRunConfigurationType">
|
||||
<toRun name="Neoforge Client (gradle)" type="GradleRunConfiguration" />
|
||||
<toRun name="Neoforge Server (gradle)" type="GradleRunConfiguration" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1,24 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Neoforge Client (gradle)" type="GradleRunConfiguration" factoryName="Gradle">
|
||||
<ExternalSystemSettings>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value="neoforge:runClient" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<RunAsTest>false</RunAsTest>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1,24 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Neoforge Server (gradle)" type="GradleRunConfiguration" factoryName="Gradle">
|
||||
<ExternalSystemSettings>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value="neoforge:runServer" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<RunAsTest>false</RunAsTest>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1,24 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="distant-horizons [build]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
||||
<ExternalSystemSettings>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value="build" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<RunAsTest>false</RunAsTest>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1,24 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="distant-horizons [clean]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
||||
<ExternalSystemSettings>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value="clean" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<RunAsTest>false</RunAsTest>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1,24 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="distant-horizons [core:build]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
||||
<ExternalSystemSettings>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value="core:build" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<RunAsTest>false</RunAsTest>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1,24 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="distant-horizons [fabric:runClient]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
||||
<ExternalSystemSettings>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value="fabric:runClient" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<RunAsTest>false</RunAsTest>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1,24 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="distant-horizons [forge:runClient]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
||||
<ExternalSystemSettings>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value="forge:runClient" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<RunAsTest>false</RunAsTest>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1,24 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="distant-horizons [mergeJars]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
||||
<ExternalSystemSettings>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value="mergeJars" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<RunAsTest>false</RunAsTest>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1,24 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="distant-horizons [neoforge:runClient]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
||||
<ExternalSystemSettings>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value="neoforge:runClient" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<RunAsTest>false</RunAsTest>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1,24 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="distant-horizons [test]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
||||
<ExternalSystemSettings>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value="test" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<RunAsTest>false</RunAsTest>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1 @@
|
||||
Distant Horizons logos © 2024 by Pankakes are licensed under CC BY-SA 4.0
|
||||
@@ -1,18 +1,16 @@
|
||||
# <img src="https://gitlab.com/jeseibel/distant-horizons-core/-/raw/main/_Misc%20Files/logo%20files/LOD%20logo%20flat%20-%20with%20boarder.png" width="32"> Distant Horizons
|
||||
|
||||
> A mod that adds a Level of Detail System to Minecraft
|
||||
# <img src="https://gitlab.com/distant-horizons-team/distant-horizons-core/-/raw/main/_Misc%20Files/logo%20files/new/SVG/Distant-Horizons.svg" height="128px">
|
||||
_See farther without turning your game into a slide show._
|
||||
|
||||
<br>
|
||||
|
||||
# What is Distant Horizons?
|
||||
|
||||
Distant Horizons is a Minecraft mod that adds a Level Of Detail (LOD) system to\
|
||||
render simplified chunks outside the normal render distance\
|
||||
allowing for an increased view distance without harming performance.
|
||||
Distant Horizons is a mod which implements a [Level of Detail](https://en.wikipedia.org/wiki/Level_of_detail_(computer_graphics)) system to Minecraft.\
|
||||
This allows for far greater render distances without harming performance by gradually lowering the quality of distant terrain.
|
||||
|
||||
In other words: this mod lets you see farther without turning your game into a slide show.\
|
||||
If you want to see a quick demo, check out a video covering the mod here:
|
||||
Below is a video demonstrating the system:
|
||||
|
||||
<a href="https://youtu.be/_04BZ8W2bDM" target="_blank"></a>
|
||||
<a href="https://youtu.be/SxQdbtjGEsc" target="_blank"></a>
|
||||
|
||||
<br>
|
||||
|
||||
@@ -20,6 +18,14 @@ If you want to see a quick demo, check out a video covering the mod here:
|
||||
|
||||
### This branch supports the following versions of Minecraft:
|
||||
|
||||
#### 1.20.4, 1.20.3 (Default)
|
||||
Fabric: 0.15.1\
|
||||
Fabric API: 0.91.2+1.20.4\
|
||||
Forge: 49.0.30\
|
||||
NeoForge: 118-beta\
|
||||
Parchment: 1.20.2:2023.12.10\
|
||||
Modmenu: 9.0.0-pre.1
|
||||
|
||||
#### 1.20.2
|
||||
Fabric: 0.14.24\
|
||||
Fabric API: 0.90.4+1.20.2\
|
||||
@@ -27,7 +33,7 @@ Forge: 48.0.13\
|
||||
Parchment: 1.20.1:2023.09.03\
|
||||
Modmenu: 8.0.0
|
||||
|
||||
#### 1.20.1, 1.20 (Default)
|
||||
#### 1.20.1, 1.20
|
||||
Fabric: 0.14.24\
|
||||
Fabric API: 0.90.4+1.20.1\
|
||||
Forge: 47.2.1\
|
||||
@@ -73,14 +79,15 @@ Modmenu: 1.16.22
|
||||
- 1.18.1, 1.18
|
||||
- 1.19.1, 1.19
|
||||
- 1.19.3
|
||||
<br><br>
|
||||
|
||||
<br>
|
||||
|
||||
### Plugin and Library versions
|
||||
|
||||
Fabric loom: 1.1.+\
|
||||
Forge gradle (Using Architectury): 3.4-SNAPSHOT\
|
||||
Gradle: 8.5\
|
||||
Fabric loom: 1.4-SNAPSHOT\
|
||||
Architectury loom (Forge gradle replacement): 1.4-SNAPSHOT\
|
||||
Sponge vanilla gradle: 0.2.1-SNAPSHOT\
|
||||
Sponge mixin: 0.8.5\
|
||||
Java Preprocessor plugin: Manifold Preprocessor
|
||||
|
||||
<br>
|
||||
@@ -93,7 +100,7 @@ Java Preprocessor plugin: Manifold Preprocessor
|
||||
Visit https://www.oracle.com/java/technologies/downloads/ for installers.
|
||||
* Git or someway to clone git projects. <br>
|
||||
Visit https://git-scm.com/ for installers.
|
||||
* (Not required) Any Java IDE with plugins that support Manifold, for example Intellij IDEA.
|
||||
* (Not required) Any Java IDE with plugins that support Manifold, for example IntelliJ IDEA.
|
||||
|
||||
**If using IntelliJ:**
|
||||
1. Install the Manifold plugin
|
||||
@@ -106,17 +113,14 @@ Java Preprocessor plugin: Manifold Preprocessor
|
||||
3. Make sure eclipse has the JDK 17 installed. (This is needed so that eclipse can run minecraft)
|
||||
4. Import the project into eclipse
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
## Switching Versions
|
||||
|
||||
To switch between different Minecraft versions, change `mcVer=1.?` in the `gradle.properties` file.
|
||||
|
||||
If running in an IDE, to ensure the IDE noticed the version change, run any gradle command to refresh gradle. (In IntellJ you will also need to do a gradle sync if it didn't happen automatically.)
|
||||
>Note: There may be a `java.nio.file.FileSystemException` thrown when running the command after switching versions. To fix it, either restart your IDE (as your IDE is probably locking a file) or use a tool like LockHunter to unlock the linked file(s). (Generally it is a lib file under `common\build\lib`, `forge\build\lib`, or `fabric\build\lib`). \
|
||||
> If anyone knows how to solve this issue please let us know here: \
|
||||
> https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/233
|
||||
|
||||
If running in an IDE, to ensure the IDE noticed the version change, run any gradle command to refresh gradle.\
|
||||
In IntelliJ, you will also need to do a gradle sync if it didn't happen automatically.
|
||||
|
||||
<br>
|
||||
|
||||
@@ -127,24 +131,23 @@ Prerequisites:
|
||||
|
||||
From the File Explorer:
|
||||
1. Download and extract the project zip
|
||||
2. Download the core from https://gitlab.com/jeseibel/distant-horizons-core and extract into a folder called `coreSubProjects`
|
||||
2. Download the core from https://gitlab.com/distant-horizons-team/distant-horizons-core and extract into a folder called `coreSubProjects`
|
||||
3. Open a terminal emulator in the project folder (On Windows you can type `cmd` in the title bar)
|
||||
4. Run the commands: `./gradlew assemble` (You may need to use a `.\` on Windows)
|
||||
5. Merge the jars wih `./gradlew mergeJars`
|
||||
5. Merge the jars with `./gradlew mergeJars`
|
||||
6. The compiled jar file will be in the folder `Merged`
|
||||
|
||||
From the command line:
|
||||
1. `git clone --recurse-submodules https://gitlab.com/jeseibel/minecraft-lod-mod.git`
|
||||
2. `cd minecraft-lod-mod`
|
||||
1. `git clone --recurse-submodules https://gitlab.com/distant-horizons-team/distant-horizons.git`
|
||||
2. `cd distant-horizons`
|
||||
3. `./gradlew assemble`
|
||||
4. `./gradlew mergeJars`
|
||||
5. The compiled jar file will be in the folder `Merged`
|
||||
|
||||
Run tests with: `./gradlew test`
|
||||
|
||||
>Note: You can add the arg: `-PmcVer=?` to tell gradle to build a selected MC version instead of having to modify the `gradle.properties` file. \
|
||||
> Example: `./gradlew assemble -PmcVer=1.18.2`
|
||||
|
||||
>Note: You can add the argument `-PmcVer=?` to tell gradle to build a selected MC version instead of having to modify the `gradle.properties` file.\
|
||||
> For example: `./gradlew assemble -PmcVer=1.18.2`
|
||||
|
||||
<br>
|
||||
|
||||
@@ -154,7 +157,6 @@ Run tests with: `./gradlew test`
|
||||
|
||||
You can also locally compile the DH jars without a Java environment by using Docker. Where `<version>` is the version of Minecraft to compile for (ie `1.20.1`), or the keyword `all`. See [Versions](#minecraft-and-library-versions) for a list of all supported values.
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
## Other commands
|
||||
@@ -163,6 +165,7 @@ You can also locally compile the DH jars without a Java environment by using Doc
|
||||
|
||||
`./gradlew clean` to delete any compiled code.
|
||||
|
||||
<br>
|
||||
|
||||
## Note to self
|
||||
|
||||
@@ -170,7 +173,7 @@ The Minecraft source code is NOT added to your workspace in an editable way. Min
|
||||
|
||||
Source code uses Mojang mappings & [Parchment](https://parchmentmc.org/) mappings.
|
||||
|
||||
To generate the source code run `./gradlew genSources`\
|
||||
To generate the source code run `./gradlew genSources` <br>
|
||||
If your IDE fails to auto-detect the source jars when browsing Minecraft classes; manually select the JAR file ending with -sources.jar when prompted by your IDE. <br>
|
||||
(In IntelliJ it's at the top where it says "choose sources" when browsing a Minecraft class)
|
||||
|
||||
@@ -178,12 +181,12 @@ If your IDE fails to auto-detect the source jars when browsing Minecraft classes
|
||||
|
||||
## Other Useful commands
|
||||
|
||||
Run the standalone jar: `./gradlew run`\
|
||||
Build the standalone jar: `./gradlew core:build`\
|
||||
Only build Fabric: `./gradlew fabric:assemble` or `./gradlew fabric:build`\
|
||||
Only build Forge: `./gradlew fabric:assemble` or `./gradlew forge:build`\
|
||||
Run the Fabric client (for debugging): `./gradlew fabric:runClient`\
|
||||
Run the Forge client (for debugging): `./gradlew forge:runClient`
|
||||
Run the standalone jar: `./gradlew run` <br>
|
||||
Build the standalone jar: `./gradlew core:build` <br>
|
||||
Only build Fabric: `./gradlew fabric:assemble` or `./gradlew fabric:build` <br>
|
||||
Only build Forge: `./gradlew forge:assemble` or `./gradlew forge:build` <br>
|
||||
Run the Fabric client (for debugging): `./gradlew fabric:runClient` <br>
|
||||
Run the Forge client (for debugging): `./gradlew forge:runClient` <br>
|
||||
|
||||
To build all versions: `./buildAll` (all builds will end up in the `Merged` folder)
|
||||
|
||||
@@ -197,7 +200,7 @@ https://github.com/PacifistMC/Forgix
|
||||
LZ4 for Java (data compression)\
|
||||
https://github.com/lz4/lz4-java
|
||||
|
||||
NightConfig for Json & Toml (config handling)\
|
||||
NightConfig for JSON & TOML (config handling)\
|
||||
https://github.com/TheElectronWill/night-config
|
||||
|
||||
SVG Salamander for SVG support (not being used atm)\
|
||||
|
||||
+149
-108
@@ -1,77 +1,52 @@
|
||||
plugins {
|
||||
id "java"
|
||||
|
||||
// Plugin to handle dependencies
|
||||
id "com.github.johnrengelman.shadow" version '7.1.2' apply false
|
||||
// Plugin to put dependencies inside our final jar
|
||||
id "com.github.johnrengelman.shadow" version '8.1.1' apply false
|
||||
|
||||
// Plugin to create merged jars
|
||||
id "io.github.pacifistmc.forgix" version "1.2.6"
|
||||
id "io.github.pacifistmc.forgix" version "1.2.9"
|
||||
|
||||
// Manifold preprocessor
|
||||
id "systems.manifold.manifold-gradle-plugin" version "0.0.2-alpha"
|
||||
|
||||
// Provides mc libraries to core
|
||||
// id "org.spongepowered.gradle.vanilla" version '0.2.1-SNAPSHOT' apply false
|
||||
|
||||
// Architectury is used here only as a replacement for forge's own loom
|
||||
id "dev.architectury.loom" version "1.1.+" apply false
|
||||
id "dev.architectury.loom" version "1.7-SNAPSHOT" apply false
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates the list of preprocessors to use.
|
||||
*
|
||||
* @param mcVers array of all MC versions
|
||||
* @param mcIndex array index of the currently active MC version
|
||||
*/
|
||||
def writeBuildGradlePredefine(List<String> mcVers, int mcIndex) {
|
||||
ArrayList<String> redefineList = new ArrayList<String>()
|
||||
def writeBuildGradlePredefine(List<String> mcVers, int mcIndex)
|
||||
{
|
||||
// Build the list of preprocessors to use
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < mcVers.size(); i++) {
|
||||
String mcStr = mcVers[i].replace(".", "_")
|
||||
sb.append("# DON'T TOUCH THIS FILE, This is handled by the build script\n");
|
||||
|
||||
if (mcIndex < i) {
|
||||
// exclusive before
|
||||
// FIXME doesn't function correctly for 1.16.5 (IE the first item in the list)
|
||||
redefineList.add("PRE_MC_" + mcStr)
|
||||
}
|
||||
if (mcIndex <= i) {
|
||||
// inclusive before
|
||||
redefineList.add("PRE_AND_MC_" + mcStr)
|
||||
}
|
||||
|
||||
if (mcIndex == i) {
|
||||
// exact
|
||||
redefineList.add("MC_" + mcStr)
|
||||
}
|
||||
for (int i = 0; i < mcVers.size(); i++)
|
||||
{
|
||||
String verStr = mcVers[i].replace(".", "_");
|
||||
sb.append("MC_" + verStr + "=" + i.toString() + "\n");
|
||||
|
||||
if (mcIndex > i) {
|
||||
// inclusive after
|
||||
redefineList.add("POST_AND_MC_" + mcStr)
|
||||
}
|
||||
if (mcIndex >= i) {
|
||||
// exclusive after
|
||||
redefineList.add("POST_MC_" + mcStr)
|
||||
}
|
||||
if (mcIndex == i)
|
||||
sb.append("MC_VER=" + i.toString() + "\n");
|
||||
}
|
||||
|
||||
// Build the list of preprocessors to use
|
||||
StringBuilder sb = new StringBuilder()
|
||||
|
||||
sb.append("# DON'T TOUCH THIS FILE, This is handled by the build script\n")
|
||||
|
||||
// Check if this is a development build
|
||||
if (mod_version.toLowerCase().contains("dev")) {
|
||||
if (mod_version.toLowerCase().contains("dev"))
|
||||
{
|
||||
// WARNING: only use this for logging, we don't want to have confusion
|
||||
// when a method doesn't work correctly in the release build.
|
||||
sb.append("DEV_BUILD")
|
||||
sb.append("=\n")
|
||||
sb.append("DEV_BUILD=\n");
|
||||
}
|
||||
|
||||
// Build the MC version preprocessors
|
||||
for (String redefinedVersion : redefineList) {
|
||||
sb.append(redefinedVersion)
|
||||
sb.append("=\n")
|
||||
}
|
||||
new File(projectDir, "build.properties").text = sb.toString()
|
||||
}
|
||||
|
||||
@@ -79,7 +54,7 @@ def writeBuildGradlePredefine(List<String> mcVers, int mcIndex) {
|
||||
// Transfers the values set in settings.gradle to the rest of the project
|
||||
project.gradle.ext.getProperties().each { prop ->
|
||||
rootProject.ext.set(prop.key, prop.value)
|
||||
// println "Added prop [key:" + prop.key + ", value:" + prop.value + "]"
|
||||
//println "Added prop [key:" + prop.key + ", value:" + prop.value + "]"
|
||||
}
|
||||
// Sets up manifold stuff
|
||||
writeBuildGradlePredefine(rootProject.mcVers, rootProject.mcIndex)
|
||||
@@ -91,14 +66,23 @@ writeBuildGradlePredefine(rootProject.mcVers, rootProject.mcIndex)
|
||||
rootProject.versionStr = rootProject.mod_version + "-" + rootProject.minecraft_version // + "-" + new Date().format("yyyy_MM_dd_HH_mm")
|
||||
// Forgix settings (used for merging jars)
|
||||
forgix {
|
||||
|
||||
String loaderHyphenSeparatedList = ((String)gradle.builds_for).replaceAll(",", "-");
|
||||
|
||||
group = "com.seibel.distanthorizons"
|
||||
mergedJarName = "DistantHorizons-${rootProject.versionStr}.jar"
|
||||
mergedJarName = "DistantHorizons-${rootProject.versionStr}-${loaderHyphenSeparatedList}.jar"
|
||||
|
||||
if (findProject(":forge"))
|
||||
forge {
|
||||
jarLocation = "build/libs/DistantHorizons-forge-${rootProject.versionStr}.jar"
|
||||
}
|
||||
|
||||
if (findProject(":neoforge"))
|
||||
custom {
|
||||
projectName = "neoforge"
|
||||
jarLocation = "build/libs/DistantHorizons-neoforge-${rootProject.versionStr}.jar"
|
||||
}
|
||||
|
||||
if (findProject(":fabric"))
|
||||
fabric {
|
||||
jarLocation = "build/libs/DistantHorizons-fabric-${rootProject.versionStr}.jar"
|
||||
@@ -123,13 +107,14 @@ subprojects { p ->
|
||||
apply plugin: "com.github.johnrengelman.shadow"
|
||||
if (isMinecraftSubProject)
|
||||
apply plugin: "systems.manifold.manifold-gradle-plugin"
|
||||
if (p == project(":core"))
|
||||
apply plugin: "application"
|
||||
// apply plugin: "org.spongepowered.gradle.vanilla" // Provides minecraft libraries
|
||||
|
||||
// Apply forge's loom
|
||||
if (findProject(":forge") && p == project(":forge"))
|
||||
if ((findProject(":forge") && p == project(":forge")) ||
|
||||
(findProject(":neoforge") && p == project(":neoforge"))
|
||||
)
|
||||
{
|
||||
apply plugin: "dev.architectury.loom"
|
||||
}
|
||||
|
||||
|
||||
// Set the manifold version (may not be required tough)
|
||||
@@ -166,58 +151,74 @@ subprojects { p ->
|
||||
shadowCommon // Don't use shadow from the shadow plugin because we don't want IDEA to index this.
|
||||
compileClasspath.extendsFrom common
|
||||
runtimeClasspath.extendsFrom common
|
||||
developmentForge.extendsFrom common
|
||||
if (findProject(":forge"))
|
||||
developmentForge.extendsFrom common
|
||||
if (findProject(":neoforge"))
|
||||
developmentNeoForge.extendsFrom common
|
||||
compileClasspath.extendsFrom coreProjects
|
||||
runtimeClasspath.extendsFrom coreProjects
|
||||
developmentForge.extendsFrom coreProjects
|
||||
if (findProject(":forge"))
|
||||
developmentForge.extendsFrom coreProjects
|
||||
if (findProject(":neoforge"))
|
||||
developmentNeoForge.extendsFrom coreProjects
|
||||
|
||||
// TODO remove unused fabricLike
|
||||
if (findProject(":fabricLike") && p != project(":fabricLike")) {
|
||||
// Shadow fabricLike
|
||||
fabricLike
|
||||
shadowFabricLike
|
||||
compileClasspath.extendsFrom fabricLike
|
||||
runtimeClasspath.extendsFrom fabricLike
|
||||
developmentForge.extendsFrom fabricLike
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Let the application plugin know where the main class is
|
||||
// (This will point to a non-existent class in all sub-projects except "Core")
|
||||
if (p == project(":core")) {
|
||||
application {
|
||||
mainClass.set("com.seibel.distanthorizons.core.jar.JarMain")
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
//=====================//
|
||||
// shared dependencies //
|
||||
//=====================//
|
||||
|
||||
|
||||
// Manifold
|
||||
if (isMinecraftSubProject) {
|
||||
annotationProcessor("systems.manifold:manifold-preprocessor:${rootProject.manifold_version}")
|
||||
}
|
||||
|
||||
// Log4j
|
||||
// TODO: Change to shadowMe later to work in the standalone jar
|
||||
// We cannot do this now as it would break Quilt
|
||||
implementation("org.apache.logging.log4j:log4j-api:${rootProject.log4j_version}")
|
||||
implementation("org.apache.logging.log4j:log4j-core:${rootProject.log4j_version}")
|
||||
if (p == project(":core"))
|
||||
{
|
||||
// the standalone core jar needs logging shaded otherwise it won't run
|
||||
forgeShadowMe("org.apache.logging.log4j:log4j-api:${rootProject.log4j_version}")
|
||||
forgeShadowMe("org.apache.logging.log4j:log4j-core:${rootProject.log4j_version}")
|
||||
}
|
||||
else
|
||||
{
|
||||
// When running in MC, MC already includes logging
|
||||
implementation("org.apache.logging.log4j:log4j-api:${rootProject.log4j_version}")
|
||||
implementation("org.apache.logging.log4j:log4j-core:${rootProject.log4j_version}")
|
||||
}
|
||||
|
||||
|
||||
// JOML
|
||||
implementation("org.joml:joml:${rootProject.joml_version}")
|
||||
if (project.hasProperty("embed_joml") && embed_joml == "true")
|
||||
forgeShadowMe("org.joml:joml:${rootProject.joml_version}")
|
||||
else
|
||||
implementation("org.joml:joml:${rootProject.joml_version}")
|
||||
|
||||
// JUnit tests
|
||||
implementation("org.junit.jupiter:junit-jupiter:5.8.2")
|
||||
implementation("org.junit.jupiter:junit-jupiter-engine:5.8.2")
|
||||
implementation("junit:junit:4.13")
|
||||
|
||||
// FastUtil
|
||||
// Note: MC 1.16 uses 8.2.1, and versions after use 8.5.12
|
||||
// We cannot relocate this library since we call some MC classes that reference it
|
||||
implementation("it.unimi.dsi:fastutil:${rootProject.fastutil_version}")
|
||||
|
||||
|
||||
// Compression
|
||||
forgeShadowMe("org.lz4:lz4-java:${rootProject.lz4_version}")
|
||||
forgeShadowMe("org.lz4:lz4-java:${rootProject.lz4_version}") // LZ4
|
||||
forgeShadowMe("org.tukaani:xz:${rootProject.xz_version}") // LZMA
|
||||
|
||||
// Sqlite Database
|
||||
forgeShadowMe("org.xerial:sqlite-jdbc:${rootProject.sqlite_jdbc_version}")
|
||||
@@ -230,8 +231,7 @@ subprojects { p ->
|
||||
// forgeShadowMe("com.formdev:svgSalamander:${rootProject.svgSalamander_version}")
|
||||
|
||||
// Netty
|
||||
// Breaks 1.16.5
|
||||
//forgeShadowMe("io.netty:netty-all:${rootProject.netty_version}")
|
||||
implementation("io.netty:netty-buffer:${rootProject.netty_version}")
|
||||
|
||||
// Remember, for lwjgl dependencies that arent included in Minecraft, you need to also need to add it to the ShadowJar thing
|
||||
forgeShadowMe("org.lwjgl:lwjgl-jawt:${rootProject.lwjgl_version}") {
|
||||
@@ -295,7 +295,7 @@ subprojects { p ->
|
||||
relocate "com.seibel.distanthorizons.fabriclike", "loaderCommon.${p.name}.com.seibel.distanthorizons.fabriclike" // Move the loader files to a different location
|
||||
}
|
||||
}
|
||||
def librariesLocation = "distanthorizons.libraries"
|
||||
def librariesLocation = "DistantHorizons.libraries"
|
||||
|
||||
// LWJGL
|
||||
// Only ever shadow the dependencies we use otherwise some stuff would break when running on an external client
|
||||
@@ -304,9 +304,33 @@ subprojects { p ->
|
||||
// Compression (LZ4)
|
||||
relocate "net.jpountz", "${librariesLocation}.jpountz"
|
||||
|
||||
// Sqlite Database
|
||||
//At the moment, there is a bug in this library which doesnt allow it to be relocated
|
||||
// relocate "org.sqlite", "${librariesLocation}.sqlite"
|
||||
// Logging
|
||||
relocate "org.slf4j", "${librariesLocation}.slf4j"
|
||||
|
||||
// // Sqlite Database
|
||||
// // James can't determine how to relocate the library correctly so this is commented out
|
||||
// relocate ("org.sqlite", "${librariesLocation}.sqlite") {
|
||||
// exclude("org/sqlite/core/NativeDB/**")
|
||||
//
|
||||
// exclude("org/sqlite/native/FreeBSD/**")
|
||||
// exclude("org/sqlite/native/Linux-Android/**")
|
||||
// exclude("org/sqlite/native/Linux-Musl/**")
|
||||
// exclude("org/sqlite/native/Linux/arm/**")
|
||||
// exclude("org/sqlite/native/Linux/aarch64/**")
|
||||
// exclude("org/sqlite/native/Linux/armv6/**")
|
||||
// exclude("org/sqlite/native/Linux/x86/**")
|
||||
// exclude("org/sqlite/native/Linux/armv7/**")
|
||||
// exclude("org/sqlite/native/Linux/ppc64/**")
|
||||
// exclude("org/sqlite/native/Linux/riscv64/**")
|
||||
// exclude("org/sqlite/native/Windows/armv7/**")
|
||||
// exclude("org/sqlite/native/Windows/aarch64/**")
|
||||
// exclude("org/sqlite/native/Windows/armv7/**")
|
||||
// }
|
||||
|
||||
|
||||
// JOML
|
||||
if (project.hasProperty("embed_joml") && embed_joml == "true")
|
||||
relocate "org.joml", "${librariesLocation}.joml"
|
||||
|
||||
// NightConfig (includes Toml & Json)
|
||||
relocate "com.electronwill.nightconfig", "${librariesLocation}.electronwill.nightconfig"
|
||||
@@ -315,7 +339,8 @@ subprojects { p ->
|
||||
// relocate "com.kitfox.svg", "${librariesLocation}.kitfox.svg"
|
||||
|
||||
// Netty
|
||||
relocate "io.netty", "${librariesLocation}.netty"
|
||||
// Don't relocate, it causes problems with using MC's FriendlyByteBufs
|
||||
// relocate "io.netty", "${librariesLocation}.netty"
|
||||
|
||||
mergeServiceFiles()
|
||||
}
|
||||
@@ -326,22 +351,23 @@ subprojects { p ->
|
||||
// Put stuff from gradle.properties into the mod info
|
||||
processResources {
|
||||
def resourceTargets = [ // Location of where to inject the properties
|
||||
// Holds info like git commit
|
||||
// TODO: For some reason this script doesnt work with the core project
|
||||
"build_info.json",
|
||||
// Holds info like git commit
|
||||
// TODO: For some reason this script doesnt work with the core project
|
||||
"build_info.json",
|
||||
|
||||
// Properties for each of the loaders
|
||||
"fabric.mod.json",
|
||||
"quilt.mod.json",
|
||||
"META-INF/mods.toml",
|
||||
// Properties for each of the loaders
|
||||
"fabric.mod.json",
|
||||
"quilt.mod.json",
|
||||
"META-INF/mods.toml",
|
||||
"META-INF/neoforge.mods.toml",
|
||||
|
||||
// The mixins for each of the loaders
|
||||
"DistantHorizons."+ p.name +".fabricLike.mixins.json"
|
||||
// The mixins for each of the loaders
|
||||
"DistantHorizons."+ p.name +".fabricLike.mixins.json"
|
||||
]
|
||||
def intoTargets = ["$buildDir/resources/main/"] // Location of the built resources folder
|
||||
|
||||
// Fix forge version numbering system as it is weird
|
||||
// For whatever reason forge uses [1.18, 1.18.1, 1.18.2) instead of the standard ["1.18", "1.18.1", "1.18.2"] which make more sense
|
||||
// For whatever reason forge uses [1.18, 1.18.1, 1.18.2) instead of the standard ["1.18", "1.18.1", "1.18.2"]
|
||||
def compatible_forgemc_versions = "${compatible_minecraft_versions}".replaceAll("\"", "").replaceAll("]", ",)")
|
||||
// println compatible_forgemc_versions
|
||||
|
||||
@@ -354,9 +380,9 @@ subprojects { p ->
|
||||
quilt_contributors.push("\"${dev.strip()}\": \"Developer\"")
|
||||
}
|
||||
quilt_contributors.reverse()
|
||||
// println quilt_contributors.join(", ")
|
||||
//println quilt_contributors.join(", ")
|
||||
|
||||
// TODOI: Find something we can use so we can basically re-map only when the jar is shadowed and relocated
|
||||
// TODO: Find something we can use so we can basically re-map only when the jar is shadowed and relocated
|
||||
// println p.tasks.findByName('shadowJar')
|
||||
|
||||
|
||||
@@ -371,6 +397,7 @@ subprojects { p ->
|
||||
println "Git or Git project not found"
|
||||
}
|
||||
|
||||
// The left side is what gets replaced in the mod info and the right side is where to get it from in the gradle.properties
|
||||
def replaceProperties = [
|
||||
version : mod_version,
|
||||
mod_name : mod_readable_name,
|
||||
@@ -392,10 +419,10 @@ subprojects { p ->
|
||||
info_build_source : infoBuildSource,
|
||||
|
||||
fabric_incompatibility_list : fabric_incompatibility_list,
|
||||
fabric_recommend_list : fabric_recommend_list,
|
||||
fabric_recommend_list : fabric_recommend_list,
|
||||
]
|
||||
// The left side is what gets replaced in the mod info and the right side is where to get it from in the gradle.properties
|
||||
|
||||
// replace any properties in the sub-projects with the values defined here
|
||||
inputs.properties replaceProperties
|
||||
replaceProperties.put "project", project
|
||||
filesMatching(resourceTargets) {
|
||||
@@ -423,7 +450,7 @@ subprojects { p ->
|
||||
//// include "${accessWidenerVersion}.distanthorizons.accesswidener"
|
||||
|
||||
// Jank solution to remove all unused accesswideners
|
||||
// The line above would work..., except forge requires the original accesswidener file, meaning we require this jank solution to keep it
|
||||
// The line above would work..., except that (neo)forge (well, mainly architectury) requires the original accesswidener file, meaning we require this jank solution to keep it
|
||||
exclude { file ->
|
||||
if (file.name.contains(".distanthorizons.accesswidener") && file.name != "${accessWidenerVersion}.distanthorizons.accesswidener") {
|
||||
return true
|
||||
@@ -437,9 +464,12 @@ subprojects { p ->
|
||||
jar {
|
||||
from "LICENSE.txt"
|
||||
manifest {
|
||||
attributes 'Implementation-Title': rootProject.mod_name,
|
||||
'Implementation-Version': rootProject.mod_version,
|
||||
'Main-Class': 'com.seibel.distanthorizons.core.jar.JarMain' // When changing the main of the jar change this line
|
||||
attributes(
|
||||
'Implementation-Title': rootProject.mod_name,
|
||||
'Implementation-Version': rootProject.mod_version,
|
||||
'Multi-Release': true, // needed for logging in the standalone core jar
|
||||
'Main-Class': 'com.seibel.distanthorizons.core.jar.JarMain', // When changing the main of the jar change this line
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,17 +489,10 @@ subprojects { p ->
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Run mergeJars when running build
|
||||
// TODO: Fix later
|
||||
// if (isMinecraftSubProject) {
|
||||
// build.finalizedBy(mergeJars)
|
||||
// assemble.finalizedBy(mergeJars)
|
||||
// }
|
||||
}
|
||||
|
||||
allprojects { p ->
|
||||
// Does the same as "p == project(":common") || p == project(":fabric") || p == project(":quilt") || p == project(":forge")"
|
||||
// Does the same as "p == project(":common") || p == project(":fabric") || p == project(":quilt") || p == project(":forge") || p == project("WhateverWeAddLaterOn")"
|
||||
// Useful later on so we dont have duplicated code
|
||||
def isMinecraftSubProject = p != project(":core") && p != project(":api")
|
||||
|
||||
@@ -485,8 +508,24 @@ allprojects { p ->
|
||||
// and is used when bookmarking a page
|
||||
javadoc.title = rootProject.mod_name + "-" + project.name
|
||||
|
||||
// Some annotations arent "technically" part of the official java standard,
|
||||
// so we define it ourself here
|
||||
javadoc {
|
||||
configure( options ) {
|
||||
tags(
|
||||
'todo:X"',
|
||||
'apiNote:a:API Note:',
|
||||
'implSpec:a:Implementation Requirements:',
|
||||
'implNote:a:Implementation Note:'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
repositories {
|
||||
// Mojang overrides (added to fix downloading the wrong LWJGL libs on M1 Mac's and potentially other arm64 based machines)
|
||||
maven { url "https://libraries.minecraft.net/" }
|
||||
|
||||
// The central repo
|
||||
mavenCentral()
|
||||
|
||||
@@ -494,6 +533,7 @@ allprojects { p ->
|
||||
maven { url "https://repo.enonic.com/public/" }
|
||||
|
||||
// For parchment mappings
|
||||
// versions can be found here: https://ldtteam.jfrog.io/ui/native/parchmentmc-public/org/parchmentmc/data/
|
||||
maven { url "https://maven.parchmentmc.org" }
|
||||
|
||||
// For Architectury API
|
||||
@@ -522,14 +562,16 @@ allprojects { p ->
|
||||
}
|
||||
}
|
||||
|
||||
// Required for ModMenu
|
||||
maven { url "https://maven.terraformersmc.com/" }
|
||||
|
||||
// Required for Mixins & VanillaGradle
|
||||
// VanillaGradle and Mixins in common
|
||||
maven { url "https://repo.spongepowered.org/maven/" }
|
||||
|
||||
// Required for Canvas (mod)
|
||||
// Canvas mod
|
||||
maven { url "https://maven.vram.io/" }
|
||||
// ModMenu mod
|
||||
maven { url "https://maven.terraformersmc.com/" }
|
||||
|
||||
// neoforge
|
||||
maven { url "https://maven.neoforged.net/releases/" }
|
||||
|
||||
// These 3 are for importing mods that arnt on CursedForge, Modrinth, GitHub, GitLab or anywhere opensource
|
||||
flatDir {
|
||||
@@ -550,6 +592,7 @@ allprojects { p ->
|
||||
includeGroup "forge-mod"
|
||||
}
|
||||
}
|
||||
// TODO: If neoforged is ever needed, should we use that, or call it a forge mod?
|
||||
}
|
||||
|
||||
// Adds some dependencies that are in vanilla but not in core
|
||||
@@ -586,7 +629,6 @@ allprojects { p ->
|
||||
implementation("com.google.code.findbugs:jsr305:3.0.2")
|
||||
implementation("com.google.common:google-collect:0.5")
|
||||
implementation("com.google.guava:guava:31.1-jre")
|
||||
implementation("it.unimi.dsi:fastutil:8.5.11")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -613,7 +655,6 @@ allprojects { p ->
|
||||
tasks.withType(JavaCompile) {
|
||||
if (isMinecraftSubProject) {
|
||||
options.release = rootProject.java_version as Integer
|
||||
options.compilerArgs += ["-Xplugin:Manifold"]
|
||||
} else {
|
||||
options.release = 8; // Core & Api should use Java 8 no matter what
|
||||
//options.release = rootProject.java_version as Integer // But if you want to test some stuff, then this can be enabled
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "==================== Note: All build jars will be in the folder called 'buildAllJars' ===================="
|
||||
mkdir -p buildAllJars | true
|
||||
mkdir -p buildAllJars
|
||||
rm -rf buildAllJars/*
|
||||
|
||||
# Loop trough everything in the version properties folder
|
||||
for d in versionProperties/*; do
|
||||
@@ -11,12 +12,17 @@ for d in versionProperties/*; do
|
||||
# Clean out the folders, build it, and merge it
|
||||
# (We could use "./" to run gradlew, but as it is a shell script im going to be running it with the "sh" command)
|
||||
echo "==================== Cleaning workspace to build $version ===================="
|
||||
sh gradlew clean -PmcVer=$version --no-daemon || true
|
||||
sh gradlew clean -PmcVer=$version
|
||||
if [ $? != 0 ]; then continue; fi
|
||||
|
||||
echo "====================Building $version ===================="
|
||||
sh gradlew build -PmcVer=$version --no-daemon || true
|
||||
sh gradlew build -PmcVer=$version
|
||||
if [ $? != 0 ]; then continue; fi
|
||||
|
||||
echo "==================== Merging $version ===================="
|
||||
sh gradlew mergeJars -PmcVer=$version --no-daemon || true
|
||||
sh gradlew mergeJars -PmcVer=$version
|
||||
if [ $? != 0 ]; then continue; fi
|
||||
|
||||
echo "==================== Moving jar ===================="
|
||||
mv Merged/*.jar buildAllJars/ || true
|
||||
# The "| true" at the end of those are just to make sure the script continues even if a build fails
|
||||
mv Merged/*.jar buildAllJars/
|
||||
done
|
||||
|
||||
+4
-3
@@ -5,6 +5,7 @@
|
||||
|
||||
echo ==================== Note: All build jars will be in the folder called 'buildAllJars' ====================
|
||||
mkdir buildAllJars
|
||||
del buildAllJars/*
|
||||
|
||||
@rem Loop trough everything in the version properties folder
|
||||
for %%f in (versionProperties\*) do (
|
||||
@@ -13,11 +14,11 @@ for %%f in (versionProperties\*) do (
|
||||
|
||||
@rem Clean out the folders, build it, and merge it
|
||||
echo ==================== Cleaning workspace to build !version! ====================
|
||||
call .\gradlew.bat clean -PmcVer="!version!" --no-daemon
|
||||
call .\gradlew.bat clean -PmcVer="!version!"
|
||||
echo ==================== Building !version! ====================
|
||||
call .\gradlew.bat build -PmcVer="!version!" --no-daemon
|
||||
call .\gradlew.bat build -PmcVer="!version!"
|
||||
echo ==================== Merging !version! ====================
|
||||
call .\gradlew.bat mergeJars -PmcVer="!version!" --no-daemon
|
||||
call .\gradlew.bat mergeJars -PmcVer="!version!"
|
||||
echo ==================== Moving jar ====================
|
||||
move Merged\*.jar buildAllJars\
|
||||
)
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class AWToAT {
|
||||
private static final Map<String, String> ACCESS_POINT_MAP = new HashMap<>();
|
||||
|
||||
static {
|
||||
ACCESS_POINT_MAP.put("accessible", "public");
|
||||
ACCESS_POINT_MAP.put("extendable", "public-f");
|
||||
ACCESS_POINT_MAP.put("mutable", "public-f");
|
||||
}
|
||||
|
||||
public String minecraftVersion;
|
||||
|
||||
public File remap(File file, String minecraftVersion) {
|
||||
this.minecraftVersion = minecraftVersion.replace("_", ".");
|
||||
File atFile = createATFile(file);
|
||||
processFile(file, atFile);
|
||||
return atFile;
|
||||
}
|
||||
|
||||
private File createATFile(File file) {
|
||||
File metaInf = new File(file.getParentFile(), "META-INF");
|
||||
if (!metaInf.exists() && !metaInf.mkdir()) throw new RuntimeException("Error creating META-INF folder");
|
||||
File atFile = new File(metaInf, "accesstransformer.cfg");
|
||||
try {
|
||||
atFile.createNewFile();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Error creating new file", e);
|
||||
}
|
||||
return atFile;
|
||||
}
|
||||
|
||||
private void processFile(File file, File atFile) {
|
||||
/* Validates if we need to recreate the Access Transformer file if it's out of date */
|
||||
// Get the hash of the file
|
||||
String fileHash = getFileHash(file);
|
||||
try (Scanner atScanner = new Scanner(atFile)) {
|
||||
// Check if the AT file is up-to-date by comparing the hash of the file with the hash stored in the AT file
|
||||
boolean hashFound = false;
|
||||
while (atScanner.hasNextLine()) {
|
||||
String line = atScanner.nextLine();
|
||||
if (hashCheck(line, fileHash)) {
|
||||
hashFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If the AT file is up-to-date, print a message and return
|
||||
if (hashFound) {
|
||||
System.out.println("Access Transformer file is already up to date.");
|
||||
return;
|
||||
}
|
||||
} catch (FileNotFoundException ignored) {
|
||||
// If the AT file does not exist, continue
|
||||
}
|
||||
|
||||
/* Creates the Access Transformer file */
|
||||
// Opens a scanner for reading the Access Widener file and a writer for writing to the Access Transformer file
|
||||
try (Scanner scanner = new Scanner(file); FileWriter writer = new FileWriter(atFile)) {
|
||||
// Create an ExecutorService with a fixed thread pool size equal to the number of available processors
|
||||
ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
|
||||
// List to hold Future objects representing results of computation
|
||||
List<Future<String>> futures = new ArrayList<>();
|
||||
|
||||
// Write the hash of the file to the AT file
|
||||
writer.write("#DH_MAPPING_HASH:" + fileHash + "\n");
|
||||
|
||||
// Read each line from the file
|
||||
while (scanner.hasNextLine()) {
|
||||
String line = scanner.nextLine();
|
||||
// Skip lines starting with "accessWidener", "#" or blank lines
|
||||
if (line.startsWith("accessWidener") || line.startsWith("#") || line.isBlank()) continue;
|
||||
|
||||
// Submit the line to the executor service for processing
|
||||
// The processing is done by the processLine method
|
||||
futures.add(executor.submit(() -> processLine(line.split(" "))));
|
||||
}
|
||||
|
||||
// Write the results to the output file
|
||||
// The results are obtained by calling the get method on each Future
|
||||
for (Future<String> future : futures) {
|
||||
writer.write(future.get());
|
||||
}
|
||||
|
||||
// Shutdown the executor service to free up resources
|
||||
executor.shutdown();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error reading or writing to file", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String processLine(String[] fields) {
|
||||
// fields[0] = access point like "accessible", "extendable", "mutable"
|
||||
// fields[1] = type like "field", "method", "class"
|
||||
// fields[2] = class name
|
||||
// fields[3] = field/method name
|
||||
// fields[4] = field/method descriptor
|
||||
|
||||
try {
|
||||
// Store the original field/method name
|
||||
String originalName = "";
|
||||
|
||||
// If there is a class name, replace the slashes with dots in the package name
|
||||
if (fields.length > 2) fields[2] = fields[2].replace("/", ".");
|
||||
|
||||
// If there is a field/method name, store the original name and remap it to SRG
|
||||
if (fields.length > 3) {
|
||||
originalName = fields[3];
|
||||
fields[3] = remapToSRG(fields[2], fields[3]);
|
||||
}
|
||||
|
||||
StringBuilder line = new StringBuilder(ACCESS_POINT_MAP.getOrDefault(fields[0], "public")).append(" ");
|
||||
switch (fields[1]) {
|
||||
case "field":
|
||||
line.append(fields[2]).append(" ").append(fields[3]).append(" #").append(originalName);
|
||||
// It'll be like: access-point class-name field-name-SRG # field-name-Mojmap
|
||||
// Eg: public net.minecraft.client.Minecraft f_90981_ # instance
|
||||
break;
|
||||
case "method":
|
||||
line.append(fields[2]).append(" ").append(fields[3]).append(fields[4]).append(" #").append(originalName);
|
||||
// It'll be like: access-point class-name method-name-SRG method-descriptor # method-name-Mojmap
|
||||
// Eg: public net.minecraft.client.Minecraft m_172797_()Lnet/minecraft/client/Minecraft; # getInstance
|
||||
break;
|
||||
default:
|
||||
line.append(fields[2]);
|
||||
// It'll be like: access-point class-name
|
||||
// Eg: public net.minecraft.client.Minecraft
|
||||
break;
|
||||
}
|
||||
line.append("\n");
|
||||
return line.toString();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error processing line", e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hashCheck(String line, String fileHash) {
|
||||
if (line.startsWith("#DH_MAPPING_HASH:")) {
|
||||
String hash = line.substring(17);
|
||||
return hash.equals(fileHash);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getFileHash(File file) {
|
||||
try {
|
||||
MessageDigest shaDigest = MessageDigest.getInstance("SHA-256");
|
||||
try (InputStream fis = new FileInputStream(file)) {
|
||||
byte[] byteArray = new byte[1024];
|
||||
int bytesCount;
|
||||
|
||||
// Read file data and update in message digest
|
||||
while ((bytesCount = fis.read(byteArray)) != -1) {
|
||||
shaDigest.update(byteArray, 0, bytesCount);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] bytes = shaDigest.digest();
|
||||
|
||||
// Convert byte array into signum representation
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte aByte : bytes) {
|
||||
sb.append(Integer.toString((aByte & 0xff) + 0x100, 16).substring(1));
|
||||
}
|
||||
|
||||
// Return complete hash
|
||||
return sb.toString();
|
||||
} catch (NoSuchAlgorithmException | IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// WARNING: BELOW LIES HIGHLY CURSED CODE AND MIGHT EVEN BE ILLEGAL
|
||||
|
||||
|
||||
|
||||
|
||||
// Flag to track if there was an error in the GET request
|
||||
boolean error = false;
|
||||
|
||||
/**
|
||||
* This method returns a field or method name from Mojang mappings as SRG mappings.
|
||||
* It makes a GET request to the Linkie API to fetch the SRG name.
|
||||
*
|
||||
* @param clazz The class name
|
||||
* @param name The field or method name
|
||||
* @return The SRG name
|
||||
* @throws Exception If there is an error in the GET request or the SRG name is not found in the response
|
||||
*/
|
||||
private String remapToSRG(String clazz, String name) throws Exception {
|
||||
// Encode the class and field/method name to be used in the URL
|
||||
String query = URLEncoder.encode(clazz + "." + name, StandardCharsets.UTF_8);
|
||||
// Construct the URL for the GET request
|
||||
String urlString = "https://linkieapi.shedaniel.me/api/search?namespace=mojang&query=" + query + "&version=" + this.minecraftVersion + "&limit=1&allowClasses=false&allowFields=true&allowMethods=true&translate=mojang_srg";
|
||||
URL url = new URI(urlString).toURL();
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestMethod("GET");
|
||||
int responseCode = conn.getResponseCode();
|
||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||
String inputLine;
|
||||
StringBuilder content = new StringBuilder();
|
||||
// Read the response line by line
|
||||
while ((inputLine = in.readLine()) != null) {
|
||||
content.append(inputLine);
|
||||
}
|
||||
in.close();
|
||||
conn.disconnect();
|
||||
// Regex to find the SRG name in the response
|
||||
Pattern pattern = Pattern.compile("\"l\"\\s*:\\s*\\{[^}]*\"i\"\\s*:\\s*\"([^\"]*)\"");
|
||||
Matcher matcher = pattern.matcher(content.toString());
|
||||
if (matcher.find()) return matcher.group(1);
|
||||
else throw new Exception("Couldn't find the SRG mapping for name: " + name + "\nCould not find 'i' in 'l' object in the response"); // `i` is the SRG name which is stored in the `l` JSON object
|
||||
} else {
|
||||
if (error) {
|
||||
// If there was an error in the GET request, and we already tried again, throw an exception
|
||||
throw new Exception("The GET request failed");
|
||||
}
|
||||
// If there was an error in the GET request, wait 2.5 seconds and try again as we probably got rate limited
|
||||
error = true;
|
||||
Thread.sleep(2500);
|
||||
return remapToSRG(clazz, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
-4
@@ -1,3 +1,15 @@
|
||||
|
||||
// temporary fix for broken spongepowered version
|
||||
buildscript {
|
||||
configurations.configureEach {
|
||||
resolutionStrategy {
|
||||
force 'org.spongepowered:vanillagradle:0.2.1-20240507.024226-82'
|
||||
// newer versions can be found by going to the link:
|
||||
// https://repo.spongepowered.org/#browse/browse:maven-public:org%2Fspongepowered%2Fvanillagradle%2F0.2.1-SNAPSHOT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "org.spongepowered.gradle.vanilla" version "0.2.1-SNAPSHOT"
|
||||
}
|
||||
@@ -8,10 +20,6 @@ minecraft {
|
||||
}
|
||||
|
||||
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
|
||||
// modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
|
||||
|
||||
// So mixins can be written in common
|
||||
compileOnly group:'org.spongepowered', name:'mixin', version:'0.8.5'
|
||||
}
|
||||
@@ -30,3 +38,4 @@ publishing {
|
||||
// Add repositories to publish to here.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,298 @@
|
||||
package com.seibel.distanthorizons.common;
|
||||
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiAfterDhInitEvent;
|
||||
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeDhInitEvent;
|
||||
import com.seibel.distanthorizons.common.commands.CommandInitializer;
|
||||
import com.seibel.distanthorizons.common.wrappers.DependencySetup;
|
||||
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftServerWrapper;
|
||||
import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
||||
import com.seibel.distanthorizons.core.api.internal.SharedApi;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.config.ConfigBase;
|
||||
import com.seibel.distanthorizons.core.config.eventHandlers.presets.ThreadPresetConfigEventHandler;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.jar.ModJarInfo;
|
||||
import com.seibel.distanthorizons.core.jar.updater.SelfUpdater;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModAccessor;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
|
||||
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
|
||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.dedicated.DedicatedServer;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Base for all mod loader initializers
|
||||
* and handles most setup.
|
||||
*/
|
||||
public abstract class AbstractModInitializer
|
||||
{
|
||||
protected static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
|
||||
|
||||
private CommandInitializer commandInitializer;
|
||||
|
||||
|
||||
|
||||
//==================//
|
||||
// abstract methods //
|
||||
//==================//
|
||||
|
||||
protected abstract void createInitialBindings();
|
||||
protected abstract IEventProxy createClientProxy();
|
||||
protected abstract IEventProxy createServerProxy(boolean isDedicated);
|
||||
protected abstract void initializeModCompat();
|
||||
|
||||
protected abstract void subscribeRegisterCommandsEvent(Consumer<CommandDispatcher<CommandSourceStack>> eventHandler);
|
||||
|
||||
protected abstract void subscribeClientStartedEvent(Runnable eventHandler);
|
||||
protected abstract void subscribeServerStartingEvent(Consumer<MinecraftServer> eventHandler);
|
||||
protected abstract void runDelayedSetup();
|
||||
|
||||
|
||||
|
||||
//===================//
|
||||
// initialize events //
|
||||
//===================//
|
||||
|
||||
public void onInitializeClient()
|
||||
{
|
||||
DependencySetup.createClientBindings();
|
||||
|
||||
LOGGER.info("Initializing " + ModInfo.READABLE_NAME + " client.");
|
||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null);
|
||||
|
||||
this.startup();
|
||||
this.logBuildInfo();
|
||||
|
||||
this.createClientProxy().registerEvents();
|
||||
this.createServerProxy(false).registerEvents();
|
||||
|
||||
this.initializeModCompat();
|
||||
|
||||
LOGGER.info(ModInfo.READABLE_NAME + " client Initialized.");
|
||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null);
|
||||
|
||||
// Client uses config for auto-updater, so it's initialized here instead of post-init stage
|
||||
this.initConfig();
|
||||
logModIncompatibilityWarnings(); // needs to be called after config loading
|
||||
|
||||
this.subscribeClientStartedEvent(this::postInit);
|
||||
}
|
||||
|
||||
public void onInitializeServer()
|
||||
{
|
||||
DependencySetup.createServerBindings();
|
||||
|
||||
LOGGER.info("Initializing " + ModInfo.READABLE_NAME + " server.");
|
||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null);
|
||||
|
||||
this.startup();
|
||||
this.logBuildInfo();
|
||||
|
||||
// This prevents returning uninitialized Config values,
|
||||
// resulting from a circular reference mid-initialization in a static class
|
||||
// noinspection ResultOfMethodCallIgnored
|
||||
ThreadPresetConfigEventHandler.INSTANCE.toString();
|
||||
|
||||
this.createServerProxy(true).registerEvents();
|
||||
|
||||
this.initializeModCompat();
|
||||
|
||||
LOGGER.info(ModInfo.READABLE_NAME + " server Initialized.");
|
||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null);
|
||||
|
||||
this.subscribeRegisterCommandsEvent(dispatcher -> { this.commandInitializer = new CommandInitializer(dispatcher); });
|
||||
|
||||
this.subscribeServerStartingEvent(server ->
|
||||
{
|
||||
MinecraftServerWrapper.INSTANCE.dedicatedServer = (DedicatedServer)server;
|
||||
|
||||
this.initConfig();
|
||||
this.postInit();
|
||||
this.commandInitializer.initCommands();
|
||||
|
||||
this.checkForUpdates();
|
||||
|
||||
LOGGER.info("Dedicated server initialized at " + server.getServerDirectory());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
//===========================//
|
||||
// inner initializer methods //
|
||||
//===========================//
|
||||
|
||||
private void startup()
|
||||
{
|
||||
DependencySetup.createSharedBindings();
|
||||
SharedApi.init();
|
||||
this.createInitialBindings();
|
||||
}
|
||||
|
||||
private void logBuildInfo()
|
||||
{
|
||||
LOGGER.info(ModInfo.READABLE_NAME + ", Version: " + ModInfo.VERSION);
|
||||
|
||||
// if the build is stable the branch/commit/etc shouldn't be needed
|
||||
if (ModInfo.IS_DEV_BUILD)
|
||||
{
|
||||
LOGGER.info("DH Branch: " + ModJarInfo.Git_Branch);
|
||||
LOGGER.info("DH Commit: " + ModJarInfo.Git_Commit);
|
||||
LOGGER.info("DH Jar Build Source: " + ModJarInfo.Build_Source);
|
||||
}
|
||||
}
|
||||
|
||||
protected <T extends IModAccessor> void tryCreateModCompatAccessor(String modId, Class<? super T> accessorClass, Supplier<T> accessorConstructor)
|
||||
{
|
||||
IModChecker modChecker = SingletonInjector.INSTANCE.get(IModChecker.class);
|
||||
if (modChecker.isModLoaded(modId))
|
||||
{
|
||||
//noinspection unchecked
|
||||
ModAccessorInjector.INSTANCE.bind((Class<? extends IModAccessor>) accessorClass, accessorConstructor.get());
|
||||
}
|
||||
}
|
||||
|
||||
private void initConfig()
|
||||
{
|
||||
ConfigBase.INSTANCE = new ConfigBase(ModInfo.ID, ModInfo.NAME, Config.class, ModInfo.CONFIG_FILE_VERSION);
|
||||
Config.completeDelayedSetup();
|
||||
}
|
||||
|
||||
private void checkForUpdates()
|
||||
{
|
||||
if (Config.Client.Advanced.AutoUpdater.enableAutoUpdater.get())
|
||||
{
|
||||
if (Config.Client.Advanced.AutoUpdater.enableSilentUpdates.get())
|
||||
{
|
||||
LOGGER.info("Silent updates are not allowed for dedicated servers; force disabling.");
|
||||
Config.Client.Advanced.AutoUpdater.enableSilentUpdates.set(false);
|
||||
}
|
||||
|
||||
SelfUpdater.onStart();
|
||||
}
|
||||
}
|
||||
|
||||
private void postInit()
|
||||
{
|
||||
LOGGER.info("Post-Initializing Mod");
|
||||
this.runDelayedSetup();
|
||||
LOGGER.info("Mod Post-Initialized");
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==================================//
|
||||
// mod partial compatibility checks //
|
||||
//==================================//
|
||||
|
||||
/**
|
||||
* Some mods will work with a few tweaks
|
||||
* or will partially work but have some known issues we can't solve.
|
||||
* This method will log (and display to chat if enabled)
|
||||
* these warnings and potential fixes.
|
||||
*/
|
||||
private static void logModIncompatibilityWarnings()
|
||||
{
|
||||
boolean showChatWarnings = Config.Common.Logging.Warning.showModCompatibilityWarningsOnStartup.get();
|
||||
IModChecker modChecker = SingletonInjector.INSTANCE.get(IModChecker.class);
|
||||
|
||||
String startingString = "Partially Incompatible Distant Horizons mod detected: ";
|
||||
|
||||
|
||||
|
||||
// Alex's caves
|
||||
if (modChecker.isModLoaded("alexscaves"))
|
||||
{
|
||||
// There've been a few reports about this mod breaking DH at a few different points in time
|
||||
// the fixes for said breakage changes depending on the version so unfortunately
|
||||
// all we can do is log a warning so the user can handle it.
|
||||
|
||||
if (showChatWarnings)
|
||||
{
|
||||
String message =
|
||||
// orange text
|
||||
"\u00A76" + "Distant Horizons: Alex's Cave detected." + "\u00A7r\n" +
|
||||
"You may have to change Alex's config for DH to render. ";
|
||||
ClientApi.INSTANCE.showChatMessageNextFrame(message);
|
||||
}
|
||||
|
||||
LOGGER.warn(startingString + "[Alex's Caves] may require some config changes in order to render Distant Horizons correctly.");
|
||||
}
|
||||
|
||||
// William Wythers' Overhauled Overworld (WWOO)
|
||||
if (modChecker.isModLoaded("wwoo"))
|
||||
{
|
||||
// WWOO has a bug with it's world gen that can't be fixed by DH or WWOO
|
||||
// (at least that is what James learned after talking with WWOO)
|
||||
// WWOO will cause grid lines to appear in the world when DH generates the chunks
|
||||
// this might be due to how WWOO uses features for everything when generating
|
||||
// and said features don't always get to the edge of said chunks.
|
||||
|
||||
String wwooWarning = "LODs generated by DH may have grid lines between sections. Disabling either WWOO or DH's distant generator will fix the problem.";
|
||||
|
||||
if (showChatWarnings)
|
||||
{
|
||||
String message =
|
||||
// orange text
|
||||
"\u00A76" + "Distant Horizons: WWOO detected." + "\u00A7r\n" +
|
||||
wwooWarning;
|
||||
ClientApi.INSTANCE.showChatMessageNextFrame(message);
|
||||
}
|
||||
|
||||
LOGGER.warn(startingString + "[WWOO] "+ wwooWarning);
|
||||
}
|
||||
|
||||
// Chunky
|
||||
boolean chunkyPresent = false;
|
||||
try
|
||||
{
|
||||
Class.forName("org.popcraft.chunky.api.ChunkyAPI");
|
||||
chunkyPresent = true;
|
||||
}
|
||||
catch (ClassNotFoundException ignore) { }
|
||||
|
||||
if (chunkyPresent)
|
||||
{
|
||||
// Chunky can generate chunks faster than DH can process them,
|
||||
// causing holes in the LODs.
|
||||
// Generally it's better and faster to use DH's world generator.
|
||||
|
||||
String chunkyWarning = "Chunky can cause DH LODs to have holes " +
|
||||
"since Chunky can generate chunks faster than DH can process them. \n" +
|
||||
"Using DH's distant generator instead of chunky or increasing DH's CPU thread count can resolve the issue.";
|
||||
|
||||
if (showChatWarnings)
|
||||
{
|
||||
String message =
|
||||
// orange text
|
||||
"\u00A76" + "Distant Horizons: Chunky detected." + "\u00A7r\n" +
|
||||
chunkyWarning;
|
||||
ClientApi.INSTANCE.showChatMessageNextFrame(message);
|
||||
}
|
||||
|
||||
LOGGER.warn(startingString + "[Chunky] "+ chunkyWarning);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper classes //
|
||||
//================//
|
||||
|
||||
public interface IEventProxy
|
||||
{
|
||||
void registerEvents();
|
||||
}
|
||||
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
package com.seibel.distanthorizons.common;
|
||||
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.logging.ConfigBasedLogger;
|
||||
import com.seibel.distanthorizons.core.network.event.internal.IncompatibleMessageInternalEvent;
|
||||
import com.seibel.distanthorizons.core.network.event.internal.ProtocolErrorInternalEvent;
|
||||
import com.seibel.distanthorizons.core.network.messages.MessageRegistry;
|
||||
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
|
||||
import com.seibel.distanthorizons.core.network.messages.base.CloseReasonMessage;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class AbstractPluginPacketSender implements IPluginPacketSender
|
||||
{
|
||||
private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(),
|
||||
() -> Config.Common.Logging.logNetworkEvent.get());
|
||||
|
||||
#if MC_VER >= MC_1_21_1
|
||||
public static final ResourceLocation WRAPPER_PACKET_RESOURCE = ResourceLocation.fromNamespaceAndPath(ModInfo.RESOURCE_NAMESPACE, ModInfo.WRAPPER_PACKET_PATH);
|
||||
#else
|
||||
public static final ResourceLocation WRAPPER_PACKET_RESOURCE = new ResourceLocation(ModInfo.RESOURCE_NAMESPACE, ModInfo.WRAPPER_PACKET_PATH);
|
||||
#endif
|
||||
|
||||
|
||||
@Override
|
||||
public final void sendToClient(IServerPlayerWrapper serverPlayer, AbstractNetworkMessage message)
|
||||
{
|
||||
this.sendToClient((ServerPlayer) serverPlayer.getWrappedMcObject(), message);
|
||||
}
|
||||
public abstract void sendToClient(ServerPlayer serverPlayer, AbstractNetworkMessage message);
|
||||
|
||||
@Override
|
||||
public abstract void sendToServer(AbstractNetworkMessage message);
|
||||
|
||||
public static AbstractNetworkMessage decodeMessage(FriendlyByteBuf in)
|
||||
{
|
||||
AbstractNetworkMessage message = null;
|
||||
|
||||
try
|
||||
{
|
||||
in.markReaderIndex();
|
||||
|
||||
int protocolVersion = in.readShort();
|
||||
if (protocolVersion != ModInfo.PROTOCOL_VERSION)
|
||||
{
|
||||
return new IncompatibleMessageInternalEvent(protocolVersion);
|
||||
}
|
||||
|
||||
message = MessageRegistry.INSTANCE.createMessage(in.readUnsignedShort());
|
||||
message.decode(in);
|
||||
|
||||
if (in.isReadable())
|
||||
{
|
||||
throw new IOException("Buffer has not been fully read");
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
in.resetReaderIndex();
|
||||
|
||||
LOGGER.error("Failed to decode message", e);
|
||||
LOGGER.error("Buffer: ["+in+"]");
|
||||
LOGGER.error("Buffer contents: ["+ByteBufUtil.hexDump(in)+"]");
|
||||
|
||||
return new ProtocolErrorInternalEvent(e, message, true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Prevent connection crashing if not entire buffer has been read
|
||||
in.readerIndex(in.writerIndex());
|
||||
}
|
||||
}
|
||||
|
||||
public static void encodeMessage(FriendlyByteBuf out, AbstractNetworkMessage message)
|
||||
{
|
||||
// This is intentionally unhandled, because errors related to this are unlikely to appear in wild
|
||||
Objects.requireNonNull(message);
|
||||
out.writeShort(ModInfo.PROTOCOL_VERSION);
|
||||
|
||||
try
|
||||
{
|
||||
out.markWriterIndex();
|
||||
out.writeShort(MessageRegistry.INSTANCE.getMessageId(message));
|
||||
message.encode(out);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOGGER.error("Failed to encode message", e);
|
||||
LOGGER.error("Message: ["+message+"]");
|
||||
|
||||
message.getSession().tryHandleMessage(new ProtocolErrorInternalEvent(e, message, false));
|
||||
|
||||
// Encode close reason message instead
|
||||
out.resetWriterIndex();
|
||||
message = new CloseReasonMessage("Internal error on other side");
|
||||
out.writeShort(MessageRegistry.INSTANCE.getMessageId(message));
|
||||
message.encode(out);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.seibel.distanthorizons.common;
|
||||
|
||||
#if MC_VER >= MC_1_20_6
|
||||
|
||||
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public record CommonPacketPayload(@Nullable AbstractNetworkMessage message) implements CustomPacketPayload
|
||||
{
|
||||
public static final Type<CommonPacketPayload> TYPE = new Type<>(AbstractPluginPacketSender.WRAPPER_PACKET_RESOURCE);
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Type<? extends CustomPacketPayload> type() { return TYPE; }
|
||||
|
||||
|
||||
public static class Codec implements StreamCodec<FriendlyByteBuf, CommonPacketPayload>
|
||||
{
|
||||
@NotNull
|
||||
@Override
|
||||
public CommonPacketPayload decode(@NotNull FriendlyByteBuf in)
|
||||
{ return new CommonPacketPayload(AbstractPluginPacketSender.decodeMessage(in)); }
|
||||
|
||||
@Override
|
||||
public void encode(@NotNull FriendlyByteBuf out, CommonPacketPayload payload)
|
||||
{ AbstractPluginPacketSender.encodeMessage(out, payload.message()); }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.common;
|
||||
|
||||
import com.seibel.distanthorizons.common.forge.LodForgeMethodCaller;
|
||||
import com.seibel.distanthorizons.common.wrappers.DependencySetup;
|
||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||
import com.seibel.distanthorizons.core.api.internal.SharedApi;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.config.ConfigBase;
|
||||
|
||||
/**
|
||||
* This is the common main class
|
||||
*
|
||||
* @author Ran
|
||||
*/
|
||||
public class LodCommonMain
|
||||
{
|
||||
public static boolean forge = false;
|
||||
public static LodForgeMethodCaller forgeMethodCaller;
|
||||
|
||||
|
||||
|
||||
public static void startup(LodForgeMethodCaller forgeMethodCaller)
|
||||
{
|
||||
if (forgeMethodCaller != null)
|
||||
{
|
||||
LodCommonMain.forge = true;
|
||||
LodCommonMain.forgeMethodCaller = forgeMethodCaller;
|
||||
}
|
||||
|
||||
DependencySetup.createSharedBindings();
|
||||
SharedApi.init();
|
||||
// if (!serverSided) {
|
||||
// new NetworkReceiver().register_Client();
|
||||
// } else {
|
||||
// new NetworkReceiver().register_Server();
|
||||
// }
|
||||
}
|
||||
|
||||
public static void initConfig()
|
||||
{
|
||||
ConfigBase.INSTANCE = new ConfigBase(ModInfo.ID, ModInfo.NAME, Config.class, 2);
|
||||
Config.completeDelayedSetup();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package com.seibel.distanthorizons.common.commands;
|
||||
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.seibel.distanthorizons.common.wrappers.misc.ServerPlayerWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
|
||||
#if MC_VER >= MC_1_19_2
|
||||
import net.minecraft.network.chat.Component;
|
||||
|
||||
import java.util.Objects;
|
||||
#else // < 1.19.2
|
||||
import net.minecraft.network.chat.TranslatableComponent;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Abstract class providing common functionality for DH's commands.
|
||||
*/
|
||||
public abstract class AbstractCommand
|
||||
{
|
||||
public abstract LiteralArgumentBuilder<CommandSourceStack> buildCommand();
|
||||
|
||||
|
||||
/**
|
||||
* Sends a success response to the player with the given text.
|
||||
*
|
||||
* @param commandContext The command context to send the response to.
|
||||
* @param text The text to display in the success message.
|
||||
* @return 1, indicating that the command was successful.
|
||||
*/
|
||||
protected int sendSuccessResponse(CommandContext<CommandSourceStack> commandContext, String text, boolean notifyAdmins)
|
||||
{
|
||||
#if MC_VER >= MC_1_20_1
|
||||
commandContext.getSource().sendSuccess(() -> Component.literal(text), notifyAdmins);
|
||||
#elif MC_VER >= MC_1_19_2
|
||||
commandContext.getSource().sendSuccess(Component.literal(text), notifyAdmins);
|
||||
#else
|
||||
commandContext.getSource().sendSuccess(new TranslatableComponent(text), notifyAdmins);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a failure response to the player with the given text.
|
||||
*
|
||||
* @param commandContext The command context to send the response to.
|
||||
* @param text The text to display in the failure message.
|
||||
* @return 1, indicating that the command was successful.
|
||||
*/
|
||||
protected int sendFailureResponse(CommandContext<CommandSourceStack> commandContext, String text)
|
||||
{
|
||||
#if MC_VER >= MC_1_20_1
|
||||
commandContext.getSource().sendFailure(Component.literal(text));
|
||||
#elif MC_VER >= MC_1_19_2
|
||||
commandContext.getSource().sendFailure(Component.literal(text));
|
||||
#else
|
||||
commandContext.getSource().sendFailure(new TranslatableComponent(text));
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server player from a command context.
|
||||
*
|
||||
* @param commandContext The command context to get the server player from.
|
||||
* @return The server player wrapper for the player who sent the command.
|
||||
*/
|
||||
protected IServerPlayerWrapper getSourcePlayer(CommandContext<CommandSourceStack> commandContext) #if MC_VER < MC_1_19_2 throws CommandSyntaxException #endif
|
||||
{
|
||||
#if MC_VER >= MC_1_19_2
|
||||
return ServerPlayerWrapper.getWrapper(Objects.requireNonNull(commandContext.getSource().getPlayer()));
|
||||
#else
|
||||
return ServerPlayerWrapper.getWrapper(commandContext.getSource().getPlayerOrException());
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the source of a command is a player.
|
||||
*
|
||||
* @param source The source of the command to check.
|
||||
* @return True if the source is a player, false otherwise.
|
||||
*/
|
||||
protected boolean isPlayerSource(CommandSourceStack source)
|
||||
{
|
||||
#if MC_VER >= MC_1_19_2
|
||||
return source.isPlayer();
|
||||
#else
|
||||
try
|
||||
{
|
||||
source.getPlayerOrException();
|
||||
return true;
|
||||
}
|
||||
catch (CommandSyntaxException e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package com.seibel.distanthorizons.common.commands;
|
||||
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
|
||||
import static com.seibel.distanthorizons.core.network.messages.MessageRegistry.DEBUG_CODEC_CRASH_MESSAGE;
|
||||
import static net.minecraft.commands.Commands.literal;
|
||||
|
||||
/**
|
||||
* Initializes commands of the mod.
|
||||
*/
|
||||
public class CommandInitializer
|
||||
{
|
||||
private final CommandDispatcher<CommandSourceStack> commandDispatcher;
|
||||
|
||||
/**
|
||||
* Constructs a new instance of this class.
|
||||
*
|
||||
* @param commandDispatcher The dispatcher to use for registering commands.
|
||||
*/
|
||||
public CommandInitializer(CommandDispatcher<CommandSourceStack> commandDispatcher)
|
||||
{
|
||||
this.commandDispatcher = commandDispatcher;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Initializes all available commands.
|
||||
*/
|
||||
public void initCommands()
|
||||
{
|
||||
LiteralArgumentBuilder<CommandSourceStack> builder = literal("dh")
|
||||
.requires(source -> source.hasPermission(4));
|
||||
|
||||
builder.then(new ConfigCommand().buildCommand());
|
||||
builder.then(new DebugCommand().buildCommand());
|
||||
builder.then(new PregenCommand().buildCommand());
|
||||
|
||||
if (DEBUG_CODEC_CRASH_MESSAGE)
|
||||
{
|
||||
builder.then(new CrashCommand().buildCommand());
|
||||
}
|
||||
|
||||
this.commandDispatcher.register(builder);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
package com.seibel.distanthorizons.common.commands;
|
||||
|
||||
import com.mojang.brigadier.arguments.*;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.seibel.distanthorizons.core.config.ConfigBase;
|
||||
import com.seibel.distanthorizons.core.config.types.AbstractConfigType;
|
||||
import com.seibel.distanthorizons.core.config.types.ConfigEntry;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.function.ToIntBiFunction;
|
||||
|
||||
import static com.mojang.brigadier.arguments.DoubleArgumentType.doubleArg;
|
||||
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
|
||||
import static net.minecraft.commands.Commands.argument;
|
||||
import static net.minecraft.commands.Commands.literal;
|
||||
|
||||
/**
|
||||
* Command for managing config.
|
||||
*/
|
||||
public class ConfigCommand extends AbstractCommand
|
||||
{
|
||||
private static final List<CommandArgumentData<?>> commandArguments = Arrays.asList(
|
||||
new CommandArgumentData<>(Integer.class, configEntry -> integer(configEntry.getMin(), configEntry.getMax()), IntegerArgumentType::getInteger),
|
||||
new CommandArgumentData<>(Double.class, configEntry -> doubleArg(configEntry.getMin(), configEntry.getMax()), DoubleArgumentType::getDouble),
|
||||
new CommandArgumentData<>(Boolean.class, BoolArgumentType::bool, BoolArgumentType::getBool),
|
||||
new CommandArgumentData<>(String.class, StringArgumentType::string, StringArgumentType::getString)
|
||||
);
|
||||
|
||||
/**
|
||||
* Builds a command tree.
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public LiteralArgumentBuilder<CommandSourceStack> buildCommand()
|
||||
{
|
||||
LiteralArgumentBuilder<CommandSourceStack> builder = literal("config");
|
||||
HashSet<String> addedCommands = new HashSet<>();
|
||||
|
||||
for (AbstractConfigType<?, ?> type : ConfigBase.INSTANCE.entries)
|
||||
{
|
||||
// Skip non-config entries
|
||||
if (!(type instanceof ConfigEntry))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//noinspection PatternVariableCanBeUsed
|
||||
ConfigEntry configEntry = (ConfigEntry) type;
|
||||
if (configEntry.getChatCommandName() == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!addedCommands.add(configEntry.getChatCommandName()))
|
||||
{
|
||||
throw new IllegalStateException("Duplicate command name: " + configEntry.getChatCommandName());
|
||||
}
|
||||
|
||||
LiteralArgumentBuilder<CommandSourceStack> subcommand = literal(configEntry.getChatCommandName())
|
||||
.executes(commandContext -> this.sendSuccessResponse(commandContext,
|
||||
"\n" +
|
||||
"Description of §l" + configEntry.getChatCommandName() + "§r:\n" +
|
||||
"§o" + configEntry.getComment().trim() + "§r\n" +
|
||||
"§7Config file name: §f" + configEntry.name + "§7, category: §f" + configEntry.category + "\n" +
|
||||
"\n" +
|
||||
"Current value of " + configEntry.getChatCommandName() + " is §n" + configEntry.get() + "§r",
|
||||
false
|
||||
));
|
||||
|
||||
ToIntBiFunction<CommandContext<CommandSourceStack>, Object> updateConfigValue = (commandContext, value) -> {
|
||||
configEntry.set(value);
|
||||
return this.sendSuccessResponse(commandContext, "Changed the value of [" + configEntry.getChatCommandName() + "] to [" + value + "]", true);
|
||||
};
|
||||
|
||||
// Enum type needs a special case since enums aren't represented by existing argument type
|
||||
// and need literals for each individual value
|
||||
if (Enum.class.isAssignableFrom(configEntry.getType()))
|
||||
{
|
||||
for (Object choice : configEntry.getType().getEnumConstants())
|
||||
{
|
||||
subcommand.then(
|
||||
literal(choice.toString())
|
||||
.executes(c -> updateConfigValue.applyAsInt(c, choice))
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
boolean setterAdded = false;
|
||||
for (CommandArgumentData<?> commandArgumentData : commandArguments)
|
||||
{
|
||||
if (!commandArgumentData.argumentClass.isAssignableFrom(configEntry.getType()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
subcommand.then(argument("value", commandArgumentData.getArgumentType(configEntry))
|
||||
.executes(c -> updateConfigValue.applyAsInt(c, commandArgumentData.getValue(c, "value"))));
|
||||
|
||||
setterAdded = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!setterAdded)
|
||||
{
|
||||
throw new RuntimeException("Config type of " + type.getName() + " is not supported: " + configEntry.getType().getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
builder.then(subcommand);
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static class CommandArgumentData<T>
|
||||
{
|
||||
public final Class<T> argumentClass;
|
||||
public final Function<ConfigEntry<T>, ArgumentType<T>> argumentTypeFunction;
|
||||
private final BiFunction<CommandContext<CommandSourceStack>, String, T> valueGetter;
|
||||
|
||||
public CommandArgumentData(Class<T> argumentClass, Supplier<ArgumentType<T>> argumentTypeSupplier, BiFunction<CommandContext<CommandSourceStack>, String, T> valueGetter)
|
||||
{
|
||||
this(argumentClass, configEntry -> argumentTypeSupplier.get(), valueGetter);
|
||||
}
|
||||
public CommandArgumentData(Class<T> argumentClass, Function<ConfigEntry<T>, ArgumentType<T>> argumentTypeFunction, BiFunction<CommandContext<CommandSourceStack>, String, T> valueGetter)
|
||||
{
|
||||
this.argumentClass = argumentClass;
|
||||
this.argumentTypeFunction = argumentTypeFunction;
|
||||
this.valueGetter = valueGetter;
|
||||
}
|
||||
|
||||
public ArgumentType<T> getArgumentType(ConfigEntry<T> configEntry)
|
||||
{
|
||||
return this.argumentTypeFunction.apply(configEntry);
|
||||
}
|
||||
|
||||
public T getValue(CommandContext<CommandSourceStack> commandContext, String argumentName)
|
||||
{
|
||||
return this.valueGetter.apply(commandContext, argumentName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.seibel.distanthorizons.common.commands;
|
||||
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.seibel.distanthorizons.core.api.internal.SharedApi;
|
||||
import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerState;
|
||||
import com.seibel.distanthorizons.core.network.messages.base.CodecCrashMessage;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
|
||||
import static net.minecraft.commands.Commands.literal;
|
||||
|
||||
public class CrashCommand extends AbstractCommand
|
||||
{
|
||||
@Override
|
||||
public LiteralArgumentBuilder<CommandSourceStack> buildCommand()
|
||||
{
|
||||
return literal("crash")
|
||||
.requires(this::isPlayerSource)
|
||||
.then(literal("encode")
|
||||
.executes(c -> {
|
||||
assert SharedApi.getIDhServerWorld() != null;
|
||||
|
||||
ServerPlayerState serverPlayerState = SharedApi.getIDhServerWorld().getServerPlayerStateManager()
|
||||
.getConnectedPlayer(this.getSourcePlayer(c));
|
||||
if (serverPlayerState != null)
|
||||
{
|
||||
serverPlayerState.networkSession.sendMessage(new CodecCrashMessage(CodecCrashMessage.ECrashPhase.ENCODE));
|
||||
}
|
||||
return 1;
|
||||
}))
|
||||
.then(literal("decode")
|
||||
.executes(c -> {
|
||||
assert SharedApi.getIDhServerWorld() != null;
|
||||
|
||||
ServerPlayerState serverPlayerState = SharedApi.getIDhServerWorld().getServerPlayerStateManager()
|
||||
.getConnectedPlayer(this.getSourcePlayer(c));
|
||||
if (serverPlayerState != null)
|
||||
{
|
||||
serverPlayerState.networkSession.sendMessage(new CodecCrashMessage(CodecCrashMessage.ECrashPhase.DECODE));
|
||||
}
|
||||
return 1;
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.seibel.distanthorizons.common.commands;
|
||||
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static net.minecraft.commands.Commands.literal;
|
||||
|
||||
public class DebugCommand extends AbstractCommand
|
||||
{
|
||||
@Override
|
||||
public LiteralArgumentBuilder<CommandSourceStack> buildCommand()
|
||||
{
|
||||
return literal("debug")
|
||||
.executes(c -> {
|
||||
List<String> lines = new ArrayList<>();
|
||||
F3Screen.addStringToDisplay(lines);
|
||||
return this.sendSuccessResponse(c, String.join("\n", lines), false);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package com.seibel.distanthorizons.common.commands;
|
||||
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.generation.PregenManager;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.arguments.DimensionArgument;
|
||||
import net.minecraft.commands.arguments.coordinates.ColumnPosArgument;
|
||||
import net.minecraft.server.level.ColumnPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static com.mojang.brigadier.arguments.IntegerArgumentType.getInteger;
|
||||
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
|
||||
import static net.minecraft.commands.Commands.argument;
|
||||
import static net.minecraft.commands.Commands.literal;
|
||||
|
||||
public class PregenCommand extends AbstractCommand
|
||||
{
|
||||
private final PregenManager pregenManager = new PregenManager();
|
||||
|
||||
@Override
|
||||
public LiteralArgumentBuilder<CommandSourceStack> buildCommand()
|
||||
{
|
||||
LiteralArgumentBuilder<CommandSourceStack> statusCommand = literal("status")
|
||||
.executes(this::pregenStatus);
|
||||
|
||||
LiteralArgumentBuilder<CommandSourceStack> startCommand = literal("start")
|
||||
.then(argument("dimension", DimensionArgument.dimension())
|
||||
.then(argument("origin", ColumnPosArgument.columnPos())
|
||||
.then(argument("chunkRadius", integer(32))
|
||||
.executes(this::pregenStart))));
|
||||
|
||||
LiteralArgumentBuilder<CommandSourceStack> stopCommand = literal("stop")
|
||||
.executes(this::pregenStop);
|
||||
|
||||
return literal("pregen")
|
||||
.then(statusCommand)
|
||||
.then(startCommand)
|
||||
.then(stopCommand);
|
||||
}
|
||||
|
||||
|
||||
private int pregenStatus(CommandContext<CommandSourceStack> c)
|
||||
{
|
||||
String statusString = this.pregenManager.getStatusString();
|
||||
//noinspection ReplaceNullCheck
|
||||
if (statusString != null)
|
||||
{
|
||||
return this.sendSuccessResponse(c, statusString, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.sendSuccessResponse(c, "Pregen is not running", false);
|
||||
}
|
||||
}
|
||||
|
||||
private int pregenStart(CommandContext<CommandSourceStack> c) throws CommandSyntaxException
|
||||
{
|
||||
this.sendSuccessResponse(c, "Starting pregen. Progress will be in the server console.", true);
|
||||
|
||||
ServerLevel level = DimensionArgument.getDimension(c, "dimension");
|
||||
ColumnPos origin = ColumnPosArgument.getColumnPos(c, "origin");
|
||||
int chunkRadius = getInteger(c, "chunkRadius");
|
||||
|
||||
CompletableFuture<Void> future = this.pregenManager.startPregen(
|
||||
ServerLevelWrapper.getWrapper(level),
|
||||
new DhBlockPos2D(#if MC_VER >= MC_1_19_2 origin.x(), origin.z() #else origin.x, origin.z #endif),
|
||||
chunkRadius
|
||||
);
|
||||
|
||||
future.whenComplete((result, throwable) -> {
|
||||
if (throwable instanceof CancellationException)
|
||||
{
|
||||
this.sendSuccessResponse(c, "Pregen is cancelled", true);
|
||||
return;
|
||||
}
|
||||
else if (throwable != null)
|
||||
{
|
||||
this.sendFailureResponse(c, "Pregen failed: " + throwable.getMessage() + "\n Check the logs for more details.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.sendSuccessResponse(c, "Pregen is complete", true);
|
||||
});
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private int pregenStop(CommandContext<CommandSourceStack> c)
|
||||
{
|
||||
CompletableFuture<Void> runningPregen = this.pregenManager.getRunningPregen();
|
||||
if (runningPregen == null)
|
||||
{
|
||||
return this.sendFailureResponse(c, "Pregen is not running");
|
||||
}
|
||||
|
||||
runningPregen.cancel(true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
package com.seibel.distanthorizons.common.commonMixins;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.api.internal.ServerApi;
|
||||
import com.seibel.distanthorizons.core.api.internal.SharedApi;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
public class MixinChunkMapCommon
|
||||
{
|
||||
|
||||
public static void onChunkSave(ServerLevel level, ChunkAccess chunk, CallbackInfoReturnable<Boolean> ci)
|
||||
{
|
||||
// is this position already being updated?
|
||||
if (SharedApi.isChunkAtChunkPosAlreadyUpdating(chunk.getPos().x, chunk.getPos().z))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// is this chunk being saved to disk?
|
||||
boolean savingChunkToDisk = ci.getReturnValue();
|
||||
// true means a chunk was saved to disk
|
||||
if (!savingChunkToDisk)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO are the following validations necessary since we are checking above if
|
||||
// the callback return value should state if the chunk was actually saved or not?
|
||||
// Do we trust it to always be correct?
|
||||
|
||||
|
||||
|
||||
// corrupt/incomplete chunk validation //
|
||||
|
||||
// MC has a tendency to try saving incomplete or corrupted chunks (which show up as empty or black chunks)
|
||||
// this logic should prevent that from happening
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
if (chunk.isUnsaved() || chunk.getUpgradeData() != null || !chunk.isLightCorrect())
|
||||
{
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if (chunk.isUnsaved() || chunk.isUpgrading() || !chunk.isLightCorrect())
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// biome validation //
|
||||
|
||||
// some chunks may be missing their biomes, which cause issues when attempting to save them
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
if (chunk.getBiomes() == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#else
|
||||
try
|
||||
{
|
||||
// this will throw an exception if the biomes aren't set up
|
||||
chunk.getNoiseBiome(0,0,0);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// submit the update event
|
||||
ServerApi.INSTANCE.serverChunkSaveEvent(
|
||||
new ChunkWrapper(chunk, ServerLevelWrapper.getWrapper(level)),
|
||||
ServerLevelWrapper.getWrapper(level)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.common.forge;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper;
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
import net.minecraft.core.Direction;
|
||||
#if POST_MC_1_19_2
|
||||
import net.minecraft.util.RandomSource;
|
||||
#endif
|
||||
import net.minecraft.world.level.ColorResolver;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* used for calling methods that forge modified
|
||||
* (forge modifies vanilla methods for some reason)
|
||||
*
|
||||
* @author Ran
|
||||
*/
|
||||
public interface LodForgeMethodCaller
|
||||
{
|
||||
#if PRE_MC_1_19_2
|
||||
List<BakedQuad> getQuads(MinecraftClientWrapper mc, Block block, BlockState blockState, Direction direction, Random random); // FIXME: For 1.19
|
||||
#else
|
||||
List<BakedQuad> getQuads(MinecraftClientWrapper mc, Block block, BlockState blockState, Direction direction, RandomSource random); // FIXME: For 1.19
|
||||
#endif
|
||||
|
||||
int colorResolverGetColor(ColorResolver resolver, Biome biome, double x, double z);
|
||||
|
||||
}
|
||||
-96
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.common.rendering;
|
||||
|
||||
#if PRE_MC_1_19_4
|
||||
|
||||
import com.mojang.math.Matrix4f;
|
||||
#else
|
||||
|
||||
import org.joml.Matrix4f;
|
||||
#endif
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.util.RenderUtil;
|
||||
import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
public class SeamlessOverdraw
|
||||
{
|
||||
/**
|
||||
* Proof-of-concept experimental option, not intended for normal use. <br>
|
||||
* (Poorly) replaces Minecraft's far clip plane so it lines up with DH's near clip plane.
|
||||
*/
|
||||
public static float[] overwriteMinecraftNearFarClipPlanes(Matrix4f minecraftProjectionMatrix, float previousPartialTicks)
|
||||
{
|
||||
float[] matrixFloatArray;
|
||||
|
||||
#if PRE_MC_1_19_4
|
||||
FloatBuffer matrixFloatBuffer = FloatBuffer.allocate(16);
|
||||
minecraftProjectionMatrix.store(matrixFloatBuffer);
|
||||
matrixFloatArray = matrixFloatBuffer.array();
|
||||
#else
|
||||
// Passing float buffers in caused native code crashes, so we are passing in a float array instead
|
||||
matrixFloatArray = new float[16];
|
||||
minecraftProjectionMatrix.get(matrixFloatArray);
|
||||
#endif
|
||||
|
||||
return overwriteMinecraftNearFarClipPlanes(matrixFloatArray, previousPartialTicks);
|
||||
}
|
||||
|
||||
public static float[] overwriteMinecraftNearFarClipPlanes(Mat4f minecraftProjectionMatrix, float previousPartialTicks)
|
||||
{
|
||||
return overwriteMinecraftNearFarClipPlanes(minecraftProjectionMatrix.getValuesAsArray(), previousPartialTicks);
|
||||
}
|
||||
|
||||
private static float[] overwriteMinecraftNearFarClipPlanes(float[] projectionMatrixFloatArray, float previousPartialTicks)
|
||||
{
|
||||
float dhFarClipPlane = RenderUtil.getNearClipPlaneDistanceInBlocks(previousPartialTicks);
|
||||
|
||||
// works for fabric, bad not for forge for some reason :/
|
||||
float farClip = dhFarClipPlane * 5.1f; // magic number found via trial and error, James has no idea what it represents, except that it makes the seam between DH and vanilla rendering pretty close
|
||||
float nearClip = 0.5f; // this causes issues with some vanilla rendering, specifically the wireframe around selected blocks is slightly off. Unfortunately the ratio between the near and far clip plane can't be easily modified without completely screwing up the rendering.
|
||||
|
||||
// these may be the wrong index locations in any version of MC other than 1.18.2
|
||||
projectionMatrixFloatArray[10] = -((farClip + nearClip) / (farClip - nearClip)); // near clip plane
|
||||
projectionMatrixFloatArray[11] = -((2 * farClip * nearClip) / (farClip - nearClip)); // far clip plane
|
||||
|
||||
|
||||
return projectionMatrixFloatArray;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper methods //
|
||||
//================//
|
||||
|
||||
public static void applyLegacyProjectionMatrix(float[] projectionMatrixFloatArray)
|
||||
{
|
||||
int glMatrixMode = GL15.glGetInteger(GL15.GL_MATRIX_MODE);
|
||||
GL15.glMatrixMode(GL15.GL_PROJECTION);
|
||||
|
||||
GL15.glLoadMatrixf(projectionMatrixFloatArray);
|
||||
|
||||
GL15.glMatrixMode(glMatrixMode);
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
|
||||
+6
-3
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -22,7 +22,8 @@ package com.seibel.distanthorizons.common.wrappers;
|
||||
import com.seibel.distanthorizons.common.wrappers.gui.ClassicConfigGUI;
|
||||
import com.seibel.distanthorizons.common.wrappers.gui.LangWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.level.KeyedClientLevelManager;
|
||||
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftDedicatedServerWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftServerWrapper;
|
||||
import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.config.IConfigGui;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.config.ILangWrapper;
|
||||
@@ -32,6 +33,7 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
|
||||
|
||||
@@ -60,7 +62,7 @@ public class DependencySetup
|
||||
//@Environment(EnvType.SERVER)
|
||||
public static void createServerBindings()
|
||||
{
|
||||
SingletonInjector.INSTANCE.bind(IMinecraftSharedWrapper.class, MinecraftDedicatedServerWrapper.INSTANCE);
|
||||
SingletonInjector.INSTANCE.bind(IMinecraftSharedWrapper.class, MinecraftServerWrapper.INSTANCE);
|
||||
}
|
||||
|
||||
//@Environment(EnvType.CLIENT)
|
||||
@@ -69,6 +71,7 @@ public class DependencySetup
|
||||
SingletonInjector.INSTANCE.bind(IMinecraftClientWrapper.class, MinecraftClientWrapper.INSTANCE);
|
||||
SingletonInjector.INSTANCE.bind(IMinecraftSharedWrapper.class, MinecraftClientWrapper.INSTANCE);
|
||||
SingletonInjector.INSTANCE.bind(IMinecraftRenderWrapper.class, MinecraftRenderWrapper.INSTANCE);
|
||||
SingletonInjector.INSTANCE.bind(IMinecraftGLWrapper.class, MinecraftGLWrapper.INSTANCE);
|
||||
SingletonInjector.INSTANCE.bind(IConfigGui.class, ClassicConfigGUI.CONFIG_CORE_INTERFACE);
|
||||
}
|
||||
|
||||
|
||||
+7
-2
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -25,7 +25,12 @@ public class DependencySetupDoneCheck
|
||||
{
|
||||
// TODO move to DependencySetup
|
||||
public static boolean isDone = false;
|
||||
// TODO why is this here and what is its purpose?
|
||||
/**
|
||||
* This is used so we can override some MC logic when running
|
||||
* in DH's world generator.
|
||||
* Specifically so we can redirect threads to run on DH threads instead
|
||||
* of MC threads.
|
||||
*/
|
||||
public static Supplier<Boolean> getIsCurrentThreadDistantGeneratorThread = (() -> { return false; });
|
||||
|
||||
}
|
||||
|
||||
+31
-61
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -20,19 +20,11 @@
|
||||
package com.seibel.distanthorizons.common.wrappers;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
#if PRE_MC_1_19_4
|
||||
import com.mojang.math.Matrix4f;
|
||||
#else
|
||||
import org.joml.Matrix4f;
|
||||
#endif
|
||||
|
||||
import com.seibel.distanthorizons.core.enums.EDhDirection;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
|
||||
import com.seibel.distanthorizons.core.util.math.Mat4f;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
@@ -51,10 +43,31 @@ public class McObjectConverter
|
||||
{
|
||||
return y * 4 + x;
|
||||
}
|
||||
/** Taken from Minecraft's com.mojang.math.Matrix4f class from 1.18.2 */
|
||||
private static void storeMatrix(Matrix4f matrix, FloatBuffer buffer)
|
||||
|
||||
|
||||
/** 4x4 float matrix converter */
|
||||
@Deprecated
|
||||
public static Mat4f Convert(
|
||||
#if MC_VER < MC_1_19_4 com.mojang.math.Matrix4f
|
||||
#else org.joml.Matrix4f #endif
|
||||
mcMatrix)
|
||||
{
|
||||
#if PRE_MC_1_19_4
|
||||
FloatBuffer buffer = FloatBuffer.allocate(16);
|
||||
storeMatrix(mcMatrix, buffer);
|
||||
Mat4f matrix = new Mat4f(buffer);
|
||||
#if MC_VER < MC_1_19_4
|
||||
matrix.transpose(); // In 1.19.3 and later, we no longer need to transpose it
|
||||
#endif
|
||||
return matrix;
|
||||
}
|
||||
/** Taken from Minecraft's com.mojang.math.Matrix4f class from 1.18.2 */
|
||||
private static void storeMatrix(
|
||||
#if MC_VER < MC_1_19_4 com.mojang.math.Matrix4f
|
||||
#else org.joml.Matrix4f #endif
|
||||
matrix,
|
||||
FloatBuffer buffer)
|
||||
{
|
||||
#if MC_VER < MC_1_19_4
|
||||
matrix.store(buffer);
|
||||
#else
|
||||
// Mojang starts to use joml's Matrix4f libary in 1.19.3 so we copy their store method and use it here if its newer than 1.19.3
|
||||
@@ -77,18 +90,6 @@ public class McObjectConverter
|
||||
#endif
|
||||
}
|
||||
|
||||
/** 4x4 float matrix converter */
|
||||
public static Mat4f Convert(Matrix4f mcMatrix)
|
||||
{
|
||||
FloatBuffer buffer = FloatBuffer.allocate(16);
|
||||
storeMatrix(mcMatrix, buffer);
|
||||
Mat4f matrix = new Mat4f(buffer);
|
||||
#if PRE_MC_1_19_4
|
||||
matrix.transpose(); // In 1.19.3 and later, we no longer need to transpose it
|
||||
#endif
|
||||
return matrix;
|
||||
}
|
||||
|
||||
|
||||
static final Direction[] directions;
|
||||
static final EDhDirection[] lodDirections;
|
||||
@@ -134,41 +135,10 @@ public class McObjectConverter
|
||||
}
|
||||
}
|
||||
|
||||
public static BlockPos Convert(DhBlockPos wrappedPos)
|
||||
{
|
||||
return new BlockPos(wrappedPos.x, wrappedPos.y, wrappedPos.z);
|
||||
}
|
||||
public static ChunkPos Convert(DhChunkPos wrappedPos)
|
||||
{
|
||||
return new ChunkPos(wrappedPos.x, wrappedPos.z);
|
||||
}
|
||||
public static BlockPos Convert(DhBlockPos wrappedPos) { return new BlockPos(wrappedPos.getX(), wrappedPos.getY(), wrappedPos.getZ()); }
|
||||
public static ChunkPos Convert(DhChunkPos wrappedPos) { return new ChunkPos(wrappedPos.getX(), wrappedPos.getZ()); }
|
||||
|
||||
public static Direction Convert(EDhDirection lodDirection)
|
||||
{
|
||||
return directions[lodDirection.ordinal()];
|
||||
}
|
||||
public static EDhDirection Convert(Direction direction)
|
||||
{
|
||||
return lodDirections[direction.ordinal()];
|
||||
}
|
||||
public static void DebugCheckAllPackers()
|
||||
{
|
||||
BiConsumer<Integer, Integer> func = (x, z) -> DhChunkPos._DebugCheckPacker(x, z, ChunkPos.asLong(x, z));
|
||||
func.accept(0, 0);
|
||||
func.accept(12345, 134);
|
||||
func.accept(-12345, -134);
|
||||
func.accept(-30000000 / 16, 30000000 / 16);
|
||||
func.accept(30000000 / 16, -30000000 / 16);
|
||||
func.accept(30000000 / 16, 30000000 / 16);
|
||||
func.accept(-30000000 / 16, -30000000 / 16);
|
||||
Consumer<BlockPos> func2 = (p) -> DhBlockPos._DebugCheckPacker(p.getX(), p.getY(), p.getZ(), p.asLong());
|
||||
func2.accept(new BlockPos(0, 0, 0));
|
||||
func2.accept(new BlockPos(12345, 134, 123));
|
||||
func2.accept(new BlockPos(-12345, -134, -80));
|
||||
func2.accept(new BlockPos(-30000000, 2047, 30000000));
|
||||
func2.accept(new BlockPos(30000000, -2048, -30000000));
|
||||
func2.accept(new BlockPos(30000000, 2047, 30000000));
|
||||
func2.accept(new BlockPos(-30000000, -2048, -30000000));
|
||||
}
|
||||
public static Direction Convert(EDhDirection lodDirection) { return directions[lodDirection.ordinal()]; }
|
||||
public static EDhDirection Convert(Direction direction) { return lodDirections[direction.ordinal()]; }
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -59,7 +59,7 @@ public class VersionConstants implements IVersionConstants
|
||||
@Override
|
||||
public String getMinecraftVersion()
|
||||
{
|
||||
#if PRE_MC_1_19_2
|
||||
#if MC_VER < MC_1_19_2
|
||||
return Minecraft.getInstance().getGame().getVersion().getId();
|
||||
#else
|
||||
return SharedConstants.getCurrentVersion().getId();
|
||||
|
||||
+199
-45
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -19,7 +19,11 @@
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers;
|
||||
|
||||
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBiomeWrapper;
|
||||
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper;
|
||||
import com.seibel.distanthorizons.api.interfaces.override.worldGenerator.IDhApiWorldGenerator;
|
||||
import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper;
|
||||
import com.seibel.distanthorizons.api.interfaces.factories.IDhApiWrapperFactory;
|
||||
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
||||
@@ -28,6 +32,7 @@ import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.level.IDhServerLevel;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
@@ -35,9 +40,14 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvironmentWrapper;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
#if MC_VER > MC_1_17_1
|
||||
import net.minecraft.core.Holder;
|
||||
#endif
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -45,9 +55,6 @@ import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* This handles creating abstract wrapper objects.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 2022-12-5
|
||||
*/
|
||||
public class WrapperFactory implements IWrapperFactory
|
||||
{
|
||||
@@ -55,6 +62,9 @@ public class WrapperFactory implements IWrapperFactory
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// core methods //
|
||||
//==============//
|
||||
|
||||
@Override
|
||||
public AbstractBatchGenerationEnvironmentWrapper createBatchGenerator(IDhLevel targetLevel)
|
||||
@@ -69,17 +79,56 @@ public class WrapperFactory implements IWrapperFactory
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IDhApiBiomeWrapper getBiomeWrapper(String resourceLocationString, IDhApiLevelWrapper levelWrapper) throws IOException, ClassCastException
|
||||
{
|
||||
if (!(levelWrapper instanceof ILevelWrapper))
|
||||
{
|
||||
throw new ClassCastException("levelWrapper must be returned by DH and of type ["+ILevelWrapper.class.getName()+"].");
|
||||
}
|
||||
|
||||
return BiomeWrapper.deserialize(resourceLocationString, (ILevelWrapper)levelWrapper);
|
||||
}
|
||||
@Override
|
||||
public IDhApiBlockStateWrapper getDefaultBlockStateWrapper(String resourceLocationString, IDhApiLevelWrapper levelWrapper) throws IOException, ClassCastException
|
||||
{
|
||||
if (!(levelWrapper instanceof ILevelWrapper))
|
||||
{
|
||||
throw new ClassCastException("Invalid ["+IDhApiLevelWrapper.class.getSimpleName()+"] value given. Level wrapper object must be one given by the DH API (it can't be a custom implementation), specifically of type ["+ILevelWrapper.class.getName()+"].");
|
||||
}
|
||||
|
||||
return BlockStateWrapper.deserialize(resourceLocationString, (ILevelWrapper)levelWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBiomeWrapper deserializeBiomeWrapper(String str, ILevelWrapper levelWrapper) throws IOException { return BiomeWrapper.deserialize(str, levelWrapper); }
|
||||
@Override
|
||||
public IBiomeWrapper getPlainsBiomeWrapper(ILevelWrapper levelWrapper) // TODO is there a way we could get this without the levelWrapper? it isn't necessary but would clean up the code a bit
|
||||
{
|
||||
try
|
||||
{
|
||||
return BiomeWrapper.deserialize(BiomeWrapper.PLAINS_RESOURCE_LOCATION_STRING, levelWrapper);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new LodUtil.AssertFailureException("Unable to parse plains resource string ["+BiomeWrapper.PLAINS_RESOURCE_LOCATION_STRING+"], error:\n " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBlockStateWrapper deserializeBlockStateWrapper(String str, ILevelWrapper levelWrapper) throws IOException { return BlockStateWrapper.deserialize(str, levelWrapper); }
|
||||
|
||||
@Override
|
||||
public IBlockStateWrapper getAirBlockStateWrapper() { return BlockStateWrapper.AIR; }
|
||||
|
||||
@Override
|
||||
public HashSet<IBlockStateWrapper> getRendererIgnoredBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getRendererIgnoredBlocks(levelWrapper); }
|
||||
@Override
|
||||
public HashSet<IBlockStateWrapper> getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getRendererIgnoredCaveBlocks(levelWrapper); }
|
||||
|
||||
@Override
|
||||
public void resetRendererIgnoredCaveBlocks() { BlockStateWrapper.clearRendererIgnoredCaveBlocks(); }
|
||||
@Override
|
||||
public void resetRendererIgnoredBlocksSet() { BlockStateWrapper.clearRendererIgnoredBlocks(); }
|
||||
|
||||
|
||||
/**
|
||||
@@ -106,8 +155,7 @@ public class WrapperFactory implements IWrapperFactory
|
||||
}
|
||||
}
|
||||
|
||||
// MC 1.16, 1.18, 1.19, 1.20
|
||||
#if POST_MC_1_17_1 || MC_1_16_5
|
||||
//#if MC_VER <= MC_1_XX_X
|
||||
else if (objectArray.length == 2)
|
||||
{
|
||||
// correct number of parameters from the API
|
||||
@@ -126,43 +174,22 @@ public class WrapperFactory implements IWrapperFactory
|
||||
}
|
||||
// the level is needed for the DH level wrapper...
|
||||
Level level = (Level) objectArray[1];
|
||||
// ...the LevelReader is needed for chunk lighting
|
||||
LevelReader lightSource = level;
|
||||
|
||||
|
||||
// level wrapper
|
||||
ILevelWrapper levelWrapper;
|
||||
if (level instanceof ServerLevel)
|
||||
{
|
||||
levelWrapper = ServerLevelWrapper.getWrapper((ServerLevel)level);
|
||||
}
|
||||
else if (level instanceof ClientLevel)
|
||||
{
|
||||
levelWrapper = ClientLevelWrapper.getWrapper((ClientLevel)level);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
|
||||
}
|
||||
ILevelWrapper levelWrapper = level.isClientSide()
|
||||
? ClientLevelWrapper.getWrapper((ClientLevel)level)
|
||||
: ServerLevelWrapper.getWrapper((ServerLevel)level);
|
||||
|
||||
|
||||
return new ChunkWrapper(chunk, lightSource, levelWrapper);
|
||||
return new ChunkWrapper(chunk, levelWrapper);
|
||||
}
|
||||
// incorrect number of parameters from the API
|
||||
else
|
||||
{
|
||||
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
|
||||
}
|
||||
#else
|
||||
// Intentional compiler error to bring attention to the missing wrapper function.
|
||||
// If you need to work on an unimplemented version but don't have the ability to implement this yet
|
||||
// you can comment it out, but please don't commit it. Someone will have to implement it .
|
||||
|
||||
// After implementing the new version please read this method's javadocs for instructions
|
||||
// on what other locations also need to be updated, the DhAPI specifically needs to
|
||||
// be updated to state which objects this method accepts.
|
||||
not implemented for this version of Minecraft!
|
||||
#endif
|
||||
//#endif
|
||||
}
|
||||
/**
|
||||
* Note: when this is updated for different MC versions,
|
||||
@@ -170,25 +197,152 @@ public class WrapperFactory implements IWrapperFactory
|
||||
*/
|
||||
private static String createChunkWrapperErrorMessage(Object[] objectArray)
|
||||
{
|
||||
StringBuilder message = new StringBuilder(
|
||||
"Chunk wrapper creation failed. \n" +
|
||||
"Expected parameters: \n");
|
||||
String[] expectedClassNames;
|
||||
|
||||
// MC 1.16, 1.18, 1.19, 1.20
|
||||
#if POST_MC_1_17_1 || MC_1_16_5
|
||||
message.append("[" + ChunkAccess.class.getName() + "], \n");
|
||||
message.append("[" + ServerLevel.class.getName() + "] or [" + ClientLevel.class.getName() + "]. \n");
|
||||
#else
|
||||
// See preprocessor comment in createChunkWrapper() for full documentation
|
||||
not implemented for this version of Minecraft!
|
||||
//#if MC_VER <= MC_1_XX_X
|
||||
expectedClassNames = new String[]
|
||||
{
|
||||
ChunkAccess.class.getName(),
|
||||
"[ServerLevel] or [ClientLevel]" // Classes are not referenced by names to avoid exception when one of them is missing
|
||||
};
|
||||
//#endif
|
||||
|
||||
return createWrapperErrorMessage("Chunk wrapper", expectedClassNames, objectArray);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// api methods //
|
||||
//=============//
|
||||
|
||||
// documentation should be in the API interface
|
||||
|
||||
public IDhApiBiomeWrapper getBiomeWrapper(Object[] objectArray, IDhApiLevelWrapper levelWrapper)
|
||||
{
|
||||
// confirm the API level wrapper is also a Core wrapper
|
||||
if (!(levelWrapper instanceof ILevelWrapper))
|
||||
{
|
||||
throw new ClassCastException("Invalid ["+IDhApiLevelWrapper.class.getSimpleName()+"] value given. Level wrapper object must be one given by the DH API (it can't be a custom implementation), specifically of type ["+ILevelWrapper.class.getName()+"].");
|
||||
}
|
||||
ILevelWrapper coreLevelWrapper = (ILevelWrapper) levelWrapper;
|
||||
|
||||
|
||||
|
||||
#if MC_VER < MC_1_20_4
|
||||
if (objectArray.length != 1)
|
||||
{
|
||||
throw new ClassCastException(createBiomeWrapperErrorMessage(objectArray));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MC_VER < MC_1_18_2
|
||||
if (!(objectArray[0] instanceof Biome))
|
||||
{
|
||||
throw new ClassCastException(createBiomeWrapperErrorMessage(objectArray));
|
||||
}
|
||||
|
||||
Biome biome = (Biome) objectArray[0];
|
||||
return BiomeWrapper.getBiomeWrapper(biome, coreLevelWrapper);
|
||||
#else
|
||||
if (!(objectArray[0] instanceof Holder) || !(((Holder<?>) objectArray[0]).value() instanceof Biome))
|
||||
{
|
||||
throw new ClassCastException(createBiomeWrapperErrorMessage(objectArray));
|
||||
}
|
||||
|
||||
Holder<Biome> biomeHolder = (Holder<Biome>) objectArray[0];
|
||||
return BiomeWrapper.getBiomeWrapper(biomeHolder, coreLevelWrapper);
|
||||
#endif
|
||||
}
|
||||
/**
|
||||
* Note: when this is updated for different MC versions,
|
||||
* make sure you also update the documentation in {@link IDhApiWrapperFactory#getBiomeWrapper}.
|
||||
*/
|
||||
private static String createBiomeWrapperErrorMessage(Object[] objectArray)
|
||||
{
|
||||
String[] expectedClassNames;
|
||||
|
||||
#if MC_VER < MC_1_18_2
|
||||
expectedClassNames = new String[] { Biome.class.getName() };
|
||||
#else
|
||||
expectedClassNames = new String[] { Holder.class.getName()+"<"+Biome.class.getName()+">" };
|
||||
#endif
|
||||
|
||||
return createWrapperErrorMessage("Biome wrapper", expectedClassNames, objectArray);
|
||||
}
|
||||
|
||||
public IDhApiBlockStateWrapper getBlockStateWrapper(Object[] objectArray, IDhApiLevelWrapper levelWrapper)
|
||||
{
|
||||
// confirm the API level wrapper is also a Core wrapper
|
||||
if (!(levelWrapper instanceof ILevelWrapper))
|
||||
{
|
||||
throw new ClassCastException("Invalid ["+IDhApiLevelWrapper.class.getSimpleName()+"] value given. Level wrapper object must be one given by the DH API (it can't be a custom implementation), specifically of type ["+ILevelWrapper.class.getName()+"].");
|
||||
}
|
||||
ILevelWrapper coreLevelWrapper = (ILevelWrapper) levelWrapper;
|
||||
|
||||
|
||||
|
||||
//#if MC_VER <= MC_1_XX_X
|
||||
if (objectArray.length != 1)
|
||||
{
|
||||
throw new ClassCastException(createBlockStateWrapperErrorMessage(objectArray));
|
||||
}
|
||||
if (!(objectArray[0] instanceof BlockState))
|
||||
{
|
||||
throw new ClassCastException(createBlockStateWrapperErrorMessage(objectArray));
|
||||
}
|
||||
|
||||
BlockState blockState = (BlockState) objectArray[0];
|
||||
return BlockStateWrapper.fromBlockState(blockState, coreLevelWrapper);
|
||||
//#endif
|
||||
}
|
||||
/**
|
||||
* Note: when this is updated for different MC versions,
|
||||
* make sure you also update the documentation in {@link IDhApiWrapperFactory#getBlockStateWrapper}.
|
||||
*/
|
||||
private static String createBlockStateWrapperErrorMessage(Object[] objectArray)
|
||||
{
|
||||
String[] expectedClassNames;
|
||||
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
expectedClassNames = new String[] { Biome.class.getName() };
|
||||
#else
|
||||
expectedClassNames = new String[] { Holder.class.getName()+"<"+Biome.class.getName()+">" };
|
||||
#endif
|
||||
|
||||
return createWrapperErrorMessage("BlockState wrapper", expectedClassNames, objectArray);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper methods //
|
||||
//================//
|
||||
|
||||
private static String createWrapperErrorMessage(String wrapperName, String[] expectedClassNames, Object[] objectArray)
|
||||
{
|
||||
// error header
|
||||
StringBuilder message = new StringBuilder(
|
||||
wrapperName + " creation failed. \n" +
|
||||
"Expected object array parameters: \n");
|
||||
|
||||
|
||||
// expected parameters
|
||||
for (String expectedClassName : expectedClassNames)
|
||||
{
|
||||
message.append("[").append(expectedClassName).append("], \n");
|
||||
}
|
||||
|
||||
|
||||
// given parameters
|
||||
if (objectArray.length != 0)
|
||||
{
|
||||
message.append("Given parameters: ");
|
||||
for (Object obj : objectArray)
|
||||
{
|
||||
message.append("[").append(obj.getClass().getName()).append("], ");
|
||||
String objClassName = (obj != null) ? obj.getClass().getName() : "NULL";
|
||||
message.append("[").append(objClassName).append("], ");
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -196,8 +350,8 @@ public class WrapperFactory implements IWrapperFactory
|
||||
message.append(" No parameters given.");
|
||||
}
|
||||
|
||||
|
||||
return message.toString();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+174
-92
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -22,6 +22,7 @@ package com.seibel.distanthorizons.common.wrappers.block;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
@@ -32,19 +33,9 @@ import org.apache.logging.log4j.Logger;
|
||||
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
#if POST_MC_1_17
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.resources.RegistryOps;
|
||||
#endif
|
||||
|
||||
#if POST_MC_1_19_2
|
||||
#endif
|
||||
|
||||
|
||||
#if MC_1_16_5 || MC_1_17_1
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
import net.minecraft.core.Registry;
|
||||
#elif MC_1_18_2 || MC_1_19_2
|
||||
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
@@ -56,7 +47,7 @@ import net.minecraft.core.registries.Registries;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
#if !PRE_MC_1_18_2
|
||||
#if MC_VER >= MC_1_18_2
|
||||
import net.minecraft.world.level.biome.Biomes;
|
||||
#endif
|
||||
|
||||
@@ -64,32 +55,46 @@ import net.minecraft.world.level.biome.Biomes;
|
||||
/** This class wraps the minecraft BlockPos.Mutable (and BlockPos) class */
|
||||
public class BiomeWrapper implements IBiomeWrapper
|
||||
{
|
||||
// must be defined before AIR, otherwise a null pointer will be thrown
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
|
||||
#if PRE_MC_1_18_2
|
||||
|
||||
#if MC_VER < MC_1_18_2
|
||||
public static final ConcurrentMap<Biome, BiomeWrapper> WRAPPER_BY_BIOME = new ConcurrentHashMap<>();
|
||||
#else
|
||||
public static final ConcurrentMap<Holder<Biome>, BiomeWrapper> WRAPPER_BY_BIOME = new ConcurrentHashMap<>();
|
||||
#endif
|
||||
|
||||
public static final String EMPTY_STRING = "EMPTY";
|
||||
public static final ConcurrentHashMap<String, BiomeWrapper> WRAPPER_BY_RESOURCE_LOCATION = new ConcurrentHashMap<>();
|
||||
|
||||
public static final String EMPTY_BIOME_STRING = "EMPTY";
|
||||
public static final BiomeWrapper EMPTY_WRAPPER = new BiomeWrapper(null, null);
|
||||
|
||||
public static final String PLAINS_RESOURCE_LOCATION_STRING = "minecraft:plains";
|
||||
|
||||
/** keep track of broken biomes so we don't log every time */
|
||||
private static final HashSet<String> BrokenResourceLocationStrings = new HashSet<>();
|
||||
private static final HashSet<String> brokenResourceLocationStrings = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Only display this warning once, otherwise the log may be spammed <br>
|
||||
* This is a known issue when joining Hypixel.
|
||||
*/
|
||||
private static boolean emptyStringWarningLogged = false;
|
||||
private static boolean emptyLevelSerializeFailLogged = false;
|
||||
|
||||
|
||||
|
||||
// properties //
|
||||
|
||||
#if PRE_MC_1_18_2
|
||||
#if MC_VER < MC_1_18_2
|
||||
public final Biome biome;
|
||||
#else
|
||||
public final Holder<Biome> biome;
|
||||
#endif
|
||||
|
||||
/** technically final, but since it requires a method call to generate it can't be marked as such */
|
||||
private String serialString = null;
|
||||
private String serialString;
|
||||
private final int hashCode;
|
||||
|
||||
|
||||
|
||||
@@ -97,7 +102,7 @@ public class BiomeWrapper implements IBiomeWrapper
|
||||
// constructors //
|
||||
//==============//
|
||||
|
||||
static public IBiomeWrapper getBiomeWrapper(#if PRE_MC_1_18_2 Biome #else Holder<Biome> #endif biome, ILevelWrapper levelWrapper)
|
||||
static public IBiomeWrapper getBiomeWrapper(#if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif biome, ILevelWrapper levelWrapper)
|
||||
{
|
||||
if (biome == null)
|
||||
{
|
||||
@@ -116,12 +121,21 @@ public class BiomeWrapper implements IBiomeWrapper
|
||||
return newWrapper;
|
||||
}
|
||||
}
|
||||
|
||||
private BiomeWrapper(#if PRE_MC_1_18_2 Biome #else Holder<Biome> #endif biome, ILevelWrapper levelWrapper)
|
||||
private BiomeWrapper(#if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif biome, ILevelWrapper levelWrapper)
|
||||
{
|
||||
this.biome = biome;
|
||||
this.serialString = this.serialize(levelWrapper);
|
||||
LOGGER.trace("Created BiomeWrapper ["+this.serialString+"] for ["+biome+"]");
|
||||
this.hashCode = Objects.hash(this.serialString);
|
||||
|
||||
//LOGGER.trace("Created BiomeWrapper ["+this.serialString+"] for ["+biome+"]");
|
||||
}
|
||||
|
||||
/** should only be used to create {@link BiomeWrapper#EMPTY_WRAPPER} */
|
||||
private BiomeWrapper()
|
||||
{
|
||||
this.biome = null;
|
||||
this.serialString = EMPTY_BIOME_STRING;
|
||||
this.hashCode = Objects.hash(this.serialString);
|
||||
}
|
||||
|
||||
|
||||
@@ -135,10 +149,10 @@ public class BiomeWrapper implements IBiomeWrapper
|
||||
{
|
||||
if (this == EMPTY_WRAPPER)
|
||||
{
|
||||
return EMPTY_STRING;
|
||||
return EMPTY_BIOME_STRING;
|
||||
}
|
||||
|
||||
#if PRE_MC_1_18_2
|
||||
#if MC_VER < MC_1_18_2
|
||||
return biome.toString();
|
||||
#else
|
||||
return this.biome.unwrapKey().orElse(Biomes.THE_VOID).registry().toString();
|
||||
@@ -163,7 +177,7 @@ public class BiomeWrapper implements IBiomeWrapper
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() { return Objects.hash(this.getSerialString()); }
|
||||
public int hashCode() { return this.hashCode; }
|
||||
|
||||
@Override
|
||||
public String getSerialString() { return this.serialString; }
|
||||
@@ -182,52 +196,77 @@ public class BiomeWrapper implements IBiomeWrapper
|
||||
|
||||
public String serialize(ILevelWrapper levelWrapper)
|
||||
{
|
||||
if (levelWrapper == null)
|
||||
if (this.biome == null)
|
||||
{
|
||||
return EMPTY_STRING;
|
||||
return EMPTY_BIOME_STRING;
|
||||
}
|
||||
|
||||
|
||||
if (this.serialString == null)
|
||||
{
|
||||
net.minecraft.core.RegistryAccess registryAccess = Minecraft.getInstance().level.registryAccess();
|
||||
|
||||
ResourceLocation resourceLocation;
|
||||
#if MC_1_16_5 || MC_1_17_1
|
||||
resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome);
|
||||
#elif MC_1_18_2 || MC_1_19_2
|
||||
resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome.value());
|
||||
// we can't generate a serial string if the level is null
|
||||
if (levelWrapper == null)
|
||||
{
|
||||
if (!emptyLevelSerializeFailLogged)
|
||||
{
|
||||
emptyLevelSerializeFailLogged = true;
|
||||
LOGGER.warn("Unable to serialize biome: [" + this.biome + "] because the passed in level wrapper is null. Future errors of this type won't be logged.");
|
||||
}
|
||||
|
||||
return EMPTY_BIOME_STRING;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// generate the serial string //
|
||||
|
||||
Level level = (Level)levelWrapper.getWrappedMcObject();
|
||||
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
|
||||
|
||||
ResourceLocation resourceLocation;
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome);
|
||||
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
|
||||
resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome.value());
|
||||
#elif MC_VER < MC_1_21_3
|
||||
resourceLocation = registryAccess.registryOrThrow(Registries.BIOME).getKey(this.biome.value());
|
||||
#else
|
||||
resourceLocation = registryAccess.lookupOrThrow(Registries.BIOME).getKey(this.biome.value());
|
||||
#endif
|
||||
|
||||
if (resourceLocation == null)
|
||||
{
|
||||
String biomeName;
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
biomeName = this.biome.toString();
|
||||
#else
|
||||
resourceLocation = registryAccess.registryOrThrow(Registries.BIOME).getKey(this.biome.value());
|
||||
biomeName = this.biome.value().toString();
|
||||
#endif
|
||||
|
||||
if (resourceLocation == null)
|
||||
{
|
||||
String biomeName;
|
||||
#if MC_1_16_5 || MC_1_17_1
|
||||
biomeName = this.biome.toString();
|
||||
#else
|
||||
biomeName = this.biome.value().toString();
|
||||
#endif
|
||||
|
||||
LOGGER.warn("unable to serialize: " + biomeName);
|
||||
// shouldn't normally happen, but just in case
|
||||
this.serialString = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
this.serialString = resourceLocation.getNamespace() + ":" + resourceLocation.getPath();
|
||||
}
|
||||
LOGGER.warn("unable to serialize: " + biomeName);
|
||||
// shouldn't normally happen, but just in case
|
||||
this.serialString = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
this.serialString = resourceLocation.getNamespace() + ":" + resourceLocation.getPath();
|
||||
}
|
||||
|
||||
return this.serialString;
|
||||
}
|
||||
|
||||
// TODO would it be worth while to cache these objects in a ConcurrentHashMap<string, IBiomeWrapper>?
|
||||
public static IBiomeWrapper deserialize(String resourceLocationString, ILevelWrapper levelWrapper) throws IOException
|
||||
{
|
||||
if (resourceLocationString.equals(EMPTY_STRING))
|
||||
// we need the final string for the concurrent hash map later
|
||||
final String finalResourceStateString = resourceLocationString;
|
||||
|
||||
if (resourceLocationString.equals(EMPTY_BIOME_STRING))
|
||||
{
|
||||
LOGGER.warn("["+EMPTY_STRING+"] biome string deserialized. This may mean there was a file saving error or a biome saving error.");
|
||||
if (!emptyStringWarningLogged)
|
||||
{
|
||||
emptyStringWarningLogged = true;
|
||||
LOGGER.warn("[" + EMPTY_BIOME_STRING + "] biome string deserialized. This may mean the level was null when a save was attempted, a file saving error, or a biome saving error. Future errors will not be logged.");
|
||||
}
|
||||
return EMPTY_WRAPPER;
|
||||
}
|
||||
else if (resourceLocationString.trim().isEmpty() || resourceLocationString.equals(""))
|
||||
@@ -236,53 +275,96 @@ public class BiomeWrapper implements IBiomeWrapper
|
||||
return EMPTY_WRAPPER;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// parse the resource location
|
||||
int separatorIndex = resourceLocationString.indexOf(":");
|
||||
if (separatorIndex == -1)
|
||||
if (WRAPPER_BY_RESOURCE_LOCATION.containsKey(finalResourceStateString))
|
||||
{
|
||||
throw new IOException("Unable to parse resource location string: [" + resourceLocationString + "].");
|
||||
return WRAPPER_BY_RESOURCE_LOCATION.get(finalResourceStateString);
|
||||
}
|
||||
ResourceLocation resourceLocation = new ResourceLocation(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
|
||||
|
||||
|
||||
|
||||
// if no wrapper is found, default to the empty wrapper
|
||||
BiomeWrapper foundWrapper = EMPTY_WRAPPER;
|
||||
try
|
||||
{
|
||||
Level level = (Level)levelWrapper.getWrappedMcObject();
|
||||
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
|
||||
|
||||
boolean success;
|
||||
#if MC_1_16_5 || MC_1_17_1
|
||||
Biome biome = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).get(resourceLocation);
|
||||
success = (biome != null);
|
||||
#elif MC_1_18_2 || MC_1_19_2
|
||||
Biome unwrappedBiome = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).get(resourceLocation);
|
||||
success = (unwrappedBiome != null);
|
||||
Holder<Biome> biome = new Holder.Direct<>(unwrappedBiome);
|
||||
#else
|
||||
Biome unwrappedBiome = registryAccess.registryOrThrow(Registries.BIOME).get(resourceLocation);
|
||||
success = (unwrappedBiome != null);
|
||||
Holder<Biome> biome = new Holder.Direct<>(unwrappedBiome);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
if (!success)
|
||||
// parse the resource location
|
||||
int separatorIndex = resourceLocationString.indexOf(":");
|
||||
if (separatorIndex == -1)
|
||||
{
|
||||
if (!BrokenResourceLocationStrings.contains(resourceLocationString))
|
||||
{
|
||||
BrokenResourceLocationStrings.add(resourceLocationString);
|
||||
LOGGER.warn("Unable to deserialize biome from string: [" + resourceLocationString + "]");
|
||||
}
|
||||
return EMPTY_WRAPPER;
|
||||
throw new IOException("Unable to parse resource location string: [" + resourceLocationString + "].");
|
||||
}
|
||||
|
||||
return getBiomeWrapper(biome, levelWrapper);
|
||||
ResourceLocation resourceLocation;
|
||||
try
|
||||
{
|
||||
#if MC_VER < MC_1_21_1
|
||||
resourceLocation = new ResourceLocation(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
|
||||
#else
|
||||
resourceLocation = ResourceLocation.fromNamespaceAndPath(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
|
||||
#endif
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new IOException("No Resource Location found for the string: [" + resourceLocationString + "] Error: [" + e.getMessage() + "].");
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
Level level = (Level) levelWrapper.getWrappedMcObject();
|
||||
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
|
||||
|
||||
boolean success;
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
Biome biome = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).get(resourceLocation);
|
||||
success = (biome != null);
|
||||
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
|
||||
Biome unwrappedBiome = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).get(resourceLocation);
|
||||
success = (unwrappedBiome != null);
|
||||
Holder<Biome> biome = new Holder.Direct<>(unwrappedBiome);
|
||||
#elif MC_VER < MC_1_21_3
|
||||
Biome unwrappedBiome = registryAccess.registryOrThrow(Registries.BIOME).get(resourceLocation);
|
||||
success = (unwrappedBiome != null);
|
||||
Holder<Biome> biome = new Holder.Direct<>(unwrappedBiome);
|
||||
#else
|
||||
Holder<Biome> biome;
|
||||
Optional<Holder.Reference<Biome>> optionalBiomeHolder = registryAccess.lookupOrThrow(Registries.BIOME).get(resourceLocation);
|
||||
if (optionalBiomeHolder.isPresent())
|
||||
{
|
||||
Biome unwrappedBiome = optionalBiomeHolder.get().value();
|
||||
success = (unwrappedBiome != null);
|
||||
biome = new Holder.Direct<>(unwrappedBiome);
|
||||
}
|
||||
else
|
||||
{
|
||||
success = false;
|
||||
biome = null;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
if (!success)
|
||||
{
|
||||
if (!brokenResourceLocationStrings.contains(resourceLocationString))
|
||||
{
|
||||
brokenResourceLocationStrings.add(resourceLocationString);
|
||||
LOGGER.warn("Unable to deserialize biome from string: [" + resourceLocationString + "]");
|
||||
}
|
||||
return EMPTY_WRAPPER;
|
||||
}
|
||||
|
||||
|
||||
foundWrapper = (BiomeWrapper) getBiomeWrapper(biome, levelWrapper);
|
||||
return foundWrapper;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new IOException("Failed to deserialize the string [" + finalResourceStateString + "] into a BiomeWrapper: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
finally
|
||||
{
|
||||
throw new IOException("Failed to deserialize the string [" + resourceLocationString + "] into a BiomeWrapper: " + e.getMessage(), e);
|
||||
WRAPPER_BY_RESOURCE_LOCATION.putIfAbsent(finalResourceStateString, foundWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+440
-104
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -19,35 +19,46 @@
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.block;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.config.types.ConfigEntry;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.BlockTags;
|
||||
import net.minecraft.world.level.block.BeaconBeamBlock;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.SoundType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
#if MC_1_16_5 || MC_1_17_1
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.EmptyBlockGetter;
|
||||
#elif MC_1_18_2 || MC_1_19_2
|
||||
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.world.level.EmptyBlockGetter;
|
||||
#else
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.world.level.EmptyBlockGetter;
|
||||
import net.minecraft.core.Holder;
|
||||
#endif
|
||||
|
||||
public class BlockStateWrapper implements IBlockStateWrapper
|
||||
@@ -62,16 +73,18 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
public static final ConcurrentHashMap<BlockState, BlockStateWrapper> WRAPPER_BY_BLOCK_STATE = new ConcurrentHashMap<>();
|
||||
public static final ConcurrentHashMap<String, BlockStateWrapper> WRAPPER_BY_RESOURCE_LOCATION = new ConcurrentHashMap<>();
|
||||
|
||||
public static final String AIR_STRING = "AIR";
|
||||
public static final BlockStateWrapper AIR = new BlockStateWrapper(null, null);
|
||||
|
||||
// TODO: Make this changeable through the config
|
||||
public static final String[] RENDERER_IGNORED_BLOCKS_RESOURCE_LOCATIONS = { AIR_STRING, "minecraft:barrier", "minecraft:structure_void", "minecraft:light", "minecraft:tripwire" };
|
||||
public static final String DIRT_RESOURCE_LOCATION_STRING = "minecraft:dirt";
|
||||
|
||||
public static HashSet<IBlockStateWrapper> rendererIgnoredBlocks = null;
|
||||
public static HashSet<IBlockStateWrapper> rendererIgnoredCaveBlocks = null;
|
||||
|
||||
/** keep track of broken blocks so we don't log every time */
|
||||
private static final HashSet<ResourceLocation> BrokenResourceLocations = new HashSet<>();
|
||||
private static final HashSet<ResourceLocation> BROKEN_RESOURCE_LOCATIONS = new HashSet<>();
|
||||
|
||||
|
||||
|
||||
@@ -80,11 +93,20 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
public final BlockState blockState;
|
||||
/** technically final, but since it requires a method call to generate it can't be marked as such */
|
||||
private String serialString;
|
||||
private final int hashCode;
|
||||
/**
|
||||
* Cached opacity value, -1 if not populated. <br>
|
||||
* Should be between {@link IBlockStateWrapper#FULLY_OPAQUE} and {@link IBlockStateWrapper#FULLY_OPAQUE}
|
||||
* Should be between {@link LodUtil#BLOCK_FULLY_OPAQUE} and {@link LodUtil#BLOCK_FULLY_OPAQUE}
|
||||
*/
|
||||
private int opacity = -1;
|
||||
/** used by the Iris shader mod to determine how each LOD should be rendered */
|
||||
private byte blockMaterialId = 0;
|
||||
|
||||
private final boolean isBeaconBlock;
|
||||
private final boolean isBeaconBaseBlock;
|
||||
/** null if this block can't tint beacons */
|
||||
private final Color beaconTintColor;
|
||||
private final Color mapColor;
|
||||
|
||||
|
||||
|
||||
@@ -112,18 +134,93 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be faster than {@link BlockStateWrapper#fromBlockState(BlockState, ILevelWrapper)}
|
||||
* in cases where the same block state is expected to be referenced multiple times.
|
||||
*/
|
||||
public static BlockStateWrapper fromBlockState(BlockState blockState, ILevelWrapper levelWrapper, IBlockStateWrapper guess)
|
||||
{
|
||||
BlockState guessBlockState = (guess == null || guess.isAir()) ? null : (BlockState) guess.getWrappedMcObject();
|
||||
BlockState inputBlockState = (blockState == null || blockState.isAir()) ? null : blockState;
|
||||
|
||||
if (guess instanceof BlockStateWrapper
|
||||
&& guessBlockState == inputBlockState)
|
||||
{
|
||||
return (BlockStateWrapper) guess;
|
||||
}
|
||||
else
|
||||
{
|
||||
return fromBlockState(blockState, levelWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
private BlockStateWrapper(BlockState blockState, ILevelWrapper levelWrapper)
|
||||
{
|
||||
this.blockState = blockState;
|
||||
this.serialString = this.serialize(levelWrapper);
|
||||
LOGGER.trace("Created BlockStateWrapper ["+this.serialString+"] for ["+blockState+"]");
|
||||
this.hashCode = Objects.hash(this.serialString);
|
||||
this.blockMaterialId = this.calculateEDhApiBlockMaterialId().index;
|
||||
|
||||
// beacon blocks
|
||||
String lowercaseSerial = this.serialString.toLowerCase();
|
||||
boolean isBeaconBaseBlock = false;
|
||||
for (int i = 0; i < LodUtil.BEACON_BASE_BLOCK_NAME_LIST.size(); i++)
|
||||
{
|
||||
String baseBlockName = LodUtil.BEACON_BASE_BLOCK_NAME_LIST.get(i);
|
||||
if (lowercaseSerial.contains(baseBlockName))
|
||||
{
|
||||
isBeaconBaseBlock = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.isBeaconBaseBlock = isBeaconBaseBlock;
|
||||
this.isBeaconBlock = lowercaseSerial.contains("minecraft:beacon");
|
||||
|
||||
// beacon tint color
|
||||
Color beaconTintColor = null;
|
||||
if (this.blockState != null
|
||||
// beacon blocks also show up here, but since they block the beacon beam we don't want their color
|
||||
&& !this.isBeaconBlock)
|
||||
{
|
||||
Block block = this.blockState.getBlock();
|
||||
if (block instanceof BeaconBeamBlock)
|
||||
{
|
||||
int colorInt;
|
||||
#if MC_VER <= MC_1_19_4
|
||||
colorInt = ((BeaconBeamBlock) block).getColor().getMaterialColor().col;
|
||||
#else
|
||||
colorInt = ((BeaconBeamBlock) block).getColor().getMapColor().col;
|
||||
#endif
|
||||
|
||||
beaconTintColor = ColorUtil.toColorObjRGB(colorInt);
|
||||
}
|
||||
}
|
||||
this.beaconTintColor = beaconTintColor;
|
||||
|
||||
|
||||
int mcColor = 0;
|
||||
if (this.blockState != null)
|
||||
{
|
||||
#if MC_VER < MC_1_20_1
|
||||
mcColor = this.blockState.getMaterial().getColor().col;
|
||||
#else
|
||||
mcColor = this.blockState.getMapColor(EmptyBlockGetter.INSTANCE, BlockPos.ZERO).col;
|
||||
#endif
|
||||
this.mapColor = ColorUtil.toColorObjRGB(mcColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.mapColor = new Color(0,0,0,0);
|
||||
}
|
||||
|
||||
//LOGGER.trace("Created BlockStateWrapper ["+this.serialString+"] for ["+blockState+"] with material ID ["+this.EDhApiBlockMaterialId+"]");
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper methods //
|
||||
//================//
|
||||
//====================//
|
||||
// LodBuilder methods //
|
||||
//====================//
|
||||
|
||||
/**
|
||||
* Requires a {@link ILevelWrapper} since {@link BlockStateWrapper#deserialize(String,ILevelWrapper)} also requires one.
|
||||
@@ -137,37 +234,104 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
return rendererIgnoredBlocks;
|
||||
}
|
||||
|
||||
HashSet<String> baseIgnoredBlock = new HashSet<>();
|
||||
baseIgnoredBlock.add(AIR_STRING);
|
||||
rendererIgnoredBlocks = getBlockWrappers(Config.Client.Advanced.Graphics.Culling.ignoredRenderBlockCsv, baseIgnoredBlock, levelWrapper);
|
||||
return rendererIgnoredBlocks;
|
||||
}
|
||||
/**
|
||||
* Requires a {@link ILevelWrapper} since {@link BlockStateWrapper#deserialize(String,ILevelWrapper)} also requires one.
|
||||
* This way the method won't accidentally be called before the deserialization can be completed.
|
||||
*/
|
||||
public static HashSet<IBlockStateWrapper> getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper)
|
||||
{
|
||||
// use the cached version if possible
|
||||
if (rendererIgnoredCaveBlocks != null)
|
||||
{
|
||||
return rendererIgnoredCaveBlocks;
|
||||
}
|
||||
|
||||
HashSet<String> baseIgnoredBlock = new HashSet<>();
|
||||
baseIgnoredBlock.add(AIR_STRING);
|
||||
rendererIgnoredCaveBlocks = getBlockWrappers(Config.Client.Advanced.Graphics.Culling.ignoredRenderCaveBlockCsv, baseIgnoredBlock, levelWrapper);
|
||||
return rendererIgnoredCaveBlocks;
|
||||
}
|
||||
|
||||
public static void clearRendererIgnoredBlocks() { rendererIgnoredBlocks = null; }
|
||||
public static void clearRendererIgnoredCaveBlocks() { rendererIgnoredCaveBlocks = null; }
|
||||
|
||||
|
||||
|
||||
// lod builder helpers //
|
||||
|
||||
private static HashSet<IBlockStateWrapper> getBlockWrappers(ConfigEntry<String> config, HashSet<String> baseResourceLocations, ILevelWrapper levelWrapper)
|
||||
{
|
||||
// get the base blocks
|
||||
HashSet<String> blockStringList = new HashSet<>();
|
||||
if (baseResourceLocations != null)
|
||||
{
|
||||
blockStringList.addAll(baseResourceLocations);
|
||||
}
|
||||
|
||||
// get the config blocks
|
||||
String ignoreBlockCsv = config.get();
|
||||
if (ignoreBlockCsv != null)
|
||||
{
|
||||
blockStringList.addAll(Arrays.asList(ignoreBlockCsv.split(",")));
|
||||
}
|
||||
|
||||
return getBlockWrappers(blockStringList, levelWrapper);
|
||||
}
|
||||
private static HashSet<IBlockStateWrapper> getBlockWrappers(HashSet<String> blockResourceLocationSet, ILevelWrapper levelWrapper)
|
||||
{
|
||||
// deserialize each of the given resource locations
|
||||
HashSet<IBlockStateWrapper> blockStateWrappers = new HashSet<>();
|
||||
for (String blockResourceLocation : RENDERER_IGNORED_BLOCKS_RESOURCE_LOCATIONS)
|
||||
for (String blockResourceLocation : blockResourceLocationSet)
|
||||
{
|
||||
try
|
||||
{
|
||||
BlockStateWrapper DefaultBlockStateToIgnore = (BlockStateWrapper) deserialize(blockResourceLocation, levelWrapper);
|
||||
blockStateWrappers.add(DefaultBlockStateToIgnore);
|
||||
|
||||
if (DefaultBlockStateToIgnore == AIR)
|
||||
if (blockResourceLocation == null)
|
||||
{
|
||||
// shouldn't happen, but just in case
|
||||
continue;
|
||||
}
|
||||
String cleanedResourceLocation = blockResourceLocation.trim();
|
||||
if (cleanedResourceLocation.length() == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// add all possible blockstates (to account for light blocks with different light values and such)
|
||||
List<BlockState> blockStatesToIgnore = DefaultBlockStateToIgnore.blockState.getBlock().getStateDefinition().getPossibleStates();
|
||||
for (BlockState blockState : blockStatesToIgnore)
|
||||
|
||||
BlockStateWrapper defaultBlockStateToIgnore = (BlockStateWrapper) deserialize(cleanedResourceLocation, levelWrapper);
|
||||
blockStateWrappers.add(defaultBlockStateToIgnore);
|
||||
|
||||
if (defaultBlockStateToIgnore != AIR)
|
||||
{
|
||||
BlockStateWrapper newBlockToIgnore = BlockStateWrapper.fromBlockState(blockState, levelWrapper);
|
||||
blockStateWrappers.add(newBlockToIgnore);
|
||||
// add all possible blockstates (to account for light blocks with different light values and such)
|
||||
List<BlockState> blockStatesToIgnore = defaultBlockStateToIgnore.blockState.getBlock().getStateDefinition().getPossibleStates();
|
||||
for (BlockState blockState : blockStatesToIgnore)
|
||||
{
|
||||
BlockStateWrapper newBlockToIgnore = BlockStateWrapper.fromBlockState(blockState, levelWrapper);
|
||||
blockStateWrappers.add(newBlockToIgnore);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// air is a special case so it must be handled separately
|
||||
blockStateWrappers.add(AIR);
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOGGER.warn("Unable to deserialize rendererIgnoredBlock with the resource location: ["+blockResourceLocation+"]. Error: "+e.getMessage(), e);
|
||||
LOGGER.warn("Unable to deserialize block with the resource location: ["+blockResourceLocation+"]. Error: "+e.getMessage(), e);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOGGER.warn("Unexpected error deserializing block with the resource location: ["+blockResourceLocation+"]. Error: "+e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
rendererIgnoredBlocks = blockStateWrappers;
|
||||
return rendererIgnoredBlocks;
|
||||
return blockStateWrappers;
|
||||
}
|
||||
|
||||
|
||||
@@ -190,23 +354,27 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
int opacity;
|
||||
if (this.isAir())
|
||||
{
|
||||
opacity = FULLY_TRANSPARENT;
|
||||
opacity = LodUtil.BLOCK_FULLY_TRANSPARENT;
|
||||
}
|
||||
else if (this.isLiquid() && !this.blockState.canOcclude())
|
||||
{
|
||||
// probably not a waterlogged block (which should block light entirely)
|
||||
|
||||
// +1 to indicate that the block is translucent (in between transparent and opaque)
|
||||
opacity = FULLY_TRANSPARENT + 1;
|
||||
opacity = LodUtil.BLOCK_FULLY_TRANSPARENT + 1;
|
||||
}
|
||||
#if MC_VER < MC_1_21_3
|
||||
else if (this.blockState.propagatesSkylightDown(EmptyBlockGetter.INSTANCE, BlockPos.ZERO))
|
||||
#else
|
||||
else if (this.blockState.propagatesSkylightDown())
|
||||
#endif
|
||||
{
|
||||
opacity = FULLY_TRANSPARENT;
|
||||
opacity = LodUtil.BLOCK_FULLY_TRANSPARENT;
|
||||
}
|
||||
else
|
||||
{
|
||||
// default for all other blocks
|
||||
opacity = FULLY_OPAQUE;
|
||||
opacity = LodUtil.BLOCK_FULLY_OPAQUE;
|
||||
}
|
||||
|
||||
|
||||
@@ -239,7 +407,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() { return Objects.hash(this.getSerialString()); }
|
||||
public int hashCode() { return this.hashCode; }
|
||||
|
||||
|
||||
@Override
|
||||
@@ -252,7 +420,12 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
@Override
|
||||
public boolean isSolid()
|
||||
{
|
||||
#if PRE_MC_1_20_1
|
||||
if (this.isAir())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#if MC_VER < MC_1_20_1
|
||||
return this.blockState.getMaterial().isSolid();
|
||||
#else
|
||||
return !this.blockState.getCollisionShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO).isEmpty();
|
||||
@@ -267,13 +440,28 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
return false;
|
||||
}
|
||||
|
||||
#if PRE_MC_1_20_1
|
||||
#if MC_VER < MC_1_20_1
|
||||
return this.blockState.getMaterial().isLiquid() || !this.blockState.getFluidState().isEmpty();
|
||||
#else
|
||||
return !this.blockState.getFluidState().isEmpty();
|
||||
#endif
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBeaconBlock() { return this.isBeaconBlock; }
|
||||
@Override
|
||||
public boolean isBeaconBaseBlock() { return this.isBeaconBaseBlock; }
|
||||
@Override
|
||||
public boolean isBeaconTintBlock() { return this.beaconTintColor != null; }
|
||||
|
||||
@Override
|
||||
public Color getMapColor() { return this.mapColor; }
|
||||
@Override
|
||||
public Color getBeaconTintColor() { return this.beaconTintColor; }
|
||||
|
||||
@Override
|
||||
public byte getMaterialId() { return this.blockMaterialId; }
|
||||
|
||||
@Override
|
||||
public String toString() { return this.getSerialString(); }
|
||||
|
||||
@@ -293,18 +481,20 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
|
||||
|
||||
// older versions of MC have a static registry
|
||||
#if !(MC_1_16_5 || MC_1_17_1)
|
||||
#if MC_VER > MC_1_17_1
|
||||
Level level = (Level)levelWrapper.getWrappedMcObject();
|
||||
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
|
||||
#endif
|
||||
|
||||
ResourceLocation resourceLocation;
|
||||
#if MC_1_16_5 || MC_1_17_1
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
resourceLocation = Registry.BLOCK.getKey(this.blockState.getBlock());
|
||||
#elif MC_1_18_2 || MC_1_19_2
|
||||
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
|
||||
resourceLocation = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).getKey(this.blockState.getBlock());
|
||||
#else
|
||||
#elif MC_VER < MC_1_21_3
|
||||
resourceLocation = registryAccess.registryOrThrow(Registries.BLOCK).getKey(this.blockState.getBlock());
|
||||
#else
|
||||
resourceLocation = registryAccess.lookupOrThrow(Registries.BLOCK).getKey(this.blockState.getBlock());
|
||||
#endif
|
||||
|
||||
|
||||
@@ -325,103 +515,142 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
/** will only work if a level is currently loaded */
|
||||
public static IBlockStateWrapper deserialize(String resourceStateString, ILevelWrapper levelWrapper) throws IOException
|
||||
{
|
||||
if (resourceStateString.equals(AIR_STRING) || resourceStateString.equals("")) // the empty string shouldn't normally happen, but just in case
|
||||
// we need the final string for the concurrent hash map later
|
||||
final String finalResourceStateString = resourceStateString;
|
||||
|
||||
if (finalResourceStateString.equals(AIR_STRING) || finalResourceStateString.equals("")) // the empty string shouldn't normally happen, but just in case
|
||||
{
|
||||
return AIR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// try to parse out the BlockState
|
||||
String blockStatePropertiesString = null; // will be null if no properties were included
|
||||
int stateSeparatorIndex = resourceStateString.indexOf(STATE_STRING_SEPARATOR);
|
||||
if (stateSeparatorIndex != -1)
|
||||
// attempt to use the existing wrapper
|
||||
if (WRAPPER_BY_RESOURCE_LOCATION.containsKey(finalResourceStateString))
|
||||
{
|
||||
// blockstate properties found
|
||||
blockStatePropertiesString = resourceStateString.substring(stateSeparatorIndex + STATE_STRING_SEPARATOR.length());
|
||||
resourceStateString = resourceStateString.substring(0, stateSeparatorIndex);
|
||||
return WRAPPER_BY_RESOURCE_LOCATION.get(finalResourceStateString);
|
||||
}
|
||||
|
||||
// parse the resource location
|
||||
int resourceSeparatorIndex = resourceStateString.indexOf(RESOURCE_LOCATION_SEPARATOR);
|
||||
if (resourceSeparatorIndex == -1)
|
||||
{
|
||||
throw new IOException("Unable to parse Resource Location out of string: [" + resourceStateString + "].");
|
||||
}
|
||||
ResourceLocation resourceLocation = new ResourceLocation(resourceStateString.substring(0, resourceSeparatorIndex), resourceStateString.substring(resourceSeparatorIndex + 1));
|
||||
|
||||
|
||||
|
||||
// attempt to get the BlockState from all possible BlockStates
|
||||
// if no wrapper is found, default to air
|
||||
BlockStateWrapper foundWrapper = AIR;
|
||||
try
|
||||
{
|
||||
|
||||
#if !(MC_1_16_5 || MC_1_17_1)
|
||||
// use the given level if possible, otherwise try using the currently loaded one
|
||||
Level level = (levelWrapper != null ? (Level)levelWrapper.getWrappedMcObject() : null);
|
||||
level = (level == null ? Minecraft.getInstance().level : level);
|
||||
#endif
|
||||
|
||||
Block block;
|
||||
#if MC_1_16_5 || MC_1_17_1
|
||||
block = Registry.BLOCK.get(resourceLocation);
|
||||
#elif MC_1_18_2 || MC_1_19_2
|
||||
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
|
||||
block = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).get(resourceLocation);
|
||||
#else
|
||||
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
|
||||
block = registryAccess.registryOrThrow(Registries.BLOCK).get(resourceLocation);
|
||||
#endif
|
||||
|
||||
|
||||
if (block == null)
|
||||
// try to parse out the BlockState
|
||||
String blockStatePropertiesString = null; // will be null if no properties were included
|
||||
int stateSeparatorIndex = resourceStateString.indexOf(STATE_STRING_SEPARATOR);
|
||||
if (stateSeparatorIndex != -1)
|
||||
{
|
||||
// shouldn't normally happen, but here to make the compiler happy
|
||||
if (!BrokenResourceLocations.contains(resourceLocation))
|
||||
{
|
||||
BrokenResourceLocations.add(resourceLocation);
|
||||
LOGGER.warn("Unable to find BlockState with the resourceLocation [" + resourceLocation + "] and properties: [" + blockStatePropertiesString + "]. Air will be used instead, some data may be lost.");
|
||||
}
|
||||
return AIR;
|
||||
// blockstate properties found
|
||||
blockStatePropertiesString = resourceStateString.substring(stateSeparatorIndex + STATE_STRING_SEPARATOR.length());
|
||||
resourceStateString = resourceStateString.substring(0, stateSeparatorIndex);
|
||||
}
|
||||
|
||||
// parse the resource location
|
||||
int separatorIndex = resourceStateString.indexOf(RESOURCE_LOCATION_SEPARATOR);
|
||||
if (separatorIndex == -1)
|
||||
{
|
||||
throw new IOException("Unable to parse Resource Location out of string: [" + resourceStateString + "].");
|
||||
}
|
||||
|
||||
ResourceLocation resourceLocation;
|
||||
try
|
||||
{
|
||||
#if MC_VER < MC_1_21_1
|
||||
resourceLocation = new ResourceLocation(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1));
|
||||
#else
|
||||
resourceLocation = ResourceLocation.fromNamespaceAndPath(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1));
|
||||
#endif
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new IOException("No Resource Location found for the string: [" + resourceStateString + "] Error: [" + e.getMessage() + "].");
|
||||
}
|
||||
|
||||
|
||||
// attempt to find the blockstate from all possibilities
|
||||
BlockState foundState = null;
|
||||
if (blockStatePropertiesString != null)
|
||||
|
||||
// attempt to get the BlockState from all possible BlockStates
|
||||
try
|
||||
{
|
||||
List<BlockState> possibleStateList = block.getStateDefinition().getPossibleStates();
|
||||
for (BlockState possibleState : possibleStateList)
|
||||
|
||||
#if MC_VER > MC_1_17_1
|
||||
LodUtil.assertTrue(levelWrapper != null && levelWrapper.getWrappedMcObject() != null);
|
||||
Level level = (Level)levelWrapper.getWrappedMcObject();
|
||||
#endif
|
||||
|
||||
Block block;
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
block = Registry.BLOCK.get(resourceLocation);
|
||||
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
|
||||
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
|
||||
block = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).get(resourceLocation);
|
||||
#elif MC_VER < MC_1_21_3
|
||||
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
|
||||
block = registryAccess.registryOrThrow(Registries.BLOCK).get(resourceLocation);
|
||||
#else
|
||||
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
|
||||
Optional<Holder.Reference<Block>> optionalBlockHolder = registryAccess.lookupOrThrow(Registries.BLOCK).get(resourceLocation);
|
||||
block = optionalBlockHolder.isPresent() ? optionalBlockHolder.get().value() : null;
|
||||
#endif
|
||||
|
||||
|
||||
if (block == null)
|
||||
{
|
||||
String possibleStatePropertiesString = serializeBlockStateProperties(possibleState);
|
||||
if (possibleStatePropertiesString.equals(blockStatePropertiesString))
|
||||
// shouldn't normally happen, but here to make the compiler happy
|
||||
if (!BROKEN_RESOURCE_LOCATIONS.contains(resourceLocation))
|
||||
{
|
||||
foundState = possibleState;
|
||||
break;
|
||||
BROKEN_RESOURCE_LOCATIONS.add(resourceLocation);
|
||||
LOGGER.warn("Unable to find BlockState with the resourceLocation [" + resourceLocation + "] and properties: [" + blockStatePropertiesString + "]. Air will be used instead, some data may be lost.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// use the default if no state was found or given
|
||||
if (foundState == null)
|
||||
{
|
||||
return AIR;
|
||||
}
|
||||
|
||||
|
||||
// attempt to find the blockstate from all possibilities
|
||||
BlockState foundState = null;
|
||||
if (blockStatePropertiesString != null)
|
||||
{
|
||||
// we should have found a blockstate, but didn't
|
||||
if (!BrokenResourceLocations.contains(resourceLocation))
|
||||
List<BlockState> possibleStateList = block.getStateDefinition().getPossibleStates();
|
||||
for (BlockState possibleState : possibleStateList)
|
||||
{
|
||||
BrokenResourceLocations.add(resourceLocation);
|
||||
LOGGER.warn("Unable to find BlockState for Block [" + resourceLocation + "] with properties: [" + blockStatePropertiesString + "]. Using the default block state.");
|
||||
String possibleStatePropertiesString = serializeBlockStateProperties(possibleState);
|
||||
if (possibleStatePropertiesString.equals(blockStatePropertiesString))
|
||||
{
|
||||
foundState = possibleState;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foundState = block.defaultBlockState();
|
||||
// use the default if no state was found or given
|
||||
if (foundState == null)
|
||||
{
|
||||
if (blockStatePropertiesString != null)
|
||||
{
|
||||
// we should have found a blockstate, but didn't
|
||||
if (!BROKEN_RESOURCE_LOCATIONS.contains(resourceLocation))
|
||||
{
|
||||
BROKEN_RESOURCE_LOCATIONS.add(resourceLocation);
|
||||
LOGGER.warn("Unable to find BlockState for Block [" + resourceLocation + "] with properties: [" + blockStatePropertiesString + "]. Using the default block state.");
|
||||
}
|
||||
}
|
||||
|
||||
foundState = block.defaultBlockState();
|
||||
}
|
||||
|
||||
foundWrapper = new BlockStateWrapper(foundState, levelWrapper);
|
||||
return foundWrapper;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new IOException("Failed to deserialize the string [" + finalResourceStateString + "] into a BlockStateWrapper: " + e.getMessage(), e);
|
||||
}
|
||||
return new BlockStateWrapper(foundState, levelWrapper);
|
||||
}
|
||||
catch (Exception e)
|
||||
finally
|
||||
{
|
||||
throw new IOException("Failed to deserialize the string [" + resourceStateString + "] into a BlockStateWrapper: " + e.getMessage(), e);
|
||||
// put if absent in case two threads deserialize at the same time
|
||||
// unfortunately we can't put everything in a computeIfAbsent() since we also throw exceptions
|
||||
WRAPPER_BY_RESOURCE_LOCATION.putIfAbsent(finalResourceStateString, foundWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -457,4 +686,111 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// Iris methods //
|
||||
//==============//
|
||||
|
||||
private EDhApiBlockMaterial calculateEDhApiBlockMaterialId()
|
||||
{
|
||||
if (this.blockState == null)
|
||||
{
|
||||
return EDhApiBlockMaterial.AIR;
|
||||
}
|
||||
|
||||
|
||||
String serialString = this.getSerialString().toLowerCase();
|
||||
|
||||
if (this.blockState.is(BlockTags.LEAVES)
|
||||
|| serialString.contains("bamboo")
|
||||
|| serialString.contains("cactus")
|
||||
|| serialString.contains("chorus_flower")
|
||||
|| serialString.contains("mushroom")
|
||||
)
|
||||
{
|
||||
return EDhApiBlockMaterial.LEAVES;
|
||||
}
|
||||
else if (this.blockState.is(Blocks.LAVA))
|
||||
{
|
||||
return EDhApiBlockMaterial.LAVA;
|
||||
}
|
||||
else if (this.isLiquid() || this.blockState.is(Blocks.WATER))
|
||||
{
|
||||
return EDhApiBlockMaterial.WATER;
|
||||
}
|
||||
else if (this.blockState.getSoundType() == SoundType.WOOD
|
||||
|| serialString.contains("root")
|
||||
#if MC_VER >= MC_1_19_4
|
||||
|| this.blockState.getSoundType() == SoundType.CHERRY_WOOD
|
||||
#endif
|
||||
)
|
||||
{
|
||||
return EDhApiBlockMaterial.WOOD;
|
||||
}
|
||||
else if (this.blockState.getSoundType() == SoundType.METAL
|
||||
#if MC_VER >= MC_1_19_2
|
||||
|| this.blockState.getSoundType() == SoundType.COPPER
|
||||
#endif
|
||||
#if MC_VER >= MC_1_20_4
|
||||
|| this.blockState.getSoundType() == SoundType.COPPER_BULB
|
||||
|| this.blockState.getSoundType() == SoundType.COPPER_GRATE
|
||||
#endif
|
||||
)
|
||||
{
|
||||
return EDhApiBlockMaterial.METAL;
|
||||
}
|
||||
else if (serialString.contains("grass_block"))
|
||||
{
|
||||
return EDhApiBlockMaterial.GRASS;
|
||||
}
|
||||
else if (
|
||||
serialString.contains("dirt")
|
||||
|| serialString.contains("gravel")
|
||||
|| serialString.contains("mud")
|
||||
|| serialString.contains("podzol")
|
||||
|| serialString.contains("mycelium")
|
||||
)
|
||||
{
|
||||
return EDhApiBlockMaterial.DIRT;
|
||||
}
|
||||
#if MC_VER >= MC_1_17_1
|
||||
else if (this.blockState.getSoundType() == SoundType.DEEPSLATE
|
||||
|| this.blockState.getSoundType() == SoundType.DEEPSLATE_BRICKS
|
||||
|| this.blockState.getSoundType() == SoundType.DEEPSLATE_TILES
|
||||
|| this.blockState.getSoundType() == SoundType.POLISHED_DEEPSLATE
|
||||
|| serialString.contains("deepslate") )
|
||||
{
|
||||
return EDhApiBlockMaterial.DEEPSLATE;
|
||||
}
|
||||
#endif
|
||||
else if (this.serialString.contains("snow"))
|
||||
{
|
||||
return EDhApiBlockMaterial.SNOW;
|
||||
}
|
||||
else if (serialString.contains("sand"))
|
||||
{
|
||||
return EDhApiBlockMaterial.SAND;
|
||||
}
|
||||
else if (serialString.contains("terracotta"))
|
||||
{
|
||||
return EDhApiBlockMaterial.TERRACOTTA;
|
||||
}
|
||||
else if (this.blockState.is(BlockTags.BASE_STONE_NETHER))
|
||||
{
|
||||
return EDhApiBlockMaterial.NETHER_STONE;
|
||||
}
|
||||
else if (serialString.contains("stone")
|
||||
|| serialString.contains("ore"))
|
||||
{
|
||||
return EDhApiBlockMaterial.STONE;
|
||||
}
|
||||
else if (this.blockState.getLightEmission() > 0)
|
||||
{
|
||||
return EDhApiBlockMaterial.ILLUMINATED;
|
||||
}
|
||||
else
|
||||
{
|
||||
return EDhApiBlockMaterial.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+485
@@ -0,0 +1,485 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.block;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.FlowerBlock;
|
||||
import net.minecraft.world.level.block.LeavesBlock;
|
||||
import net.minecraft.world.level.block.RotatedPillarBlock;
|
||||
#if MC_VER >= MC_1_19_2
|
||||
import net.minecraft.util.RandomSource;
|
||||
#else
|
||||
import java.util.Random;
|
||||
#endif
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* This stores and calculates the colors
|
||||
* the given {@link BlockState} should have based
|
||||
* on the given {@link IClientLevelWrapper}.
|
||||
*
|
||||
* @see ColorUtil
|
||||
*/
|
||||
public class ClientBlockStateColorCache
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
private static final HashSet<BlockState> BLOCK_STATES_THAT_NEED_LEVEL = new HashSet<>();
|
||||
private static final HashSet<BlockState> BROKEN_BLOCK_STATES = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Methods using MC's "RandomSource" object aren't thread safe <br>
|
||||
* so we need to put locks around that logic. <br>
|
||||
* specifically:
|
||||
* <code>
|
||||
* getBlockModel(this.blockState).getQuads(this.blockState, direction, RANDOM)
|
||||
* </code>
|
||||
*/
|
||||
private static final ReentrantLock RESOLVE_LOCK = new ReentrantLock();
|
||||
|
||||
|
||||
/** This is the order each direction on a block is processed when attempting to get the texture/color */
|
||||
private static final Direction[] COLOR_RESOLUTION_DIRECTION_ORDER = { Direction.UP, Direction.NORTH, Direction.EAST, Direction.WEST, Direction.SOUTH, Direction.DOWN };
|
||||
|
||||
private static final int FLOWER_COLOR_SCALE = 5;
|
||||
|
||||
|
||||
|
||||
#if MC_VER < MC_1_19_2
|
||||
private static final Random RANDOM = new Random(0);
|
||||
#else
|
||||
/** Note: this object isn't thread safe and must be put in a lock */
|
||||
private static final RandomSource RANDOM = RandomSource.create();
|
||||
#endif
|
||||
|
||||
private final IClientLevelWrapper levelWrapper;
|
||||
private final BlockState blockState;
|
||||
private final LevelReader level;
|
||||
|
||||
private boolean isColorResolved = false;
|
||||
private int baseColor = 0;
|
||||
private boolean needShade = true;
|
||||
private boolean needPostTinting = false;
|
||||
private int tintIndex = 0;
|
||||
|
||||
|
||||
|
||||
//===========//
|
||||
// constants //
|
||||
//===========//
|
||||
|
||||
private static final int MIN_SRGB_BITS = 0x39000000; // 2^(-13)
|
||||
private static final int MAX_SRGB_BITS = 0x3f7fffff; // 1.0 - f32::EPSILON
|
||||
private static final float MIN_SRGB_BOUND = Float.intBitsToFloat(MIN_SRGB_BITS);
|
||||
private static final float MAX_SRGB_BOUND = Float.intBitsToFloat(MAX_SRGB_BITS);
|
||||
|
||||
private static final int[] linearToSrgbTable = new int[]
|
||||
{
|
||||
0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d,
|
||||
0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a,
|
||||
0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033,
|
||||
0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067,
|
||||
0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5,
|
||||
0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2,
|
||||
0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143,
|
||||
0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af,
|
||||
0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240,
|
||||
0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300,
|
||||
0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401,
|
||||
0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559,
|
||||
0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723,
|
||||
};
|
||||
|
||||
private static final float[] srgbToLinearTable = new float[]
|
||||
{
|
||||
0.0f, 0.000303527f, 0.000607054f, 0.00091058103f, 0.001214108f, 0.001517635f, 0.0018211621f, 0.002124689f,
|
||||
0.002428216f, 0.002731743f, 0.00303527f, 0.0033465356f, 0.003676507f, 0.004024717f, 0.004391442f,
|
||||
0.0047769533f, 0.005181517f, 0.0056053917f, 0.0060488326f, 0.006512091f, 0.00699541f, 0.0074990317f,
|
||||
0.008023192f, 0.008568125f, 0.009134057f, 0.009721218f, 0.010329823f, 0.010960094f, 0.011612245f,
|
||||
0.012286487f, 0.012983031f, 0.013702081f, 0.014443844f, 0.015208514f, 0.015996292f, 0.016807375f,
|
||||
0.017641952f, 0.018500218f, 0.019382361f, 0.020288562f, 0.02121901f, 0.022173883f, 0.023153365f,
|
||||
0.02415763f, 0.025186857f, 0.026241222f, 0.027320892f, 0.028426038f, 0.029556843f, 0.03071345f, 0.03189604f,
|
||||
0.033104774f, 0.03433981f, 0.035601325f, 0.036889452f, 0.038204376f, 0.039546248f, 0.04091521f, 0.042311423f,
|
||||
0.043735042f, 0.045186214f, 0.046665095f, 0.048171833f, 0.049706575f, 0.051269468f, 0.052860655f, 0.05448028f,
|
||||
0.056128494f, 0.057805434f, 0.05951124f, 0.06124607f, 0.06301003f, 0.06480328f, 0.06662595f, 0.06847818f,
|
||||
0.07036011f, 0.07227186f, 0.07421358f, 0.07618539f, 0.07818743f, 0.08021983f, 0.082282715f, 0.084376216f,
|
||||
0.086500466f, 0.088655606f, 0.09084173f, 0.09305898f, 0.095307484f, 0.09758736f, 0.09989874f, 0.10224175f,
|
||||
0.10461649f, 0.10702311f, 0.10946172f, 0.111932434f, 0.11443538f, 0.116970696f, 0.11953845f, 0.12213881f,
|
||||
0.12477186f, 0.12743773f, 0.13013652f, 0.13286836f, 0.13563336f, 0.13843165f, 0.14126332f, 0.1441285f,
|
||||
0.1470273f, 0.14995982f, 0.15292618f, 0.1559265f, 0.15896086f, 0.16202943f, 0.16513224f, 0.16826946f,
|
||||
0.17144115f, 0.17464745f, 0.17788847f, 0.1811643f, 0.18447503f, 0.1878208f, 0.19120172f, 0.19461787f,
|
||||
0.19806935f, 0.2015563f, 0.20507877f, 0.2086369f, 0.21223079f, 0.21586053f, 0.21952623f, 0.22322798f,
|
||||
0.22696589f, 0.23074007f, 0.23455065f, 0.23839766f, 0.2422812f, 0.2462014f, 0.25015837f, 0.25415218f,
|
||||
0.2581829f, 0.26225072f, 0.26635566f, 0.27049786f, 0.27467737f, 0.27889434f, 0.2831488f, 0.2874409f,
|
||||
0.2917707f, 0.29613832f, 0.30054384f, 0.30498737f, 0.30946895f, 0.31398875f, 0.31854683f, 0.32314324f,
|
||||
0.32777813f, 0.33245158f, 0.33716366f, 0.34191445f, 0.3467041f, 0.3515327f, 0.35640025f, 0.36130688f,
|
||||
0.3662527f, 0.37123778f, 0.37626222f, 0.3813261f, 0.38642952f, 0.39157256f, 0.3967553f, 0.40197787f,
|
||||
0.4072403f, 0.4125427f, 0.41788515f, 0.42326775f, 0.42869055f, 0.4341537f, 0.43965724f, 0.44520125f,
|
||||
0.45078585f, 0.45641106f, 0.46207705f, 0.46778384f, 0.47353154f, 0.47932023f, 0.48514998f, 0.4910209f,
|
||||
0.49693304f, 0.5028866f, 0.50888145f, 0.5149178f, 0.5209957f, 0.52711535f, 0.5332766f, 0.5394797f,
|
||||
0.5457247f, 0.5520116f, 0.5583406f, 0.5647117f, 0.57112503f, 0.57758063f, 0.5840786f, 0.590619f, 0.597202f,
|
||||
0.60382754f, 0.61049575f, 0.61720675f, 0.62396055f, 0.63075733f, 0.637597f, 0.6444799f, 0.6514058f,
|
||||
0.65837497f, 0.66538745f, 0.67244333f, 0.6795426f, 0.68668544f, 0.69387203f, 0.70110214f, 0.70837605f,
|
||||
0.7156938f, 0.72305536f, 0.730461f, 0.7379107f, 0.7454045f, 0.75294244f, 0.76052475f, 0.7681514f, 0.77582246f,
|
||||
0.78353804f, 0.79129815f, 0.79910296f, 0.8069525f, 0.8148468f, 0.822786f, 0.8307701f, 0.83879924f, 0.84687346f,
|
||||
0.8549928f, 0.8631574f, 0.87136734f, 0.8796226f, 0.8879232f, 0.89626956f, 0.90466136f, 0.913099f, 0.92158204f,
|
||||
0.93011117f, 0.9386859f, 0.9473069f, 0.9559735f, 0.9646866f, 0.9734455f, 0.98225087f, 0.9911022f, 1.0f
|
||||
};
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public ClientBlockStateColorCache(BlockState blockState, IClientLevelWrapper samplingLevel)
|
||||
{
|
||||
this.blockState = blockState;
|
||||
this.levelWrapper = samplingLevel;
|
||||
this.level = (LevelReader) samplingLevel.getWrappedMcObject();
|
||||
this.resolveColors();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//===================//
|
||||
// color calculation //
|
||||
//===================//
|
||||
|
||||
private void resolveColors()
|
||||
{
|
||||
if (this.isColorResolved)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// getQuads() isn't thread safe so we need to put this logic in a lock
|
||||
RESOLVE_LOCK.lock();
|
||||
|
||||
if (this.blockState.getFluidState().isEmpty())
|
||||
{
|
||||
// look for the first non-empty direction
|
||||
List<BakedQuad> quads = null;
|
||||
for (Direction direction : COLOR_RESOLUTION_DIRECTION_ORDER)
|
||||
{
|
||||
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
|
||||
getBlockModel(this.blockState).getQuads(this.blockState, direction, RANDOM);
|
||||
|
||||
if (quads != null && !quads.isEmpty()
|
||||
&& !(
|
||||
this.blockState.getBlock() instanceof RotatedPillarBlock
|
||||
&& direction == Direction.UP
|
||||
)
|
||||
)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (quads == null || quads.isEmpty())
|
||||
{
|
||||
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
|
||||
getBlockModel(this.blockState).getQuads(this.blockState, null, RANDOM);
|
||||
}
|
||||
|
||||
if (quads != null && !quads.isEmpty())
|
||||
{
|
||||
this.needPostTinting = quads.get(0).isTinted();
|
||||
this.needShade = quads.get(0).isShade();
|
||||
this.tintIndex = quads.get(0).getTintIndex();
|
||||
this.baseColor = calculateColorFromTexture(
|
||||
#if MC_VER < MC_1_17_1 quads.get(0).sprite,
|
||||
#else quads.get(0).getSprite(), #endif
|
||||
ColorMode.getColorMode(this.blockState.getBlock()));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Backup method.
|
||||
this.needPostTinting = false;
|
||||
this.needShade = false;
|
||||
this.tintIndex = 0;
|
||||
this.baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(this.blockState),
|
||||
ColorMode.getColorMode(this.blockState.getBlock()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Liquid Block
|
||||
this.needPostTinting = true;
|
||||
this.needShade = false;
|
||||
this.tintIndex = 0;
|
||||
this.baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(this.blockState),
|
||||
ColorMode.getColorMode(this.blockState.getBlock()));
|
||||
}
|
||||
|
||||
this.isColorResolved = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
RESOLVE_LOCK.unlock();
|
||||
}
|
||||
}
|
||||
//TODO: Perhaps make this not just use the first frame?
|
||||
private static int calculateColorFromTexture(TextureAtlasSprite texture, ColorMode colorMode)
|
||||
{
|
||||
int count = 0;
|
||||
int alpha = 0;
|
||||
double red = 0;
|
||||
double green = 0;
|
||||
double blue = 0;
|
||||
int tempColor;
|
||||
|
||||
// don't render Chiseled blocks.
|
||||
// Since ColorMode is set per block, you only need to check this once.
|
||||
if (colorMode != ColorMode.Chisel)
|
||||
{
|
||||
// textures normally use u and v instead of x and y
|
||||
for (int v = 0; v < getTextureHeight(texture); v++)
|
||||
{
|
||||
for (int u = 0; u < getTextureWidth(texture); u++)
|
||||
{
|
||||
//note: Minecraft color format is: 0xAA BB GG RR
|
||||
//________ DH mod color format is: 0xAA RR GG BB
|
||||
//OpenGL RGBA format native order: 0xRR GG BB AA
|
||||
//_ OpenGL RGBA format Java Order: 0xAA BB GG RR
|
||||
tempColor = TextureAtlasSpriteWrapper.getPixelRGBA(texture, 0, u, v);
|
||||
|
||||
int r = (tempColor & 0x000000FF);
|
||||
int g = (tempColor & 0x0000FF00) >>> 8;
|
||||
int b = (tempColor & 0x00FF0000) >>> 16;
|
||||
int a = (tempColor & 0xFF000000) >>> 24;
|
||||
int scale = 1;
|
||||
if (colorMode == ColorMode.Leaves)
|
||||
{
|
||||
//switch (//FIXME add config option)
|
||||
// case BLACK:
|
||||
// a = 255; //simulate black background of fast leaves
|
||||
// break;
|
||||
// case IGNORE:
|
||||
if (a == 0) {
|
||||
continue; //same long grass
|
||||
}
|
||||
else
|
||||
{
|
||||
a = 255; //just in case there are semi transparent pixels
|
||||
}
|
||||
// break;
|
||||
// case TRANSPARENT:
|
||||
// break; //do nothing, let it count towards transparency
|
||||
|
||||
}
|
||||
else if (a == 0 && colorMode != ColorMode.Glass)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (colorMode == ColorMode.Flower && (g + 25 < b || g + 25 < r))
|
||||
{
|
||||
scale = FLOWER_COLOR_SCALE;
|
||||
}
|
||||
count += scale;
|
||||
//apparently alpha is linear
|
||||
alpha += a * scale;
|
||||
//gamma correction is complicated
|
||||
red += srgbToLinearTable[r] * a * scale;
|
||||
green += srgbToLinearTable[g] * a * scale;
|
||||
blue += srgbToLinearTable[b] * a * scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
// this block is entirely transparent
|
||||
tempColor = ColorUtil.argbToInt(0, 255, 255, 255);
|
||||
}
|
||||
else
|
||||
{
|
||||
// determine the average color
|
||||
tempColor = ColorUtil.argbToInt(
|
||||
alpha / count,
|
||||
linearToSrgb((float) (red / (double) alpha)),
|
||||
linearToSrgb((float) (green / (double) alpha)),
|
||||
linearToSrgb((float) (blue / (double) alpha)));
|
||||
}
|
||||
|
||||
//check if not missing texture
|
||||
if (tempColor == ColorUtil.argbToInt(255, 182, 0, 182))
|
||||
{
|
||||
//make it not render at all
|
||||
tempColor = ColorUtil.argbToInt(0, 255, 255, 255);
|
||||
}
|
||||
return tempColor;
|
||||
}
|
||||
private static int getTextureWidth(TextureAtlasSprite texture)
|
||||
{
|
||||
#if MC_VER < MC_1_19_4
|
||||
return texture.getWidth();
|
||||
#else
|
||||
return texture.contents().width();
|
||||
#endif
|
||||
}
|
||||
private static int getTextureHeight(TextureAtlasSprite texture)
|
||||
{
|
||||
#if MC_VER < MC_1_19_4
|
||||
return texture.getHeight();
|
||||
#else
|
||||
return texture.contents().height();
|
||||
#endif
|
||||
}
|
||||
/**
|
||||
* This method was suggested by IMS from the Iris/Sodium team.
|
||||
* That's where the numbers and code are based.
|
||||
*/
|
||||
private static int linearToSrgb(float c)
|
||||
{
|
||||
if (!(c > MIN_SRGB_BOUND)) {
|
||||
c = MIN_SRGB_BOUND;
|
||||
}
|
||||
|
||||
if (c > MAX_SRGB_BOUND) {
|
||||
c = MAX_SRGB_BOUND;
|
||||
}
|
||||
int inputBits = Float.floatToRawIntBits(c);
|
||||
int entry = linearToSrgbTable[((inputBits - MIN_SRGB_BITS) >> 20)];
|
||||
|
||||
int bias = (entry >>> 16) << 9;
|
||||
int scale = entry & 0xffff;
|
||||
int t = (inputBits >>> 12) & 0xff;
|
||||
|
||||
return (bias + (scale * t)) >>> 16;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//===============//
|
||||
// public getter //
|
||||
//===============//
|
||||
|
||||
public int getColor(BiomeWrapper biome, DhBlockPos pos)
|
||||
{
|
||||
// only get the tint if the block needs to be tinted
|
||||
if (!this.needPostTinting)
|
||||
{
|
||||
return this.baseColor;
|
||||
}
|
||||
|
||||
// don't try tinting blocks that don't support our method of tint getting
|
||||
if (BROKEN_BLOCK_STATES.contains(this.blockState))
|
||||
{
|
||||
return this.baseColor;
|
||||
}
|
||||
|
||||
|
||||
// attempt to get the tint
|
||||
int tintColor = -1;
|
||||
try
|
||||
{
|
||||
// try to use the fast tint getter logic first
|
||||
if (!BLOCK_STATES_THAT_NEED_LEVEL.contains(this.blockState))
|
||||
{
|
||||
try
|
||||
{
|
||||
tintColor = Minecraft.getInstance().getBlockColors()
|
||||
.getColor(this.blockState, new TintWithoutLevelOverrider(biome, this.levelWrapper), McObjectConverter.Convert(pos), this.tintIndex);
|
||||
}
|
||||
catch (UnsupportedOperationException e)
|
||||
{
|
||||
// this exception generally occurs if the tint requires other blocks besides itself
|
||||
LOGGER.debug("Unable to use ["+TintWithoutLevelOverrider.class.getSimpleName()+"] to get the block tint for block: [" + this.blockState + "] and biome: [" + biome + "] at pos: " + pos + ". Error: [" + e.getMessage() + "]. Attempting to use backup method...", e);
|
||||
BLOCK_STATES_THAT_NEED_LEVEL.add(this.blockState);
|
||||
}
|
||||
}
|
||||
|
||||
// use the level logic only if requested
|
||||
if (BLOCK_STATES_THAT_NEED_LEVEL.contains(this.blockState))
|
||||
{
|
||||
// this logic can't be used all the time due to it breaking some blocks tinting
|
||||
// specifically oceans don't render correctly
|
||||
tintColor = Minecraft.getInstance().getBlockColors()
|
||||
.getColor(this.blockState, new TintGetterOverrideFast(this.level), McObjectConverter.Convert(pos), this.tintIndex);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// only display the error once per block/biome type to reduce log spam
|
||||
if (!BROKEN_BLOCK_STATES.contains(this.blockState))
|
||||
{
|
||||
LOGGER.warn("Failed to get block color for block: [" + this.blockState + "] and biome: [" + biome + "] at pos: " + pos + ". Error: ["+e.getMessage() + "]. Note: future errors for this block/biome will be ignored.", e);
|
||||
BROKEN_BLOCK_STATES.add(this.blockState);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (tintColor != -1)
|
||||
{
|
||||
return ColorUtil.multiplyARGBwithRGB(this.baseColor, tintColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
// unable to get the tinted color, use the base color instead
|
||||
return this.baseColor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper classes //
|
||||
//================//
|
||||
|
||||
enum ColorMode
|
||||
{
|
||||
Default,
|
||||
Flower,
|
||||
Leaves,
|
||||
Chisel,
|
||||
Glass;
|
||||
|
||||
static ColorMode getColorMode(Block block)
|
||||
{
|
||||
if (block instanceof LeavesBlock) return Leaves;
|
||||
if (block instanceof FlowerBlock) return Flower;
|
||||
if (block.toString().contains("glass")) return Glass;
|
||||
if (block.toString().equals("Block{chiselsandbits:chiseled}")) return Chisel;
|
||||
return Default;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
+27
-14
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -19,9 +19,15 @@
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.block;
|
||||
|
||||
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
|
||||
#if MC_VER < MC_1_17_1
|
||||
#elif MC_VER < MC_1_21_3
|
||||
#else
|
||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
||||
import net.minecraft.client.renderer.texture.SpriteContents;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* For wrapping/utilizing around TextureAtlasSprite
|
||||
*
|
||||
@@ -29,35 +35,42 @@ import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
*/
|
||||
public class TextureAtlasSpriteWrapper
|
||||
{
|
||||
|
||||
/**
|
||||
* This code is from Minecraft Forge
|
||||
* Which is licensed under the terms of GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* The code has been modified to use TextureAtlasSprite
|
||||
*/
|
||||
public static int getPixelRGBA(TextureAtlasSprite sprite, int frameIndex, int x, int y)
|
||||
{
|
||||
#if PRE_MC_1_17_1
|
||||
#if MC_VER < MC_1_17_1
|
||||
return sprite.mainImage[0].getPixelRGBA(
|
||||
x + sprite.framesX[frameIndex] * sprite.getWidth(),
|
||||
y + sprite.framesY[frameIndex] * sprite.getHeight());
|
||||
#elif PRE_MC_1_19_4
|
||||
#elif MC_VER < MC_1_19_4
|
||||
if (sprite.animatedTexture != null)
|
||||
{
|
||||
x += sprite.animatedTexture.getFrameX(frameIndex) * sprite.width;
|
||||
y += sprite.animatedTexture.getFrameY(frameIndex) * sprite.height;
|
||||
}
|
||||
return sprite.mainImage[0].getPixelRGBA(x, y);
|
||||
#else
|
||||
#elif MC_VER < MC_1_21_3
|
||||
if (sprite.contents().animatedTexture != null)
|
||||
{
|
||||
x += sprite.contents().animatedTexture.getFrameX(frameIndex) * sprite.contents().width();
|
||||
y += sprite.contents().animatedTexture.getFrameY(frameIndex) * sprite.contents().width();
|
||||
}
|
||||
return sprite.contents().originalImage.getPixelRGBA(x, y);
|
||||
#else
|
||||
|
||||
SpriteContents content = sprite.contents(); // don't close, otherwise MC will be corrupted and you won't be able to re-access the texture
|
||||
if (content.animatedTexture != null)
|
||||
{
|
||||
x += content.animatedTexture.getFrameX(frameIndex) * content.width();
|
||||
y += content.animatedTexture.getFrameY(frameIndex) * content.width();
|
||||
}
|
||||
|
||||
int abgr = content.originalImage.getPixel(x, y);
|
||||
// re-pack the color so we can access it normally
|
||||
int a = (abgr & 0xFF000000) >>> 24;
|
||||
int b = (abgr & 0x00FF0000) >>> 16;
|
||||
int g = (abgr & 0x0000FF00) >>> 8;
|
||||
int r = (abgr & 0x000000FF);
|
||||
return ColorUtil.argbToInt(a, r, g, b);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
+80
-129
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.block;
|
||||
|
||||
import com.seibel.distanthorizons.common.LodCommonMain;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.*;
|
||||
@@ -43,201 +42,153 @@ public class TintGetterOverrideFast implements BlockAndTintGetter
|
||||
{
|
||||
LevelReader parent;
|
||||
|
||||
public TintGetterOverrideFast(LevelReader parent)
|
||||
{
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public TintGetterOverrideFast(LevelReader parent) { this.parent = parent; }
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// methods //
|
||||
//=========//
|
||||
|
||||
private Biome _getBiome(BlockPos pos)
|
||||
{
|
||||
#if POST_MC_1_18_2
|
||||
return parent.getBiome(pos).value();
|
||||
#if MC_VER >= MC_1_18_2
|
||||
return this.parent.getBiome(pos).value();
|
||||
#else
|
||||
return parent.getBiome(pos);
|
||||
#endif
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver)
|
||||
{
|
||||
if (LodCommonMain.forgeMethodCaller != null)
|
||||
{
|
||||
return LodCommonMain.forgeMethodCaller.colorResolverGetColor(colorResolver, _getBiome(blockPos),
|
||||
blockPos.getX(), blockPos.getZ());
|
||||
}
|
||||
else
|
||||
{
|
||||
return colorResolver.getColor(_getBiome(blockPos), blockPos.getX(), blockPos.getZ());
|
||||
}
|
||||
}
|
||||
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver) { return colorResolver.getColor(this._getBiome(blockPos), blockPos.getX(), blockPos.getZ()); }
|
||||
|
||||
@Override
|
||||
public float getShade(Direction direction, boolean bl) { return this.parent.getShade(direction, bl); }
|
||||
|
||||
@Override
|
||||
public LevelLightEngine getLightEngine()
|
||||
{
|
||||
return parent.getLightEngine();
|
||||
}
|
||||
public LevelLightEngine getLightEngine() { return this.parent.getLightEngine(); }
|
||||
|
||||
@Override
|
||||
public int getBrightness(LightLayer lightLayer, BlockPos blockPos)
|
||||
{
|
||||
return parent.getBrightness(lightLayer, blockPos);
|
||||
}
|
||||
public int getBrightness(LightLayer lightLayer, BlockPos blockPos) { return this.parent.getBrightness(lightLayer, blockPos); }
|
||||
|
||||
@Override
|
||||
public int getRawBrightness(BlockPos blockPos, int i)
|
||||
{
|
||||
return parent.getRawBrightness(blockPos, i);
|
||||
}
|
||||
public int getRawBrightness(BlockPos blockPos, int i) { return this.parent.getRawBrightness(blockPos, i); }
|
||||
|
||||
@Override
|
||||
public boolean canSeeSky(BlockPos blockPos)
|
||||
{
|
||||
return parent.canSeeSky(blockPos);
|
||||
}
|
||||
public boolean canSeeSky(BlockPos blockPos) { return this.parent.canSeeSky(blockPos); }
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public BlockEntity getBlockEntity(BlockPos blockPos)
|
||||
{
|
||||
return parent.getBlockEntity(blockPos);
|
||||
}
|
||||
public BlockEntity getBlockEntity(BlockPos blockPos) { return this.parent.getBlockEntity(blockPos); }
|
||||
|
||||
|
||||
@Override
|
||||
public BlockState getBlockState(BlockPos blockPos)
|
||||
{
|
||||
return parent.getBlockState(blockPos);
|
||||
}
|
||||
public BlockState getBlockState(BlockPos blockPos) { return this.parent.getBlockState(blockPos); }
|
||||
|
||||
@Override
|
||||
public FluidState getFluidState(BlockPos blockPos)
|
||||
{
|
||||
return parent.getFluidState(blockPos);
|
||||
}
|
||||
public FluidState getFluidState(BlockPos blockPos) { return this.parent.getFluidState(blockPos); }
|
||||
|
||||
@Override
|
||||
public int getLightEmission(BlockPos blockPos)
|
||||
{
|
||||
return parent.getLightEmission(blockPos);
|
||||
}
|
||||
public int getLightEmission(BlockPos blockPos) { return this.parent.getLightEmission(blockPos); }
|
||||
|
||||
#if MC_VER < MC_1_21_3
|
||||
@Override
|
||||
public int getMaxLightLevel()
|
||||
{
|
||||
return parent.getMaxLightLevel();
|
||||
}
|
||||
public int getMaxLightLevel() { return parent.getMaxLightLevel(); }
|
||||
#else
|
||||
#endif
|
||||
|
||||
@Override
|
||||
public Stream<BlockState> getBlockStates(AABB aABB)
|
||||
{
|
||||
return parent.getBlockStates(aABB);
|
||||
}
|
||||
{ return this.parent.getBlockStates(aABB); }
|
||||
|
||||
@Override
|
||||
public BlockHitResult clip(ClipContext clipContext)
|
||||
{
|
||||
return parent.clip(clipContext);
|
||||
}
|
||||
{ return this.parent.clip(clipContext); }
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public BlockHitResult clipWithInteractionOverride(Vec3 vec3, Vec3 vec32, BlockPos blockPos, VoxelShape voxelShape, BlockState blockState)
|
||||
{
|
||||
return parent.clipWithInteractionOverride(vec3, vec32, blockPos, voxelShape, blockState);
|
||||
}
|
||||
{ return this.parent.clipWithInteractionOverride(vec3, vec32, blockPos, voxelShape, blockState); }
|
||||
|
||||
@Override
|
||||
public double getBlockFloorHeight(VoxelShape voxelShape, Supplier<VoxelShape> supplier)
|
||||
{
|
||||
return parent.getBlockFloorHeight(voxelShape, supplier);
|
||||
}
|
||||
{ return this.parent.getBlockFloorHeight(voxelShape, supplier); }
|
||||
|
||||
@Override
|
||||
public double getBlockFloorHeight(BlockPos blockPos)
|
||||
{
|
||||
return parent.getBlockFloorHeight(blockPos);
|
||||
}
|
||||
public double getBlockFloorHeight(BlockPos blockPos) { return this.parent.getBlockFloorHeight(blockPos); }
|
||||
|
||||
#if MC_VER < MC_1_21_3
|
||||
@Override
|
||||
public int getMaxBuildHeight()
|
||||
{
|
||||
return parent.getMaxBuildHeight();
|
||||
}
|
||||
public int getMaxBuildHeight() { return this.parent.getMaxBuildHeight(); }
|
||||
#else
|
||||
@Override
|
||||
public int getMaxY() { return this.parent.getMaxY(); }
|
||||
#endif
|
||||
|
||||
#if POST_MC_1_17_1
|
||||
|
||||
|
||||
//==============//
|
||||
// post MC 1.17 //
|
||||
//==============//
|
||||
|
||||
#if MC_VER >= MC_1_17_1
|
||||
@Override
|
||||
public <T extends BlockEntity> Optional<T> getBlockEntity(BlockPos blockPos, BlockEntityType<T> blockEntityType)
|
||||
{
|
||||
return parent.getBlockEntity(blockPos, blockEntityType);
|
||||
}
|
||||
{ return this.parent.getBlockEntity(blockPos, blockEntityType); }
|
||||
|
||||
@Override
|
||||
public BlockHitResult isBlockInLine(ClipBlockStateContext clipBlockStateContext)
|
||||
{
|
||||
return parent.isBlockInLine(clipBlockStateContext);
|
||||
}
|
||||
{ return this.parent.isBlockInLine(clipBlockStateContext); }
|
||||
|
||||
@Override
|
||||
public int getHeight()
|
||||
{
|
||||
return parent.getHeight();
|
||||
}
|
||||
public int getHeight() { return this.parent.getHeight(); }
|
||||
|
||||
#if MC_VER < MC_1_21_3
|
||||
@Override
|
||||
public int getMinBuildHeight() { return this.parent.getMinBuildHeight(); }
|
||||
#else
|
||||
@Override
|
||||
public int getMinY() { return this.parent.getMinY(); }
|
||||
#endif
|
||||
|
||||
@Override
|
||||
public int getMinBuildHeight()
|
||||
{
|
||||
return parent.getMinBuildHeight();
|
||||
}
|
||||
public int getSectionsCount() { return this.parent.getSectionsCount(); }
|
||||
|
||||
#if MC_VER < MC_1_21_3
|
||||
@Override
|
||||
public int getMinSection() { return this.parent.getMinSection(); }
|
||||
#else
|
||||
@Override
|
||||
public int getMinSectionY() { return BlockAndTintGetter.super.getMinSectionY(); }
|
||||
#endif
|
||||
|
||||
#if MC_VER < MC_1_21_3
|
||||
@Override
|
||||
public int getMaxSection() { return this.parent.getMaxSection(); }
|
||||
#else
|
||||
@Override
|
||||
public int getMaxSectionY() { return this.parent.getMaxSectionY(); }
|
||||
#endif
|
||||
|
||||
@Override
|
||||
public int getSectionsCount()
|
||||
{
|
||||
return parent.getSectionsCount();
|
||||
}
|
||||
public boolean isOutsideBuildHeight(BlockPos blockPos) { return this.parent.isOutsideBuildHeight(blockPos); }
|
||||
|
||||
@Override
|
||||
public int getMinSection()
|
||||
{
|
||||
return parent.getMinSection();
|
||||
}
|
||||
public boolean isOutsideBuildHeight(int i) { return this.parent.isOutsideBuildHeight(i); }
|
||||
|
||||
@Override
|
||||
public int getMaxSection()
|
||||
{
|
||||
return parent.getMaxSection();
|
||||
}
|
||||
public int getSectionIndex(int i) { return this.parent.getSectionIndex(i); }
|
||||
|
||||
@Override
|
||||
public boolean isOutsideBuildHeight(BlockPos blockPos)
|
||||
{
|
||||
return parent.isOutsideBuildHeight(blockPos);
|
||||
}
|
||||
public int getSectionIndexFromSectionY(int i) { return this.parent.getSectionIndexFromSectionY(i); }
|
||||
|
||||
@Override
|
||||
public boolean isOutsideBuildHeight(int i)
|
||||
{
|
||||
return parent.isOutsideBuildHeight(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSectionIndex(int i)
|
||||
{
|
||||
return parent.getSectionIndex(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSectionIndexFromSectionY(int i)
|
||||
{
|
||||
return parent.getSectionIndexFromSectionY(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSectionYFromSectionIndex(int i)
|
||||
{
|
||||
return parent.getSectionYFromSectionIndex(i);
|
||||
}
|
||||
public int getSectionYFromSectionIndex(int i) { return this.parent.getSectionYFromSectionIndex(i); }
|
||||
#endif
|
||||
}
|
||||
|
||||
+81
-136
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.block;
|
||||
|
||||
import com.seibel.distanthorizons.common.LodCommonMain;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Cursor3D;
|
||||
import net.minecraft.core.Direction;
|
||||
@@ -45,16 +45,28 @@ public class TintGetterOverrideSmooth implements BlockAndTintGetter
|
||||
LevelReader parent;
|
||||
public int smoothingRange;
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public TintGetterOverrideSmooth(LevelReader parent, int smoothingRange)
|
||||
{
|
||||
this.parent = parent;
|
||||
this.smoothingRange = smoothingRange;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// methods //
|
||||
//=========//
|
||||
|
||||
private Biome _getBiome(BlockPos pos)
|
||||
{
|
||||
#if POST_MC_1_18_2
|
||||
return parent.getBiome(pos).value();
|
||||
#if MC_VER >= MC_1_18_2
|
||||
return this.parent.getBiome(pos).value();
|
||||
#else
|
||||
return parent.getBiome(pos);
|
||||
#endif
|
||||
@@ -74,16 +86,7 @@ public class TintGetterOverrideSmooth implements BlockAndTintGetter
|
||||
while (cursor3D.advance())
|
||||
{
|
||||
mutableBlockPos.set(cursor3D.nextX(), cursor3D.nextY(), cursor3D.nextZ());
|
||||
int n;
|
||||
if (LodCommonMain.forgeMethodCaller != null)
|
||||
{
|
||||
n = LodCommonMain.forgeMethodCaller.colorResolverGetColor(colorResolver, _getBiome(mutableBlockPos),
|
||||
mutableBlockPos.getX(), mutableBlockPos.getZ());
|
||||
}
|
||||
else
|
||||
{
|
||||
n = colorResolver.getColor(_getBiome(mutableBlockPos), mutableBlockPos.getX(), mutableBlockPos.getZ());
|
||||
}
|
||||
int n = colorResolver.getColor(this._getBiome(mutableBlockPos), mutableBlockPos.getX(), mutableBlockPos.getZ());
|
||||
|
||||
k += (n & 0xFF0000) >> 16;
|
||||
l += (n & 0xFF00) >> 8;
|
||||
@@ -93,177 +96,119 @@ public class TintGetterOverrideSmooth implements BlockAndTintGetter
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver)
|
||||
{
|
||||
return calculateBlockTint(blockPos, colorResolver);
|
||||
}
|
||||
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver) { return this.calculateBlockTint(blockPos, colorResolver); }
|
||||
|
||||
@Override
|
||||
public float getShade(Direction direction, boolean bl) { return this.parent.getShade(direction, bl); }
|
||||
|
||||
@Override
|
||||
public LevelLightEngine getLightEngine()
|
||||
{
|
||||
return parent.getLightEngine();
|
||||
}
|
||||
public LevelLightEngine getLightEngine() { return this.parent.getLightEngine(); }
|
||||
|
||||
@Override
|
||||
public int getBrightness(LightLayer lightLayer, BlockPos blockPos)
|
||||
{
|
||||
return parent.getBrightness(lightLayer, blockPos);
|
||||
}
|
||||
public int getBrightness(LightLayer lightLayer, BlockPos blockPos) { return this.parent.getBrightness(lightLayer, blockPos); }
|
||||
|
||||
@Override
|
||||
public int getRawBrightness(BlockPos blockPos, int i)
|
||||
{
|
||||
return parent.getRawBrightness(blockPos, i);
|
||||
}
|
||||
public int getRawBrightness(BlockPos blockPos, int i) { return this.parent.getRawBrightness(blockPos, i); }
|
||||
|
||||
@Override
|
||||
public boolean canSeeSky(BlockPos blockPos)
|
||||
{
|
||||
return parent.canSeeSky(blockPos);
|
||||
}
|
||||
public boolean canSeeSky(BlockPos blockPos) { return this.parent.canSeeSky(blockPos); }
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public BlockEntity getBlockEntity(BlockPos blockPos)
|
||||
{
|
||||
return parent.getBlockEntity(blockPos);
|
||||
}
|
||||
public BlockEntity getBlockEntity(BlockPos blockPos) { return this.parent.getBlockEntity(blockPos); }
|
||||
|
||||
@Override
|
||||
public BlockState getBlockState(BlockPos blockPos)
|
||||
{
|
||||
return parent.getBlockState(blockPos);
|
||||
}
|
||||
public BlockState getBlockState(BlockPos blockPos) { return this.parent.getBlockState(blockPos); }
|
||||
|
||||
@Override
|
||||
public FluidState getFluidState(BlockPos blockPos)
|
||||
{
|
||||
return parent.getFluidState(blockPos);
|
||||
}
|
||||
public FluidState getFluidState(BlockPos blockPos) { return this.parent.getFluidState(blockPos); }
|
||||
|
||||
@Override
|
||||
public int getLightEmission(BlockPos blockPos)
|
||||
{
|
||||
return parent.getLightEmission(blockPos);
|
||||
}
|
||||
public int getLightEmission(BlockPos blockPos) { return this.parent.getLightEmission(blockPos); }
|
||||
|
||||
#if MC_VER < MC_1_21_3
|
||||
@Override
|
||||
public int getMaxLightLevel() { return this.parent.getMaxLightLevel(); }
|
||||
#else
|
||||
#endif
|
||||
|
||||
@Override
|
||||
public int getMaxLightLevel()
|
||||
{
|
||||
return parent.getMaxLightLevel();
|
||||
}
|
||||
public Stream<BlockState> getBlockStates(AABB aABB) { return this.parent.getBlockStates(aABB); }
|
||||
|
||||
@Override
|
||||
public Stream<BlockState> getBlockStates(AABB aABB)
|
||||
{
|
||||
return parent.getBlockStates(aABB);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockHitResult clip(ClipContext clipContext)
|
||||
{
|
||||
return parent.clip(clipContext);
|
||||
}
|
||||
public BlockHitResult clip(ClipContext clipContext) { return this.parent.clip(clipContext); }
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public BlockHitResult clipWithInteractionOverride(Vec3 vec3, Vec3 vec32, BlockPos blockPos, VoxelShape voxelShape, BlockState blockState)
|
||||
{
|
||||
return parent.clipWithInteractionOverride(vec3, vec32, blockPos, voxelShape, blockState);
|
||||
return this.parent.clipWithInteractionOverride(vec3, vec32, blockPos, voxelShape, blockState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getBlockFloorHeight(VoxelShape voxelShape, Supplier<VoxelShape> supplier)
|
||||
{
|
||||
return parent.getBlockFloorHeight(voxelShape, supplier);
|
||||
}
|
||||
public double getBlockFloorHeight(VoxelShape voxelShape, Supplier<VoxelShape> supplier) { return this.parent.getBlockFloorHeight(voxelShape, supplier); }
|
||||
|
||||
@Override
|
||||
public double getBlockFloorHeight(BlockPos blockPos)
|
||||
{
|
||||
return parent.getBlockFloorHeight(blockPos);
|
||||
}
|
||||
public double getBlockFloorHeight(BlockPos blockPos) { return this.parent.getBlockFloorHeight(blockPos); }
|
||||
|
||||
#if MC_VER < MC_1_21_3
|
||||
@Override
|
||||
public int getMaxBuildHeight() { return this.parent.getMaxBuildHeight(); }
|
||||
#else
|
||||
@Override
|
||||
public int getMaxY() { return this.parent.getMaxY(); }
|
||||
#endif
|
||||
|
||||
#if MC_VER >= MC_1_17_1
|
||||
@Override
|
||||
public <T extends BlockEntity> Optional<T> getBlockEntity(BlockPos blockPos, BlockEntityType<T> blockEntityType) { return this.parent.getBlockEntity(blockPos, blockEntityType); }
|
||||
|
||||
@Override
|
||||
public int getMaxBuildHeight()
|
||||
{
|
||||
return parent.getMaxBuildHeight();
|
||||
}
|
||||
|
||||
#if POST_MC_1_17_1
|
||||
@Override
|
||||
public <T extends BlockEntity> Optional<T> getBlockEntity(BlockPos blockPos, BlockEntityType<T> blockEntityType)
|
||||
{
|
||||
return parent.getBlockEntity(blockPos, blockEntityType);
|
||||
}
|
||||
public BlockHitResult isBlockInLine(ClipBlockStateContext clipBlockStateContext) { return this.parent.isBlockInLine(clipBlockStateContext); }
|
||||
|
||||
@Override
|
||||
public BlockHitResult isBlockInLine(ClipBlockStateContext clipBlockStateContext)
|
||||
{
|
||||
return parent.isBlockInLine(clipBlockStateContext);
|
||||
}
|
||||
public int getHeight() { return this.parent.getHeight(); }
|
||||
|
||||
#if MC_VER < MC_1_21_3
|
||||
@Override
|
||||
public int getMinBuildHeight() { return this.parent.getMinBuildHeight(); }
|
||||
#else
|
||||
@Override
|
||||
public int getMinY() { return this.parent.getMinY(); }
|
||||
#endif
|
||||
|
||||
@Override
|
||||
public int getHeight()
|
||||
{
|
||||
return parent.getHeight();
|
||||
}
|
||||
public int getSectionsCount() { return this.parent.getSectionsCount(); }
|
||||
|
||||
#if MC_VER < MC_1_21_3
|
||||
@Override
|
||||
public int getMinSection() { return this.parent.getMinSection(); }
|
||||
#else
|
||||
@Override
|
||||
public int getMinSectionY() { return BlockAndTintGetter.super.getMinSectionY(); }
|
||||
#endif
|
||||
|
||||
#if MC_VER < MC_1_21_3
|
||||
@Override
|
||||
public int getMaxSection() { return this.parent.getMaxSection(); }
|
||||
#else
|
||||
@Override
|
||||
public int getMaxSectionY() { return this.parent.getMaxSectionY(); }
|
||||
#endif
|
||||
|
||||
@Override
|
||||
public int getMinBuildHeight()
|
||||
{
|
||||
return parent.getMinBuildHeight();
|
||||
}
|
||||
public boolean isOutsideBuildHeight(BlockPos blockPos) { return this.parent.isOutsideBuildHeight(blockPos); }
|
||||
|
||||
@Override
|
||||
public int getSectionsCount()
|
||||
{
|
||||
return parent.getSectionsCount();
|
||||
}
|
||||
public boolean isOutsideBuildHeight(int i) { return this.parent.isOutsideBuildHeight(i); }
|
||||
|
||||
@Override
|
||||
public int getMinSection()
|
||||
{
|
||||
return parent.getMinSection();
|
||||
}
|
||||
public int getSectionIndex(int i) { return this.parent.getSectionIndex(i); }
|
||||
|
||||
@Override
|
||||
public int getMaxSection()
|
||||
{
|
||||
return parent.getMaxSection();
|
||||
}
|
||||
public int getSectionIndexFromSectionY(int i) { return this.parent.getSectionIndexFromSectionY(i); }
|
||||
|
||||
@Override
|
||||
public boolean isOutsideBuildHeight(BlockPos blockPos)
|
||||
{
|
||||
return parent.isOutsideBuildHeight(blockPos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOutsideBuildHeight(int i)
|
||||
{
|
||||
return parent.isOutsideBuildHeight(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSectionIndex(int i)
|
||||
{
|
||||
return parent.getSectionIndex(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSectionIndexFromSectionY(int i)
|
||||
{
|
||||
return parent.getSectionIndexFromSectionY(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSectionYFromSectionIndex(int i)
|
||||
{
|
||||
return parent.getSectionYFromSectionIndex(i);
|
||||
}
|
||||
public int getSectionYFromSectionIndex(int i) { return this.parent.getSectionYFromSectionIndex(i); }
|
||||
#endif
|
||||
}
|
||||
|
||||
+79
-21
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -19,82 +19,140 @@
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.block;
|
||||
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.*;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.Biomes;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.lighting.LevelLightEngine;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
#if POST_MC_1_18_2
|
||||
#if MC_VER >= MC_1_18_2
|
||||
import net.minecraft.core.Holder;
|
||||
#endif
|
||||
|
||||
public class TintWithoutLevelOverrider implements BlockAndTintGetter
|
||||
{
|
||||
final BiomeWrapper biome;
|
||||
/**
|
||||
* This will only ever be null if there was an issue with {@link IClientLevelWrapper#getPlainsBiomeWrapper()}
|
||||
* but {@link Nullable} is there just in case.
|
||||
*/
|
||||
@Nullable
|
||||
private final Biome biome;
|
||||
|
||||
public TintWithoutLevelOverrider(BiomeWrapper biome)
|
||||
|
||||
|
||||
public TintWithoutLevelOverrider(BiomeWrapper biomeWrapper, IClientLevelWrapper clientLevelWrapper)
|
||||
{
|
||||
this.biome = biome;
|
||||
// try to get the wrapped biome
|
||||
Biome unwrappedBiome = null;
|
||||
if (biomeWrapper.biome != null)
|
||||
{
|
||||
unwrappedBiome = unwrap(biomeWrapper.biome);
|
||||
}
|
||||
|
||||
if(unwrappedBiome == null)
|
||||
{
|
||||
// we are looking at the empty biome wrapper, try using plains as a backup
|
||||
BiomeWrapper plainsBiomeWrapper = ((BiomeWrapper) clientLevelWrapper.getPlainsBiomeWrapper());
|
||||
if (plainsBiomeWrapper != null)
|
||||
{
|
||||
unwrappedBiome = unwrap(plainsBiomeWrapper.biome);
|
||||
}
|
||||
}
|
||||
|
||||
this.biome = unwrappedBiome;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver)
|
||||
public int getBlockTint(@NotNull BlockPos blockPos, @NotNull ColorResolver colorResolver)
|
||||
{
|
||||
return colorResolver.getColor(_unwrap(biome.biome), blockPos.getX(), blockPos.getZ());
|
||||
if (this.biome != null)
|
||||
{
|
||||
return colorResolver.getColor(this.biome, blockPos.getX(), blockPos.getZ());
|
||||
}
|
||||
else
|
||||
{
|
||||
// hopefully unneeded debug color
|
||||
return ColorUtil.CYAN;
|
||||
}
|
||||
}
|
||||
private Biome _unwrap(#if POST_MC_1_18_2 Holder<Biome> #else Biome #endif biome)
|
||||
|
||||
private static Biome unwrap(#if MC_VER >= MC_1_18_2 Holder<Biome> #else Biome #endif biome)
|
||||
{
|
||||
#if POST_MC_1_18_2
|
||||
#if MC_VER >= MC_1_18_2
|
||||
return biome.value();
|
||||
#else
|
||||
return biome;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// unused methods //
|
||||
//================//
|
||||
|
||||
@Override
|
||||
public float getShade(Direction direction, boolean shade)
|
||||
public float getShade(@NotNull Direction direction, boolean shade)
|
||||
{
|
||||
throw new UnsupportedOperationException("ERROR: getShade() called on TintWithoutLevelOverrider. Object is for tinting only.");
|
||||
}
|
||||
@Override
|
||||
public LevelLightEngine getLightEngine()
|
||||
public @NotNull LevelLightEngine getLightEngine()
|
||||
{
|
||||
throw new UnsupportedOperationException("ERROR: getLightEngine() called on TintWithoutLevelOverrider. Object is for tinting only.");
|
||||
}
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockEntity getBlockEntity(BlockPos pos)
|
||||
public BlockEntity getBlockEntity(@NotNull BlockPos pos)
|
||||
{
|
||||
throw new UnsupportedOperationException("ERROR: getBlockEntity() called on TintWithoutLevelOverrider. Object is for tinting only.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlockState(BlockPos pos)
|
||||
public @NotNull BlockState getBlockState(@NotNull BlockPos pos)
|
||||
{
|
||||
throw new UnsupportedOperationException("ERROR: getBlockState() called on TintWithoutLevelOverrider. Object is for tinting only.");
|
||||
}
|
||||
@Override
|
||||
public FluidState getFluidState(BlockPos pos)
|
||||
public @NotNull FluidState getFluidState(@NotNull BlockPos pos)
|
||||
{
|
||||
throw new UnsupportedOperationException("ERROR: getFluidState() called on TintWithoutLevelOverrider. Object is for tinting only.");
|
||||
}
|
||||
|
||||
|
||||
#if MC_1_17_1 || POST_MC_1_18_2
|
||||
|
||||
//==============//
|
||||
// post MC 1.17 //
|
||||
//==============//
|
||||
|
||||
#if MC_VER >= MC_1_17_1
|
||||
|
||||
@Override
|
||||
public int getHeight()
|
||||
{
|
||||
throw new UnsupportedOperationException("ERROR: getHeight() called on TintWithoutLevelOverrider. Object is for tinting only.");
|
||||
}
|
||||
{ throw new UnsupportedOperationException("ERROR: getHeight() called on TintWithoutLevelOverrider. Object is for tinting only."); }
|
||||
|
||||
#if MC_VER < MC_1_21_3
|
||||
@Override
|
||||
public int getMinBuildHeight()
|
||||
{
|
||||
throw new UnsupportedOperationException("ERROR: getMinBuildHeight() called on TintWithoutLevelOverrider. Object is for tinting only.");
|
||||
}
|
||||
{ throw new UnsupportedOperationException("ERROR: getMinBuildHeight() called on TintWithoutLevelOverrider. Object is for tinting only."); }
|
||||
#else
|
||||
@Override
|
||||
public int getMinY()
|
||||
{ throw new UnsupportedOperationException("ERROR: getMinY() called on TintWithoutLevelOverrider. Object is for tinting only."); }
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
+39
-27
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -30,7 +30,7 @@ import net.minecraft.world.level.lighting.LevelLightEngine;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
#if POST_MC_1_18_2
|
||||
#if MC_VER >= MC_1_18_2
|
||||
import net.minecraft.core.Holder;
|
||||
#endif
|
||||
|
||||
@@ -39,25 +39,38 @@ public class TintWithoutLevelSmoothOverrider implements BlockAndTintGetter
|
||||
final BiomeWrapper biome;
|
||||
public int smoothingRange;
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public TintWithoutLevelSmoothOverrider(BiomeWrapper biome, int smoothingRange)
|
||||
{
|
||||
this.biome = biome;
|
||||
this.smoothingRange = smoothingRange;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// methods //
|
||||
//=========//
|
||||
|
||||
@Override
|
||||
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver)
|
||||
{
|
||||
return colorResolver.getColor(_unwrap(biome.biome), blockPos.getX(), blockPos.getZ());
|
||||
}
|
||||
private Biome _unwrap(#if POST_MC_1_18_2 Holder<Biome> #else Biome #endif biome)
|
||||
private Biome _unwrap(#if MC_VER >= MC_1_18_2 Holder<Biome> #else Biome #endif biome)
|
||||
{
|
||||
#if POST_MC_1_18_2
|
||||
#if MC_VER >= MC_1_18_2
|
||||
return biome.value();
|
||||
#else
|
||||
return biome;
|
||||
#endif
|
||||
}
|
||||
//
|
||||
|
||||
// public int calculateBlockTint(BlockPos blockPos, ColorResolver colorResolver)
|
||||
// {
|
||||
// int i = smoothingRange;
|
||||
@@ -89,44 +102,43 @@ public class TintWithoutLevelSmoothOverrider implements BlockAndTintGetter
|
||||
|
||||
@Override
|
||||
public float getShade(Direction direction, boolean shade)
|
||||
{
|
||||
throw new UnsupportedOperationException("ERROR: getShade() called on TintWithoutLevelOverrider. Object is for tinting only.");
|
||||
}
|
||||
{ throw new UnsupportedOperationException("ERROR: getShade() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
|
||||
@Override
|
||||
public LevelLightEngine getLightEngine()
|
||||
{
|
||||
throw new UnsupportedOperationException("ERROR: getLightEngine() called on TintWithoutLevelOverrider. Object is for tinting only.");
|
||||
}
|
||||
{ throw new UnsupportedOperationException("ERROR: getLightEngine() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockEntity getBlockEntity(BlockPos pos)
|
||||
{
|
||||
throw new UnsupportedOperationException("ERROR: getBlockEntity() called on TintWithoutLevelOverrider. Object is for tinting only.");
|
||||
}
|
||||
{ throw new UnsupportedOperationException("ERROR: getBlockEntity() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
|
||||
|
||||
@Override
|
||||
public BlockState getBlockState(BlockPos pos)
|
||||
{
|
||||
throw new UnsupportedOperationException("ERROR: getBlockState() called on TintWithoutLevelOverrider. Object is for tinting only.");
|
||||
}
|
||||
{ throw new UnsupportedOperationException("ERROR: getBlockState() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
|
||||
@Override
|
||||
public FluidState getFluidState(BlockPos pos)
|
||||
{
|
||||
throw new UnsupportedOperationException("ERROR: getFluidState() called on TintWithoutLevelOverrider. Object is for tinting only.");
|
||||
}
|
||||
{ throw new UnsupportedOperationException("ERROR: getFluidState() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
|
||||
|
||||
|
||||
#if MC_1_17_1 || POST_MC_1_18_2
|
||||
//==============//
|
||||
// post MC 1.17 //
|
||||
//==============//
|
||||
|
||||
#if MC_VER >= MC_1_17_1
|
||||
|
||||
@Override
|
||||
public int getHeight()
|
||||
{
|
||||
throw new UnsupportedOperationException("ERROR: getHeight() called on TintWithoutLevelOverrider. Object is for tinting only.");
|
||||
}
|
||||
{ throw new UnsupportedOperationException("ERROR: getHeight() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
|
||||
|
||||
#if MC_VER < MC_1_21_3
|
||||
@Override
|
||||
public int getMinBuildHeight()
|
||||
{
|
||||
throw new UnsupportedOperationException("ERROR: getMinBuildHeight() called on TintWithoutLevelOverrider. Object is for tinting only.");
|
||||
}
|
||||
{ throw new UnsupportedOperationException("ERROR: getMinBuildHeight() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
|
||||
#else
|
||||
@Override
|
||||
public int getMinY()
|
||||
{ throw new UnsupportedOperationException("ERROR: getMinY() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.block.cache;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class ClientBlockDetailMap
|
||||
{
|
||||
private final ConcurrentHashMap<BlockState, ClientBlockStateCache> blockCache = new ConcurrentHashMap<>();
|
||||
//private final ConcurrentHashMap<#if PRE_MC_1_18_2 Biome #else Holder<Biome> #endif, Biome> biomeMap = new ConcurrentHashMap<>();
|
||||
private final ClientLevelWrapper level;
|
||||
public ClientBlockDetailMap(ClientLevelWrapper level) { this.level = level; }
|
||||
|
||||
public ClientBlockStateCache getBlockStateData(BlockState state, DhBlockPos pos)
|
||||
{ //TODO: Allow a per pos unique setting
|
||||
return blockCache.computeIfAbsent(state, (s) -> new ClientBlockStateCache(s, level, pos));
|
||||
}
|
||||
|
||||
public void clear() { blockCache.clear(); }
|
||||
|
||||
public int getColor(BlockState state, BiomeWrapper biome, DhBlockPos pos)
|
||||
{
|
||||
return getBlockStateData(state, pos).getAndResolveFaceColor(biome, pos);
|
||||
}
|
||||
|
||||
}
|
||||
-307
@@ -1,307 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.block.cache;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.block.TextureAtlasSpriteWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.block.TintWithoutLevelOverrider;
|
||||
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
|
||||
import com.seibel.distanthorizons.common.wrappers.block.*;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.FlowerBlock;
|
||||
import net.minecraft.world.level.block.LeavesBlock;
|
||||
import net.minecraft.world.level.block.RotatedPillarBlock;
|
||||
#if POST_MC_1_19_2
|
||||
import net.minecraft.util.RandomSource;
|
||||
#else
|
||||
import java.util.Random;
|
||||
#endif
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @version 2022-9-16
|
||||
*/
|
||||
public class ClientBlockStateCache
|
||||
{
|
||||
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
private static final HashSet<BlockState> BLOCK_STATES_THAT_NEED_LEVEL = new HashSet<>();
|
||||
private static final HashSet<BlockState> BROKEN_BLOCK_STATES = new HashSet<>();
|
||||
|
||||
#if PRE_MC_1_19_2
|
||||
public static final Random random = new Random(0);
|
||||
#else
|
||||
public static final RandomSource random = RandomSource.create();
|
||||
#endif
|
||||
|
||||
public final BlockState blockState;
|
||||
public final LevelReader level;
|
||||
public final BlockPos pos;
|
||||
public ClientBlockStateCache(BlockState blockState, IClientLevelWrapper samplingLevel, DhBlockPos samplingPos)
|
||||
{
|
||||
this.blockState = blockState;
|
||||
level = (LevelReader) samplingLevel.getWrappedMcObject();
|
||||
pos = McObjectConverter.Convert(samplingPos);
|
||||
resolveColors();
|
||||
//LOGGER.info("ClientBlocKCache created for {}", blockState);
|
||||
}
|
||||
|
||||
boolean isColorResolved = false;
|
||||
int baseColor = 0; //TODO: Impl per-face color
|
||||
boolean needShade = true;
|
||||
boolean needPostTinting = false;
|
||||
int tintIndex = 0;
|
||||
|
||||
|
||||
public static final int FLOWER_COLOR_SCALE = 5;
|
||||
|
||||
enum ColorMode
|
||||
{
|
||||
Default,
|
||||
Flower,
|
||||
Leaves;
|
||||
static ColorMode getColorMode(Block b)
|
||||
{
|
||||
if (b instanceof LeavesBlock) return Leaves;
|
||||
if (b instanceof FlowerBlock) return Flower;
|
||||
return Default;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getWidth(TextureAtlasSprite texture)
|
||||
{
|
||||
#if PRE_MC_1_19_4
|
||||
return texture.getWidth();
|
||||
#else
|
||||
return texture.contents().width();
|
||||
#endif
|
||||
}
|
||||
|
||||
private static int getHeight(TextureAtlasSprite texture)
|
||||
{
|
||||
#if PRE_MC_1_19_4
|
||||
return texture.getHeight();
|
||||
#else
|
||||
return texture.contents().height();
|
||||
#endif
|
||||
}
|
||||
|
||||
//TODO: Perhaps make this not just use the first frame?
|
||||
private static int calculateColorFromTexture(TextureAtlasSprite texture, ColorMode colorMode)
|
||||
{
|
||||
int count = 0;
|
||||
double alpha = 0;
|
||||
double red = 0;
|
||||
double green = 0;
|
||||
double blue = 0;
|
||||
int tempColor;
|
||||
{
|
||||
// textures normally use u and v instead of x and y
|
||||
for (int u = 0; u < getWidth(texture); u++)
|
||||
{
|
||||
for (int v = 0; v < getHeight(texture); v++)
|
||||
{
|
||||
//note: Minecraft color format is: 0xAA BB GG RR
|
||||
//________ DH mod color format is: 0xAA RR GG BB
|
||||
//OpenGL RGBA format native order: 0xRR GG BB AA
|
||||
//_ OpenGL RGBA format Java Order: 0xAA BB GG RR
|
||||
tempColor = TextureAtlasSpriteWrapper.getPixelRGBA(texture, 0, u, v);
|
||||
|
||||
double r = ((tempColor & 0x000000FF)) / 255.;
|
||||
double g = ((tempColor & 0x0000FF00) >>> 8) / 255.;
|
||||
double b = ((tempColor & 0x00FF0000) >>> 16) / 255.;
|
||||
double a = ((tempColor & 0xFF000000) >>> 24) / 255.;
|
||||
int scale = 1;
|
||||
|
||||
if (colorMode == ColorMode.Leaves)
|
||||
{
|
||||
r *= a;
|
||||
g *= a;
|
||||
b *= a;
|
||||
a = 1.;
|
||||
}
|
||||
else if (a == 0.)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (colorMode == ColorMode.Flower && (g + 0.1 < b || g + 0.1 < r))
|
||||
{
|
||||
scale = FLOWER_COLOR_SCALE;
|
||||
}
|
||||
|
||||
count += scale;
|
||||
alpha += a * a * scale;
|
||||
red += r * r * scale;
|
||||
green += g * g * scale;
|
||||
blue += b * b * scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
// this block is entirely transparent
|
||||
tempColor = ColorUtil.rgbToInt(0, 255, 255, 255);
|
||||
else
|
||||
{
|
||||
// determine the average color
|
||||
tempColor = ColorUtil.rgbToInt(
|
||||
(int) (Math.sqrt(alpha / count) * 255.),
|
||||
(int) (Math.sqrt(red / count) * 255.),
|
||||
(int) (Math.sqrt(green / count) * 255.),
|
||||
(int) (Math.sqrt(blue / count) * 255.));
|
||||
}
|
||||
return tempColor;
|
||||
}
|
||||
private static final Direction[] DIRECTION_ORDER = {Direction.UP, Direction.NORTH, Direction.EAST, Direction.WEST, Direction.SOUTH, Direction.DOWN};
|
||||
|
||||
private void resolveColors()
|
||||
{
|
||||
if (isColorResolved) return;
|
||||
if (blockState.getFluidState().isEmpty())
|
||||
{
|
||||
List<BakedQuad> quads = null;
|
||||
for (Direction direction : DIRECTION_ORDER)
|
||||
{
|
||||
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
|
||||
getBlockModel(blockState).getQuads(blockState, direction, random);
|
||||
if (quads != null && !quads.isEmpty() &&
|
||||
!(blockState.getBlock() instanceof RotatedPillarBlock && direction == Direction.UP))
|
||||
break;
|
||||
} ;
|
||||
if (quads == null || quads.isEmpty())
|
||||
{
|
||||
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
|
||||
getBlockModel(blockState).getQuads(blockState, null, random);
|
||||
}
|
||||
if (quads != null && !quads.isEmpty())
|
||||
{
|
||||
needPostTinting = quads.get(0).isTinted();
|
||||
needShade = quads.get(0).isShade();
|
||||
tintIndex = quads.get(0).getTintIndex();
|
||||
baseColor = calculateColorFromTexture(
|
||||
#if PRE_MC_1_17_1 quads.get(0).sprite,
|
||||
#else quads.get(0).getSprite(), #endif
|
||||
ColorMode.getColorMode(blockState.getBlock()));
|
||||
}
|
||||
else
|
||||
{ // Backup method.
|
||||
needPostTinting = false;
|
||||
needShade = false;
|
||||
tintIndex = 0;
|
||||
baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(blockState),
|
||||
ColorMode.getColorMode(blockState.getBlock()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // Liquid Block
|
||||
needPostTinting = true;
|
||||
needShade = false;
|
||||
tintIndex = 0;
|
||||
baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(blockState),
|
||||
ColorMode.getColorMode(blockState.getBlock()));
|
||||
}
|
||||
isColorResolved = true;
|
||||
}
|
||||
|
||||
public int getAndResolveFaceColor(BiomeWrapper biome, DhBlockPos pos)
|
||||
{
|
||||
// FIXME: impl per-face colors
|
||||
|
||||
// only get the tint if the block needs to be tinted
|
||||
if (!this.needPostTinting)
|
||||
{
|
||||
return this.baseColor;
|
||||
}
|
||||
|
||||
// don't try tinting blocks that don't support our method of tint getting
|
||||
if (BROKEN_BLOCK_STATES.contains(this.blockState))
|
||||
{
|
||||
return this.baseColor;
|
||||
}
|
||||
|
||||
|
||||
// attempt to get the tint
|
||||
int tintColor = -1;
|
||||
try
|
||||
{
|
||||
// try to use the fast tint getter logic first
|
||||
if (!BLOCK_STATES_THAT_NEED_LEVEL.contains(this.blockState))
|
||||
{
|
||||
try
|
||||
{
|
||||
tintColor = Minecraft.getInstance().getBlockColors()
|
||||
.getColor(this.blockState, new TintWithoutLevelOverrider(biome), McObjectConverter.Convert(pos), this.tintIndex);
|
||||
}
|
||||
catch (UnsupportedOperationException e)
|
||||
{
|
||||
// this exception generally occurs if the tint requires other blocks besides itself
|
||||
LOGGER.debug("Unable to use ["+TintWithoutLevelOverrider.class.getSimpleName()+"] to get the block tint for block: [" + this.blockState + "] and biome: [" + biome + "] at pos: " + pos + ". Error: [" + e.getMessage() + "]. Attempting to use backup method...", e);
|
||||
BLOCK_STATES_THAT_NEED_LEVEL.add(this.blockState);
|
||||
}
|
||||
}
|
||||
|
||||
// use the level logic only if requested
|
||||
if (BLOCK_STATES_THAT_NEED_LEVEL.contains(this.blockState))
|
||||
{
|
||||
// this logic can't be used all the time due to it breaking some blocks tinting
|
||||
// specifically oceans don't render correctly
|
||||
tintColor = Minecraft.getInstance().getBlockColors()
|
||||
.getColor(this.blockState, new TintGetterOverrideFast(this.level), McObjectConverter.Convert(pos), this.tintIndex);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// only display the error once per block/biome type to reduce log spam
|
||||
if (!BROKEN_BLOCK_STATES.contains(this.blockState))
|
||||
{
|
||||
LOGGER.warn("Failed to get block color for block: [" + this.blockState + "] and biome: [" + biome + "] at pos: " + pos + ". Error: ["+e.getMessage() + "]. Note: future errors for this block/biome will be ignored.", e);
|
||||
BROKEN_BLOCK_STATES.add(this.blockState);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (tintColor != -1)
|
||||
{
|
||||
return ColorUtil.multiplyARGBwithRGB(this.baseColor, tintColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
// unable to get the tinted color, use the base color instead
|
||||
return this.baseColor;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.block.cache;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
|
||||
public class ServerBlockDetailMap
|
||||
{
|
||||
private final ConcurrentHashMap<BlockState, ServerBlockStateCache> blockCache = new ConcurrentHashMap<>();
|
||||
//private final ConcurrentHashMap<#if PRE_MC_1_18_2 Biome #else Holder<Biome> #endif, Biome> biomeMap = new ConcurrentHashMap<>();
|
||||
private final ServerLevelWrapper level;
|
||||
public ServerBlockDetailMap(ServerLevelWrapper level) { this.level = level; }
|
||||
|
||||
public ServerBlockStateCache getBlockStateData(BlockState state, DhBlockPos pos)
|
||||
{ //TODO: Allow a per pos unique setting
|
||||
return blockCache.computeIfAbsent(state, (s) -> new ServerBlockStateCache(s, level, new DhBlockPos(0, 0, 0)));
|
||||
}
|
||||
|
||||
public void clear() { blockCache.clear(); }
|
||||
|
||||
}
|
||||
-104
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.block.cache;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @version 2022-9-16
|
||||
*/
|
||||
public class ServerBlockStateCache
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
public final BlockState state;
|
||||
public final LevelReader level;
|
||||
public final BlockPos pos;
|
||||
|
||||
public ServerBlockStateCache(BlockState blockState, ILevelWrapper samplingLevel, DhBlockPos samplingPos)
|
||||
{
|
||||
state = blockState;
|
||||
level = (LevelReader) samplingLevel.getWrappedMcObject();
|
||||
pos = McObjectConverter.Convert(samplingPos);
|
||||
resolveShapes();
|
||||
//LOGGER.info("ServerBlockState created for {}", blockState);
|
||||
}
|
||||
|
||||
boolean noCollision = false;
|
||||
boolean[] occludeFaces = null;
|
||||
boolean[] fullFaces = null;
|
||||
boolean isShapeResolved = false;
|
||||
public void resolveShapes()
|
||||
{
|
||||
if (isShapeResolved) return;
|
||||
if (state.getFluidState().isEmpty())
|
||||
{
|
||||
noCollision = state.getCollisionShape(level, pos).isEmpty();
|
||||
occludeFaces = new boolean[6];
|
||||
if (state.canOcclude())
|
||||
{
|
||||
for (Direction dir : Direction.values())
|
||||
{
|
||||
// Note: isEmpty() isn't quite correct... best would be a isFull() or something...
|
||||
occludeFaces[McObjectConverter.Convert(dir).ordinal()]
|
||||
= !state.getFaceOcclusionShape(level, pos, dir).isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
VoxelShape voxelShape = state.getShape(level, pos);
|
||||
fullFaces = new boolean[6];
|
||||
if (!voxelShape.isEmpty())
|
||||
{
|
||||
for (Direction dir : Direction.values())
|
||||
{
|
||||
VoxelShape faceShape = voxelShape.getFaceShape(dir);
|
||||
AABB aabb = faceShape.bounds();
|
||||
boolean xFull = aabb.minX <= 0.01 && aabb.maxX >= 0.99;
|
||||
boolean yFull = aabb.minY <= 0.01 && aabb.maxY >= 0.99;
|
||||
boolean zFull = aabb.minZ <= 0.01 && aabb.maxZ >= 0.99;
|
||||
fullFaces[McObjectConverter.Convert(dir).ordinal()] =
|
||||
(xFull || dir.getAxis().equals(Direction.Axis.X))
|
||||
&& (yFull || dir.getAxis().equals(Direction.Axis.Y))
|
||||
&& (zFull || dir.getAxis().equals(Direction.Axis.Z));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // Liquid Block. Treat as full block
|
||||
occludeFaces = new boolean[6];
|
||||
Arrays.fill(occludeFaces, true);
|
||||
fullFaces = new boolean[6];
|
||||
Arrays.fill(fullFaces, true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-216
@@ -1,216 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.chunk;
|
||||
|
||||
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Compact, efficient storage for light levels.
|
||||
* all blocks only take up 4 bits in total,
|
||||
* and if a 16x16x16 area is detected to have the same light level in all positions,
|
||||
* then we store a single byte for that light level, instead of 2 kilobytes.
|
||||
*
|
||||
* @author Builderb0y
|
||||
*/
|
||||
public class ChunkLightStorage
|
||||
{
|
||||
/** the minimum Y level in the chunk which this storage is storing light levels for (inclusive). */
|
||||
public int minY;
|
||||
/** the maximum Y level in the chunk which this storage is storing light levels for (exclusive). */
|
||||
public int maxY;
|
||||
|
||||
/** the data stored in this storage, split up into 16x16x16 areas. */
|
||||
public LightSection[] lightSections;
|
||||
|
||||
|
||||
|
||||
public ChunkLightStorage(int minY, int maxY)
|
||||
{
|
||||
this.minY = minY;
|
||||
this.maxY = maxY;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int get(int x, int y, int z)
|
||||
{
|
||||
if (y < this.minY)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (y >= this.maxY)
|
||||
{
|
||||
return 15;
|
||||
}
|
||||
|
||||
if (this.lightSections != null)
|
||||
{
|
||||
LightSection lightSection = this.lightSections[BitShiftUtil.divideByPowerOfTwo(y - this.minY, 4)];
|
||||
if (lightSection != null)
|
||||
{
|
||||
return lightSection.get(x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void set(int x, int y, int z, int lightLevel)
|
||||
{
|
||||
if (y < this.minY || y >= this.maxY)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//populate array if it doesn't exist.
|
||||
if (this.lightSections == null)
|
||||
{
|
||||
this.lightSections = new LightSection[BitShiftUtil.divideByPowerOfTwo(this.maxY - this.minY, 4)];
|
||||
}
|
||||
|
||||
int index = (y - this.minY) >> 4;
|
||||
LightSection lightSection = this.lightSections[index];
|
||||
|
||||
//populate lightSection in array if it doesn't exist.
|
||||
if (lightSection == null)
|
||||
{
|
||||
lightSection = new LightSection(0);
|
||||
this.lightSections[index] = lightSection;
|
||||
}
|
||||
lightSection.set(x, y, z, lightLevel);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper classes //
|
||||
//================//
|
||||
|
||||
public static class LightSection
|
||||
{
|
||||
public byte constantValue;
|
||||
public long[] data;
|
||||
public short[] counts;
|
||||
|
||||
public LightSection(int initialValue)
|
||||
{
|
||||
this.constantValue = (byte) (initialValue);
|
||||
this.counts = new short[16];
|
||||
this.counts[initialValue] = 16 * 16 * 16;
|
||||
}
|
||||
|
||||
public int get(int x, int y, int z)
|
||||
{
|
||||
if (this.constantValue >= 0)
|
||||
{
|
||||
return this.constantValue;
|
||||
}
|
||||
|
||||
x &= 15;
|
||||
y &= 15;
|
||||
z &= 15;
|
||||
long bits = this.data[(z << 4) | x];
|
||||
return ((int) (bits >>> (y << 2))) & 15;
|
||||
}
|
||||
|
||||
public void set(int x, int y, int z, int lightLevel)
|
||||
{
|
||||
int oldLightLevel = -1;
|
||||
if (this.constantValue >= 0)
|
||||
{
|
||||
oldLightLevel = this.constantValue;
|
||||
|
||||
//if the light level didn't change, then there's nothing to do.
|
||||
if (oldLightLevel == lightLevel) return;
|
||||
|
||||
//if we are a constant value and need to change something,
|
||||
//then that means we need to convert to a non-constant value.
|
||||
this.data = DataRecycler.get();
|
||||
|
||||
//repeat oldLightLevel 16 times as a bit pattern.
|
||||
long payload = oldLightLevel;
|
||||
payload |= payload << 4;
|
||||
payload |= payload << 8;
|
||||
payload |= payload << 16;
|
||||
payload |= payload << 32;
|
||||
|
||||
//fill our data with our constant value.
|
||||
Arrays.fill(this.data, payload);
|
||||
|
||||
//we are no longer a constant value.
|
||||
this.constantValue = -1;
|
||||
}
|
||||
|
||||
x &= 15;
|
||||
y &= 15;
|
||||
z &= 15;
|
||||
int index = (z << 4) | x;
|
||||
long bits = this.data[index];
|
||||
//if we weren't a constant value before, now's the time to initialize oldLightLevel.
|
||||
if (oldLightLevel < 0)
|
||||
{
|
||||
oldLightLevel = ((int) (bits >>> (y << 2))) & 15;
|
||||
}
|
||||
//clear the 4 bits that correspond to the light level at x, y, z...
|
||||
bits &= ~(15L << (y << 2));
|
||||
//...and then re-populate those bits with the new light level.
|
||||
bits |= ((long) (lightLevel)) << (y << 2);
|
||||
//store the updated bits in our data.
|
||||
this.data[index] = bits;
|
||||
|
||||
//we have one less of the old light level...
|
||||
this.counts[oldLightLevel]--;
|
||||
//...and one more of the new level.
|
||||
//if the number associated with the new level is now 4096 (AKA 16 ^ 3),
|
||||
//then this implies every position in this section has the same light level,
|
||||
//and therefore we can convert back to a constant value.
|
||||
if (++this.counts[lightLevel] == 4096)
|
||||
{
|
||||
this.constantValue = (byte) (lightLevel);
|
||||
DataRecycler.reclaim(this.data);
|
||||
this.data = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class DataRecycler
|
||||
{
|
||||
private static final ArrayList<long[]> recycled = new ArrayList<>(256);
|
||||
|
||||
static synchronized long[] get()
|
||||
{
|
||||
if (recycled.isEmpty())
|
||||
{
|
||||
return new long[256];
|
||||
}
|
||||
else
|
||||
{
|
||||
return recycled.remove(recycled.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
static synchronized void reclaim(long[] data) { if (recycled.size() < 256) recycled.add(data); }
|
||||
}
|
||||
|
||||
}
|
||||
+396
-319
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -16,102 +16,91 @@
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.chunk;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
|
||||
import com.seibel.distanthorizons.common.wrappers.misc.MutableBlockPosWrapper;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IMutableBlockPosWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||
import net.minecraft.client.multiplayer.ClientChunkCache;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
import net.minecraft.world.level.LightLayer;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
#if POST_MC_1_17_1
|
||||
#if MC_VER >= MC_1_17_1
|
||||
import net.minecraft.core.QuartPos;
|
||||
#endif
|
||||
|
||||
#if MC_1_16_5
|
||||
#if MC_VER == MC_1_16_5
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
#endif
|
||||
|
||||
#if MC_1_17_1
|
||||
#if MC_VER == MC_1_17_1
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
#endif
|
||||
|
||||
#if MC_1_18_2
|
||||
#if MC_VER == MC_1_18_2
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
#endif
|
||||
|
||||
#if MC_1_19_2 || MC_1_19_4
|
||||
#if MC_VER == MC_1_19_2 || MC_VER == MC_1_19_4
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
#endif
|
||||
|
||||
#if POST_MC_1_20_1
|
||||
#if MC_VER >= MC_1_20_1
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
import net.minecraft.world.level.lighting.LevelLightEngine;
|
||||
import net.minecraft.core.SectionPos;
|
||||
#endif
|
||||
|
||||
#if MC_VER <= MC_1_20_4
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
#else
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
#endif
|
||||
|
||||
|
||||
public class ChunkWrapper implements IChunkWrapper
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
/** useful for debugging, but can slow down chunk operations quite a bit due to being called every time. */
|
||||
private static final boolean RUN_RELATIVE_POS_INDEX_VALIDATION = ModInfo.IS_DEV_BUILD;
|
||||
|
||||
/** can be used for interactions with the underlying chunk where creating new BlockPos objects could cause issues for the garbage collector. */
|
||||
private static final ThreadLocal<BlockPos.MutableBlockPos> MUTABLE_BLOCK_POS_REF = ThreadLocal.withInitial(() -> new BlockPos.MutableBlockPos());
|
||||
private static final ThreadLocal<MutableBlockPosWrapper> MUTABLE_BLOCK_POS_WRAPPER_REF = ThreadLocal.withInitial(() -> new MutableBlockPosWrapper());
|
||||
|
||||
|
||||
private final ChunkAccess chunk;
|
||||
private final DhChunkPos chunkPos;
|
||||
private final LevelReader lightSource;
|
||||
private final ILevelWrapper wrappedLevel;
|
||||
|
||||
private boolean isDhLightCorrect = false;
|
||||
/** only used when connected to a dedicated server */
|
||||
private boolean isMcClientLightingCorrect = false;
|
||||
private boolean isDhBlockLightCorrect = false;
|
||||
private boolean isDhSkyLightCorrect = false;
|
||||
|
||||
private ChunkLightStorage blockLightStorage;
|
||||
private ChunkLightStorage skyLightStorage;
|
||||
|
||||
private ArrayList<DhBlockPos> blockLightPosList = null;
|
||||
|
||||
private boolean useDhLighting;
|
||||
private int minNonEmptyHeight = Integer.MIN_VALUE;
|
||||
private int maxNonEmptyHeight = Integer.MAX_VALUE;
|
||||
|
||||
/**
|
||||
* Due to vanilla `isClientLightReady()` not being designed for use by a non-render thread, it may return 'true'
|
||||
* before the light engine has ticked, (right after all light changes is marked by the engine to be processed).
|
||||
* To fix this, on client-only mode, we mixin-redirect the `isClientLightReady()` so that after the call, it will
|
||||
* trigger a synchronous update of this flag here on all chunks that are wrapped. <br><br>
|
||||
*
|
||||
* Note: Using a static weak hash map to store the chunks that need to be updated, as instance of chunk wrapper
|
||||
* can be duplicated, with same chunk instance. And the data stored here are all temporary, and thus will not be
|
||||
* visible when a chunk is re-wrapped later. <br>
|
||||
* (Also, thread safety done via a reader writer lock)
|
||||
*/
|
||||
private static final ConcurrentLinkedQueue<ChunkWrapper> chunksNeedingClientLightUpdating = new ConcurrentLinkedQueue<>();
|
||||
/** will be null if we are using MC heightmaps */
|
||||
private final int[][] solidHeightMap;
|
||||
/** will be null if we are using MC heightmaps */
|
||||
private final int[][] lightBlockingHeightMap;
|
||||
|
||||
|
||||
|
||||
@@ -119,54 +108,82 @@ public class ChunkWrapper implements IChunkWrapper
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public ChunkWrapper(ChunkAccess chunk, LevelReader lightSource, ILevelWrapper wrappedLevel)
|
||||
public ChunkWrapper(ChunkAccess chunk, ILevelWrapper wrappedLevel)
|
||||
{
|
||||
this.chunk = chunk;
|
||||
this.lightSource = lightSource;
|
||||
this.wrappedLevel = wrappedLevel;
|
||||
this.chunkPos = new DhChunkPos(chunk.getPos().x, chunk.getPos().z);
|
||||
|
||||
// TODO is this the best way to differentiate between when we are generating chunks and when MC gave us a chunk?
|
||||
boolean isDhGeneratedChunk = (this.lightSource.getClass() == DhLitWorldGenRegion.class);
|
||||
// MC loaded chunks should get their lighting from MC, DH generated chunks should get their lighting from DH
|
||||
this.useDhLighting = isDhGeneratedChunk;
|
||||
// use DH heightmaps if requested
|
||||
if (Config.Common.LodBuilding.recalculateChunkHeightmaps.get())
|
||||
{
|
||||
this.solidHeightMap = new int[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH];
|
||||
this.lightBlockingHeightMap = new int[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH];
|
||||
|
||||
// FIXME +1 is to handle the fact that LodDataBuilder adds +1 to all block lighting calculations, also done in the relative position validator
|
||||
|
||||
chunksNeedingClientLightUpdating.add(this);
|
||||
this.recalculateDhHeightMapsIfNeeded();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.solidHeightMap = null;
|
||||
this.lightBlockingHeightMap = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// methods //
|
||||
// getters //
|
||||
//=========//
|
||||
|
||||
@Override
|
||||
public int getHeight()
|
||||
public int getHeight() { return getHeight(this.chunk); }
|
||||
public static int getHeight(ChunkAccess chunk)
|
||||
{
|
||||
#if PRE_MC_1_17_1
|
||||
#if MC_VER < MC_1_17_1
|
||||
return 255;
|
||||
#else
|
||||
return this.chunk.getHeight();
|
||||
return chunk.getHeight();
|
||||
#endif
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinBuildHeight()
|
||||
public int getInclusiveMinBuildHeight() { return getInclusiveMinBuildHeight(this.chunk); }
|
||||
public static int getInclusiveMinBuildHeight(ChunkAccess chunk)
|
||||
{
|
||||
#if PRE_MC_1_17_1
|
||||
#if MC_VER < MC_1_17_1
|
||||
return 0;
|
||||
#elif MC_VER < MC_1_21_3
|
||||
return chunk.getMinBuildHeight();
|
||||
#else
|
||||
return this.chunk.getMinBuildHeight();
|
||||
return chunk.getMinY();
|
||||
#endif
|
||||
}
|
||||
@Override
|
||||
public int getMaxBuildHeight() { return this.chunk.getMaxBuildHeight(); }
|
||||
|
||||
@Override
|
||||
public int getMinFilledHeight()
|
||||
public int getExclusiveMaxBuildHeight() { return getExclusiveMaxBuildHeight(this.chunk); }
|
||||
public static int getExclusiveMaxBuildHeight(ChunkAccess chunk)
|
||||
{
|
||||
#if MC_VER < MC_1_21_3
|
||||
return chunk.getMaxBuildHeight();
|
||||
#else
|
||||
// +1 since Minecraft made the max value inclusive
|
||||
return chunk.getMaxY() + 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinNonEmptyHeight()
|
||||
{
|
||||
if (this.minNonEmptyHeight != Integer.MIN_VALUE)
|
||||
{
|
||||
return this.minNonEmptyHeight;
|
||||
}
|
||||
|
||||
|
||||
// default if every section is empty or missing
|
||||
this.minNonEmptyHeight = this.getInclusiveMinBuildHeight();
|
||||
|
||||
// determine the lowest empty section (bottom up)
|
||||
LevelChunkSection[] sections = this.chunk.getSections();
|
||||
for (int index = 0; index < sections.length; index++)
|
||||
{
|
||||
@@ -175,50 +192,160 @@ public class ChunkWrapper implements IChunkWrapper
|
||||
continue;
|
||||
}
|
||||
|
||||
#if MC_1_16_5
|
||||
if (!sections[index].isEmpty())
|
||||
if (!isChunkSectionEmpty(sections[index]))
|
||||
{
|
||||
// convert from an index to a block coordinate
|
||||
return this.chunk.getSections()[index].bottomBlockY() * 16;
|
||||
this.minNonEmptyHeight = this.getChunkSectionMinHeight(index);
|
||||
break;
|
||||
}
|
||||
#elif MC_1_17_1
|
||||
if (!sections[index].isEmpty())
|
||||
{
|
||||
// convert from an index to a block coordinate
|
||||
return this.chunk.getSections()[index].bottomBlockY() * 16;
|
||||
}
|
||||
#else
|
||||
if (!sections[index].hasOnlyAir())
|
||||
{
|
||||
// convert from an index to a block coordinate
|
||||
return this.chunk.getSectionYFromSectionIndex(index) * 16;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return Integer.MAX_VALUE;
|
||||
|
||||
return this.minNonEmptyHeight;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getSolidHeightMapValue(int xRel, int zRel) { return this.chunk.getOrCreateHeightmapUnprimed(Heightmap.Types.WORLD_SURFACE).getFirstAvailable(xRel, zRel); }
|
||||
public int getMaxNonEmptyHeight()
|
||||
{
|
||||
if (this.maxNonEmptyHeight != Integer.MAX_VALUE)
|
||||
{
|
||||
return this.maxNonEmptyHeight;
|
||||
}
|
||||
|
||||
|
||||
// default if every section is empty or missing
|
||||
this.maxNonEmptyHeight = this.getExclusiveMaxBuildHeight();
|
||||
|
||||
// determine the highest empty section (top down)
|
||||
LevelChunkSection[] sections = this.chunk.getSections();
|
||||
for (int index = sections.length-1; index >= 0; index--)
|
||||
{
|
||||
// update at each position to fix using the max height if the chunk is empty
|
||||
this.maxNonEmptyHeight = this.getChunkSectionMinHeight(index) + 16;
|
||||
|
||||
if (sections[index] == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isChunkSectionEmpty(sections[index]))
|
||||
{
|
||||
// non-empty section found
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return this.maxNonEmptyHeight;
|
||||
}
|
||||
private static boolean isChunkSectionEmpty(LevelChunkSection section)
|
||||
{
|
||||
#if MC_VER == MC_1_16_5
|
||||
return section.isEmpty();
|
||||
#elif MC_VER == MC_1_17_1
|
||||
return section.isEmpty();
|
||||
#else
|
||||
return section.hasOnlyAir();
|
||||
#endif
|
||||
}
|
||||
private int getChunkSectionMinHeight(int index) { return (index * 16) + this.getInclusiveMinBuildHeight(); }
|
||||
|
||||
/** Will only run if the config says the MC heightmaps shouldn't be trusted. */
|
||||
public void recalculateDhHeightMapsIfNeeded()
|
||||
{
|
||||
// re-calculate the min/max heights for consistency (during world gen these may be wrong)
|
||||
this.minNonEmptyHeight = Integer.MIN_VALUE;
|
||||
this.maxNonEmptyHeight = Integer.MAX_VALUE;
|
||||
|
||||
|
||||
// recalculate heightmaps if needed
|
||||
if (this.solidHeightMap != null)
|
||||
{
|
||||
for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++)
|
||||
{
|
||||
for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++)
|
||||
{
|
||||
int minInclusiveBuildHeight = this.getMinNonEmptyHeight();
|
||||
// if no blocks are found the height map will be at the bottom of the world
|
||||
int solidHeight = minInclusiveBuildHeight;
|
||||
int lightBlockingHeight = minInclusiveBuildHeight;
|
||||
|
||||
|
||||
int y = this.getMaxNonEmptyHeight(); //this.getExclusiveMaxBuildHeight();
|
||||
IBlockStateWrapper block = this.getBlockState(x, y, z);
|
||||
while (// go down until we reach the minimum build height
|
||||
y > minInclusiveBuildHeight
|
||||
// keep going until we find both height map values
|
||||
&& (solidHeight == minInclusiveBuildHeight || lightBlockingHeight == minInclusiveBuildHeight))
|
||||
{
|
||||
// is this block solid?
|
||||
if (solidHeight == minInclusiveBuildHeight
|
||||
&& block.isSolid())
|
||||
{
|
||||
solidHeight = y;
|
||||
}
|
||||
|
||||
// is this block light blocking?
|
||||
if (lightBlockingHeight == minInclusiveBuildHeight
|
||||
&& block.getOpacity() != LodUtil.BLOCK_FULLY_TRANSPARENT)
|
||||
{
|
||||
lightBlockingHeight = y;
|
||||
}
|
||||
|
||||
// get the next block down
|
||||
y--;
|
||||
block = this.getBlockState(x, y, z);
|
||||
}
|
||||
|
||||
this.solidHeightMap[x][z] = solidHeight;
|
||||
this.lightBlockingHeightMap[x][z] = lightBlockingHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLightBlockingHeightMapValue(int xRel, int zRel) { return this.chunk.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING).getFirstAvailable(xRel, zRel); }
|
||||
public int getSolidHeightMapValue(int xRel, int zRel)
|
||||
{
|
||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(xRel, zRel);
|
||||
|
||||
// will be null if we want to use MC heightmaps
|
||||
if (this.solidHeightMap == null)
|
||||
{
|
||||
return this.chunk.getOrCreateHeightmapUnprimed(Heightmap.Types.WORLD_SURFACE).getFirstAvailable(xRel, zRel);
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.solidHeightMap[xRel][zRel];
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLightBlockingHeightMapValue(int xRel, int zRel)
|
||||
{
|
||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(xRel, zRel);
|
||||
|
||||
if (this.lightBlockingHeightMap == null)
|
||||
{
|
||||
return this.chunk.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING).getFirstAvailable(xRel, zRel);
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.lightBlockingHeightMap[xRel][zRel];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public IBiomeWrapper getBiome(int relX, int relY, int relZ)
|
||||
{
|
||||
#if PRE_MC_1_17_1
|
||||
#if MC_VER < MC_1_17_1
|
||||
return BiomeWrapper.getBiomeWrapper(this.chunk.getBiomes().getNoiseBiome(
|
||||
relX >> 2, relY >> 2, relZ >> 2),
|
||||
this.wrappedLevel);
|
||||
#elif PRE_MC_1_18_2
|
||||
#elif MC_VER < MC_1_18_2
|
||||
return BiomeWrapper.getBiomeWrapper(this.chunk.getBiomes().getNoiseBiome(
|
||||
QuartPos.fromBlock(relX), QuartPos.fromBlock(relY), QuartPos.fromBlock(relZ)),
|
||||
this.wrappedLevel);
|
||||
#elif PRE_MC_1_18_2
|
||||
#elif MC_VER < MC_1_18_2
|
||||
return BiomeWrapper.getBiomeWrapper(this.chunk.getNoiseBiome(
|
||||
QuartPos.fromBlock(relX), QuartPos.fromBlock(relY), QuartPos.fromBlock(relZ)),
|
||||
this.wrappedLevel);
|
||||
@@ -230,11 +357,151 @@ public class ChunkWrapper implements IChunkWrapper
|
||||
#endif
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBlockStateWrapper getBlockState(int relX, int relY, int relZ)
|
||||
{
|
||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, relY, relZ);
|
||||
|
||||
BlockPos.MutableBlockPos blockPos = MUTABLE_BLOCK_POS_REF.get();
|
||||
|
||||
blockPos.setX(relX);
|
||||
blockPos.setY(relY);
|
||||
blockPos.setZ(relZ);
|
||||
|
||||
// TODO copy into pooled array, this isn't thread safe and can cause MC to throw errors if the chunk is loaded
|
||||
return BlockStateWrapper.fromBlockState(this.chunk.getBlockState(blockPos), this.wrappedLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBlockStateWrapper getBlockState(int relX, int relY, int relZ, IMutableBlockPosWrapper mcBlockPos, IBlockStateWrapper guess)
|
||||
{
|
||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, relY, relZ);
|
||||
|
||||
BlockPos.MutableBlockPos pos = (BlockPos.MutableBlockPos)mcBlockPos.getWrappedMcObject();
|
||||
pos.setX(relX);
|
||||
pos.setY(relY);
|
||||
pos.setZ(relZ);
|
||||
|
||||
return BlockStateWrapper.fromBlockState(this.chunk.getBlockState(pos), this.wrappedLevel, guess);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
// Commented out experimental LevelChunkSection cloning logic to fix extremely rare concurrency modification issue
|
||||
// James has only ever seen a report relating to LevelSection concurrent modification once,
|
||||
// the issue can cause DH lighting/LOD building to fail due to the chunk being modified on the server.
|
||||
// James has only heard of this issue once, so it isn't a high priority issue.
|
||||
// And from James' quick look at a few different MC versions it appears the LevelChunkSection object changes quite drastically between MC versions,
|
||||
// meaning any cloning logic would have to either be a new wrapper or very MC version dependent, either way a lot of additional work.
|
||||
// Due to the large time cost and extremely rare nature of the issue, this logic is commented out unless this issue pops up again in the future.
|
||||
|
||||
// instance variable to hold the cloned sections
|
||||
private final LevelChunkSection[] levelChunkSections;
|
||||
|
||||
// new constructor logic to clone the sections
|
||||
public constructor(...)
|
||||
{
|
||||
// other constructor logic //
|
||||
|
||||
LevelChunkSection[] sections = this.chunk.getSections();
|
||||
this.levelChunkSections = new LevelChunkSection[sections.length];
|
||||
for (int i = 0; i < sections.length; i++)
|
||||
{
|
||||
LevelChunkSection section = sections[i];
|
||||
if (section != null)
|
||||
{
|
||||
// TODO implement section cloning for older MC versions, only 1.21.4 MC (and maybe other semi recent versions) have a clean way to handle this
|
||||
// TODO we probably want a wrapper object instead
|
||||
#if MC_VER < MC_1_21_4
|
||||
this.levelChunkSections[i] = section;
|
||||
#else
|
||||
this.levelChunkSections[i] = section.copy();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// replacement getters
|
||||
@Override
|
||||
public IBlockStateWrapper getBlockState(int relX, int relY, int relZ)
|
||||
{
|
||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, relY, relZ);
|
||||
return this.getBlockStateInternal(relX, relY, relZ, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBlockStateWrapper getBlockState(int relX, int relY, int relZ, IMutableBlockPosWrapper mcBlockPos, IBlockStateWrapper guess)
|
||||
{
|
||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, relY, relZ);
|
||||
return this.getBlockStateInternal(relX, relY, relZ, guess);
|
||||
}
|
||||
|
||||
// internal getter logic
|
||||
private IBlockStateWrapper getBlockStateInternal(int relX, int y, int relZ, @Nullable IBlockStateWrapper guess)
|
||||
{
|
||||
try
|
||||
{
|
||||
// attempt to get the section for this position
|
||||
int i = (y - this.getInclusiveMinBuildHeight()) / 16;
|
||||
if (i >= 0 && i < this.levelChunkSections.length)
|
||||
{
|
||||
LevelChunkSection section = this.levelChunkSections[i];
|
||||
if (!section.hasOnlyAir())
|
||||
{
|
||||
if (guess != null)
|
||||
{
|
||||
return BlockStateWrapper.fromBlockState(section.getBlockState(relX & 15, y & 15, relZ & 15), this.wrappedLevel, guess);
|
||||
}
|
||||
else
|
||||
{
|
||||
return BlockStateWrapper.fromBlockState(section.getBlockState(relX & 15, y & 15, relZ & 15), this.wrappedLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return BlockStateWrapper.AIR;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return BlockStateWrapper.AIR;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public IMutableBlockPosWrapper getMutableBlockPosWrapper() { return MUTABLE_BLOCK_POS_WRAPPER_REF.get(); }
|
||||
|
||||
@Override
|
||||
public DhChunkPos getChunkPos() { return this.chunkPos; }
|
||||
|
||||
public ChunkAccess getChunk() { return this.chunk; }
|
||||
|
||||
public void trySetStatus(ChunkStatus status) { trySetStatus(this.getChunk(), status); }
|
||||
/** does nothing if the chunk object doesn't support setting it's status */
|
||||
public static void trySetStatus(ChunkAccess chunk, ChunkStatus status)
|
||||
{
|
||||
if (chunk instanceof ProtoChunk)
|
||||
{
|
||||
#if MC_VER < MC_1_21_1
|
||||
((ProtoChunk) chunk).setStatus(status);
|
||||
#else
|
||||
((ProtoChunk) chunk).setPersistedStatus(status);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public ChunkStatus getStatus() { return getStatus(this.getChunk()); }
|
||||
public static ChunkStatus getStatus(ChunkAccess chunk)
|
||||
{
|
||||
#if MC_VER < MC_1_21_1
|
||||
return chunk.getStatus();
|
||||
#else
|
||||
return chunk.getPersistedStatus();
|
||||
#endif
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxBlockX() { return this.chunk.getPos().getMaxBlockX(); }
|
||||
@Override
|
||||
@@ -244,50 +511,21 @@ public class ChunkWrapper implements IChunkWrapper
|
||||
@Override
|
||||
public int getMinBlockZ() { return this.chunk.getPos().getMinBlockZ(); }
|
||||
|
||||
@Override
|
||||
public long getLongChunkPos() { return this.chunk.getPos().toLong(); }
|
||||
|
||||
|
||||
//==========//
|
||||
// lighting //
|
||||
//==========//
|
||||
|
||||
@Override
|
||||
public void setIsDhLightCorrect(boolean isDhLightCorrect) { this.isDhLightCorrect = isDhLightCorrect; }
|
||||
public void setIsDhSkyLightCorrect(boolean isDhLightCorrect) { this.isDhSkyLightCorrect = isDhLightCorrect; }
|
||||
@Override
|
||||
public void setIsDhBlockLightCorrect(boolean isDhLightCorrect) { this.isDhBlockLightCorrect = isDhLightCorrect; }
|
||||
|
||||
@Override
|
||||
public void setUseDhLighting(boolean useDhLighting) { this.useDhLighting = useDhLighting; }
|
||||
|
||||
|
||||
|
||||
public boolean isDhBlockLightingCorrect() { return this.isDhBlockLightCorrect; }
|
||||
@Override
|
||||
public boolean isLightCorrect()
|
||||
{
|
||||
if (this.useDhLighting)
|
||||
{
|
||||
return this.isDhLightCorrect;
|
||||
}
|
||||
|
||||
|
||||
#if MC_1_16_5 || MC_1_17_1
|
||||
return false; // MC's lighting engine doesn't work consistently enough to trust for 1.16 or 1.17
|
||||
#else
|
||||
if (this.chunk instanceof LevelChunk)
|
||||
{
|
||||
LevelChunk levelChunk = (LevelChunk) this.chunk;
|
||||
if (levelChunk.getLevel() instanceof ClientLevel)
|
||||
{
|
||||
// connected to a server
|
||||
return this.isMcClientLightingCorrect;
|
||||
}
|
||||
else
|
||||
{
|
||||
// in a single player world
|
||||
return this.chunk.isLightCorrect() && levelChunk.loaded;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// called when in a single player world and the chunk is a proto chunk (in world gen, and not active)
|
||||
return this.chunk.isLightCorrect();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
public boolean isDhSkyLightCorrect() { return this.isDhSkyLightCorrect; }
|
||||
|
||||
|
||||
@Override
|
||||
@@ -307,10 +545,13 @@ public class ChunkWrapper implements IChunkWrapper
|
||||
{
|
||||
if (this.blockLightStorage == null)
|
||||
{
|
||||
this.blockLightStorage = new ChunkLightStorage(this.getMinBuildHeight(), this.getMaxBuildHeight());
|
||||
this.blockLightStorage = ChunkLightStorage.createBlockLightStorage(this);
|
||||
}
|
||||
return this.blockLightStorage;
|
||||
}
|
||||
public void setBlockLightStorage(ChunkLightStorage lightStorage) { this.blockLightStorage = lightStorage; }
|
||||
@Override
|
||||
public void clearDhBlockLighting() { this.getBlockLightStorage().clear(); }
|
||||
|
||||
|
||||
@Override
|
||||
@@ -325,57 +566,26 @@ public class ChunkWrapper implements IChunkWrapper
|
||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
|
||||
this.getSkyLightStorage().set(relX, y, relZ, lightValue);
|
||||
}
|
||||
@Override
|
||||
public void clearDhSkyLighting() { this.getSkyLightStorage().clear(); }
|
||||
|
||||
private ChunkLightStorage getSkyLightStorage()
|
||||
{
|
||||
if (this.skyLightStorage == null)
|
||||
{
|
||||
this.skyLightStorage = new ChunkLightStorage(this.getMinBuildHeight(), this.getMaxBuildHeight());
|
||||
this.skyLightStorage = ChunkLightStorage.createSkyLightStorage(this);
|
||||
}
|
||||
return this.skyLightStorage;
|
||||
}
|
||||
public void setSkyLightStorage(ChunkLightStorage lightStorage) { this.skyLightStorage = lightStorage; }
|
||||
|
||||
|
||||
/**
|
||||
* FIXME synchronized is necessary for a rare issue where this method is called from two separate threads at the same time
|
||||
* before the list has finished populating.
|
||||
*/
|
||||
@Override
|
||||
public int getBlockLight(int relX, int y, int relZ)
|
||||
{
|
||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
|
||||
|
||||
// use the full lighting engine when the chunks are within render distance or the config requests it
|
||||
if (this.useDhLighting)
|
||||
{
|
||||
// DH lighting method
|
||||
return this.getBlockLightStorage().get(relX, y, relZ);
|
||||
}
|
||||
else
|
||||
{
|
||||
// note: this returns 0 if the chunk is unload
|
||||
|
||||
// MC lighting method
|
||||
return this.lightSource.getBrightness(LightLayer.BLOCK, new BlockPos(relX + this.getMinBlockX(), y, relZ + this.getMinBlockZ()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSkyLight(int relX, int y, int relZ)
|
||||
{
|
||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
|
||||
|
||||
// use the full lighting engine when the chunks are within render distance or the config requests it
|
||||
if (this.useDhLighting)
|
||||
{
|
||||
// DH lighting method
|
||||
return this.getSkyLightStorage().get(relX, y, relZ);
|
||||
}
|
||||
else
|
||||
{
|
||||
// MC lighting method
|
||||
return this.lightSource.getBrightness(LightLayer.SKY, new BlockPos(relX + this.getMinBlockX(), y, relZ + this.getMinBlockZ()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<DhBlockPos> getBlockLightPosList()
|
||||
public synchronized ArrayList<DhBlockPos> getWorldBlockLightPosList()
|
||||
{
|
||||
// only populate the list once
|
||||
if (this.blockLightPosList == null)
|
||||
@@ -383,7 +593,7 @@ public class ChunkWrapper implements IChunkWrapper
|
||||
this.blockLightPosList = new ArrayList<>();
|
||||
|
||||
|
||||
#if PRE_MC_1_20_1
|
||||
#if MC_VER < MC_1_20_1
|
||||
this.chunk.getLights().forEach((blockPos) ->
|
||||
{
|
||||
this.blockLightPosList.add(new DhBlockPos(blockPos.getX(), blockPos.getY(), blockPos.getZ()));
|
||||
@@ -391,7 +601,13 @@ public class ChunkWrapper implements IChunkWrapper
|
||||
#else
|
||||
this.chunk.findBlockLightSources((blockPos, blockState) ->
|
||||
{
|
||||
this.blockLightPosList.add(new DhBlockPos(blockPos.getX(), blockPos.getY(), blockPos.getZ()));
|
||||
DhBlockPos pos = new DhBlockPos(blockPos.getX(), blockPos.getY(), blockPos.getZ());
|
||||
|
||||
// this can be uncommented if MC decides to return relative block positions in the future instead of world positions
|
||||
//pos.mutateToChunkRelativePos(pos);
|
||||
//pos.mutateOffset(this.chunkPos.getMinBlockX(), 0, this.chunkPos.getMinBlockZ(), pos);
|
||||
|
||||
this.blockLightPosList.add(pos);
|
||||
});
|
||||
#endif
|
||||
}
|
||||
@@ -399,172 +615,33 @@ public class ChunkWrapper implements IChunkWrapper
|
||||
return this.blockLightPosList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doNearbyChunksExist()
|
||||
{
|
||||
if (this.lightSource instanceof DhLitWorldGenRegion)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int dx = -1; dx <= 1; dx++)
|
||||
{
|
||||
for (int dz = -1; dz <= 1; dz++)
|
||||
{
|
||||
if (dx == 0 && dz == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (this.lightSource.getChunk(dx + this.chunk.getPos().x, dz + this.chunk.getPos().z, ChunkStatus.BIOMES, false) == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public LevelReader getColorResolver() { return this.lightSource; }
|
||||
|
||||
@Override
|
||||
public String toString() { return this.chunk.getClass().getSimpleName() + this.chunk.getPos(); }
|
||||
|
||||
@Override
|
||||
public IBlockStateWrapper getBlockState(int relX, int relY, int relZ)
|
||||
{
|
||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, relY, relZ);
|
||||
|
||||
BlockPos.MutableBlockPos blockPos = MUTABLE_BLOCK_POS_REF.get();
|
||||
|
||||
blockPos.setX(relX);
|
||||
blockPos.setY(relY);
|
||||
blockPos.setZ(relZ);
|
||||
|
||||
return BlockStateWrapper.fromBlockState(this.chunk.getBlockState(blockPos), this.wrappedLevel);
|
||||
}
|
||||
//===============//
|
||||
// other methods //
|
||||
//===============//
|
||||
|
||||
@Override
|
||||
public boolean isStillValid() { return this.wrappedLevel.tryGetChunk(this.chunkPos) == this; }
|
||||
|
||||
|
||||
public static void syncedUpdateClientLightStatus()
|
||||
{
|
||||
#if PRE_MC_1_18_2
|
||||
// TODO: Check what to do in 1.18.1 and older
|
||||
|
||||
// since we don't currently handle this list,
|
||||
// clear it to prevent memory leaks
|
||||
chunksNeedingClientLightUpdating.clear();
|
||||
|
||||
#else
|
||||
|
||||
// update the chunks client lighting
|
||||
ChunkWrapper chunkWrapper = chunksNeedingClientLightUpdating.poll();
|
||||
while (chunkWrapper != null)
|
||||
{
|
||||
chunkWrapper.updateIsClientLightingCorrect();
|
||||
chunkWrapper = chunksNeedingClientLightUpdating.poll();
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
/** Should be called after client light updates are triggered. */
|
||||
private void updateIsClientLightingCorrect()
|
||||
{
|
||||
if (this.chunk instanceof LevelChunk && ((LevelChunk) this.chunk).getLevel() instanceof ClientLevel)
|
||||
{
|
||||
LevelChunk levelChunk = (LevelChunk) this.chunk;
|
||||
ClientChunkCache clientChunkCache = ((ClientLevel) levelChunk.getLevel()).getChunkSource();
|
||||
this.isMcClientLightingCorrect = clientChunkCache.getChunkForLighting(this.chunk.getPos().x, this.chunk.getPos().z) != null &&
|
||||
#if MC_1_16_5 || MC_1_17_1
|
||||
levelChunk.isLightCorrect();
|
||||
#elif PRE_MC_1_20_1
|
||||
levelChunk.isClientLightReady();
|
||||
#else
|
||||
checkLightSectionsOnChunk(levelChunk, levelChunk.getLevel().getLightEngine());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#if POST_MC_1_20_1
|
||||
private static boolean checkLightSectionsOnChunk(LevelChunk chunk, LevelLightEngine engine)
|
||||
{
|
||||
LevelChunkSection[] sections = chunk.getSections();
|
||||
int minY = chunk.getMinSection();
|
||||
int maxY = chunk.getMaxSection();
|
||||
for (int y = minY; y < maxY; ++y)
|
||||
{
|
||||
LevelChunkSection section = sections[chunk.getSectionIndexFromSectionY(y)];
|
||||
if (section.hasOnlyAir()) continue;
|
||||
if (!engine.lightOnInSection(SectionPos.of(chunk.getPos(), y)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper methods //
|
||||
// base overrides //
|
||||
//================//
|
||||
|
||||
/** used to prevent accidentally attempting to get/set values outside this chunk's boundaries */
|
||||
private void throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(int x, int y, int z) throws IndexOutOfBoundsException
|
||||
{
|
||||
if (!RUN_RELATIVE_POS_INDEX_VALIDATION)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// FIXME +1 is to handle the fact that LodDataBuilder adds +1 to all block lighting calculations, also done in the constructor
|
||||
int minHeight = this.getMinBuildHeight();
|
||||
int maxHeight = this.getMaxBuildHeight() + 1;
|
||||
|
||||
if (x < 0 || x >= LodUtil.CHUNK_WIDTH
|
||||
|| z < 0 || z >= LodUtil.CHUNK_WIDTH
|
||||
|| y < minHeight || y > maxHeight)
|
||||
{
|
||||
String errorMessage = "Relative position [" + x + "," + y + "," + z + "] out of bounds. \n" +
|
||||
"X/Z must be between 0 and 15 (inclusive) \n" +
|
||||
"Y must be between [" + minHeight + "] and [" + maxHeight + "] (inclusive).";
|
||||
throw new IndexOutOfBoundsException(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts a 3D position into a 1D array index. <br><br>
|
||||
*
|
||||
* Source: <br>
|
||||
* <a href="https://stackoverflow.com/questions/7367770/how-to-flatten-or-index-3d-array-in-1d-array">stackoverflow</a>
|
||||
*/
|
||||
public int relativeBlockPosToIndex(int xRel, int y, int zRel)
|
||||
{
|
||||
int yRel = y - this.getMinBuildHeight();
|
||||
return (zRel * LodUtil.CHUNK_WIDTH * this.getHeight()) + (yRel * LodUtil.CHUNK_WIDTH) + xRel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a 3D position into a 1D array index. <br><br>
|
||||
*
|
||||
* Source: <br>
|
||||
* <a href="https://stackoverflow.com/questions/7367770/how-to-flatten-or-index-3d-array-in-1d-array">stackoverflow</a>
|
||||
*/
|
||||
public DhBlockPos indexToRelativeBlockPos(int index)
|
||||
{
|
||||
final int zRel = index / (LodUtil.CHUNK_WIDTH * this.getHeight());
|
||||
index -= (zRel * LodUtil.CHUNK_WIDTH * this.getHeight());
|
||||
|
||||
final int y = index / LodUtil.CHUNK_WIDTH;
|
||||
final int yRel = y + this.getMinBuildHeight();
|
||||
|
||||
final int xRel = index % LodUtil.CHUNK_WIDTH;
|
||||
return new DhBlockPos(xRel, yRel, zRel);
|
||||
}
|
||||
@Override
|
||||
public String toString() { return this.chunk.getClass().getSimpleName() + this.chunk.getPos(); }
|
||||
|
||||
//@Override
|
||||
//public int hashCode()
|
||||
//{
|
||||
// if (this.blockBiomeHashCode == 0)
|
||||
// {
|
||||
// this.blockBiomeHashCode = this.getBlockBiomeHashCode();
|
||||
// }
|
||||
//
|
||||
// return this.blockBiomeHashCode;
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
+96
-62
@@ -16,7 +16,6 @@ import java.util.regex.Pattern;
|
||||
// Logger (for debug stuff)
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.config.DisallowSelectingViaConfigGui;
|
||||
import com.seibel.distanthorizons.api.enums.config.EUpdateBranch;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.config.ConfigBase;
|
||||
import com.seibel.distanthorizons.core.config.types.*;
|
||||
@@ -35,7 +34,7 @@ import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.Font;
|
||||
#if PRE_MC_1_20_1
|
||||
#if MC_VER < MC_1_20_1
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.gui.GuiComponent;
|
||||
#else
|
||||
@@ -49,7 +48,7 @@ import net.minecraft.client.gui.components.events.GuiEventListener;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.client.resources.language.I18n; // translation
|
||||
#if POST_MC_1_17_1
|
||||
#if MC_VER >= MC_1_17_1
|
||||
import net.minecraft.client.gui.narration.NarratableEntry;
|
||||
#endif
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
@@ -98,6 +97,7 @@ public class ClassicConfigGUI
|
||||
public static final int SpaceFromRightScreen = 10;
|
||||
public static final int ButtonWidthSpacing = 5;
|
||||
public static final int ResetButtonWidth = 40;
|
||||
public static final int ResetButtonHeight = 20;
|
||||
|
||||
}
|
||||
|
||||
@@ -118,14 +118,18 @@ public class ClassicConfigGUI
|
||||
*/
|
||||
private static void textField(AbstractConfigType info, Function<String, Number> func, Pattern pattern, boolean cast)
|
||||
{
|
||||
boolean isNumber = pattern != null;
|
||||
((EntryInfo) info.guiValue).widget = (BiFunction<EditBox, Button, Predicate<String>>) (editBox, button) -> stringValue ->
|
||||
{
|
||||
boolean isNumber = (pattern != null);
|
||||
|
||||
stringValue = stringValue.trim();
|
||||
if (!(stringValue.isEmpty() || !isNumber || pattern.matcher(stringValue).matches()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Number value = 0;
|
||||
|
||||
Number value = info.typeIsFloatingPointNumber() ? 0.0 : 0; // different default values are needed so implicit casting works correctly (if not done casting from 0 (an int) to a double will cause an exception)
|
||||
((EntryInfo) info.guiValue).error = null;
|
||||
if (isNumber && !stringValue.isEmpty() && !stringValue.equals("-") && !stringValue.equals("."))
|
||||
{
|
||||
@@ -142,34 +146,41 @@ public class ClassicConfigGUI
|
||||
switch (isValid)
|
||||
{
|
||||
case 0:
|
||||
((EntryInfo) info.guiValue).error = null; break;
|
||||
((EntryInfo) info.guiValue).error = null;
|
||||
break;
|
||||
case -1:
|
||||
((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cMinimum length is " + ((ConfigEntry) info).getMin())); break;
|
||||
((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cMinimum length is " + ((ConfigEntry) info).getMin()));
|
||||
break;
|
||||
case 1:
|
||||
((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cMaximum length is " + ((ConfigEntry) info).getMax())); break;
|
||||
((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cMaximum length is " + ((ConfigEntry) info).getMax()));
|
||||
break;
|
||||
case 2:
|
||||
((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cValue is invalid")); break;
|
||||
((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cValue is invalid"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
((EntryInfo) info.guiValue).tempValue = stringValue;
|
||||
editBox.setTextColor(((ConfigEntry) info).isValid(value) == 0 ? 0xFFFFFFFF : 0xFFFF7777);
|
||||
editBox.setTextColor(((ConfigEntry) info).isValid(value) == 0 ? 0xFFFFFFFF : 0xFFFF7777); // white and red
|
||||
// button.active = entries.stream().allMatch(e -> e.inLimits);
|
||||
|
||||
|
||||
if (((ConfigEntry) info).isValid(value) == 0 && info.getType() != List.class)
|
||||
if (info.getType() == String.class
|
||||
|| info.getType() == List.class)
|
||||
{
|
||||
((ConfigEntry) info).uiSetWithoutSaving(stringValue);
|
||||
}
|
||||
else if (((ConfigEntry) info).isValid(value) == 0)
|
||||
{
|
||||
if (!cast)
|
||||
{
|
||||
((ConfigEntry) info).uiSetWithoutSaving(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
((ConfigEntry) info).uiSetWithoutSaving(value.intValue());
|
||||
}
|
||||
}
|
||||
// else if (((ConfigEntry) info).isValidMemoryAddress() == 0)
|
||||
// {
|
||||
// if (((List<String>) info.get()).size() == ((EntryInfo) info.guiValue).index)
|
||||
// info.uiSet(((List<String>) info.get()).add(""));
|
||||
// info.uiSet(((List<String>) info.get()).set(((EntryInfo) info.guiValue).index, Arrays.stream(((EntryInfo) info.guiValue).tempValue.replace("[", "").replace("]", "").split(", ")).collect(Collectors.toList()).get(0)));
|
||||
// }
|
||||
|
||||
return true;
|
||||
};
|
||||
@@ -244,17 +255,23 @@ public class ClassicConfigGUI
|
||||
}
|
||||
|
||||
// Changelog button
|
||||
if (Config.Client.Advanced.AutoUpdater.enableAutoUpdater.get() && Config.Client.Advanced.AutoUpdater.updateBranch.get() == EUpdateBranch.STABLE)
|
||||
if (Config.Client.Advanced.AutoUpdater.enableAutoUpdater.get() && !ModInfo.IS_DEV_BUILD) // we only have changelogs for stable builds
|
||||
{
|
||||
this.addBtn(new TexturedButtonWidget(
|
||||
// Where the button is on the screen
|
||||
this.width - 28, this.height - 28,
|
||||
// Width and height of the button
|
||||
20, 20,
|
||||
// Offset
|
||||
// texture UV Offset
|
||||
0, 0,
|
||||
// Some textuary stuff
|
||||
0, new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"), 20, 20,
|
||||
0,
|
||||
#if MC_VER < MC_1_21_1
|
||||
new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"),
|
||||
#else
|
||||
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"),
|
||||
#endif
|
||||
20, 20,
|
||||
// Create the button and tell it where to go
|
||||
(buttonWidget) -> {
|
||||
ChangelogScreen changelogScreen = new ChangelogScreen(this);
|
||||
@@ -269,18 +286,25 @@ public class ClassicConfigGUI
|
||||
}
|
||||
|
||||
|
||||
addBtn(MakeBtn(Translatable("distanthorizons.general.cancel"), this.width / 2 - 154, this.height - 28, 150, 20, button -> {
|
||||
ConfigBase.INSTANCE.configFileINSTANCE.loadFromFile();
|
||||
Objects.requireNonNull(minecraft).setScreen(parent);
|
||||
}));
|
||||
addBtn(MakeBtn(Translatable("distanthorizons.general.cancel"),
|
||||
this.width / 2 - 154, this.height - 28,
|
||||
150, 20,
|
||||
button ->
|
||||
{
|
||||
ConfigBase.INSTANCE.configFileINSTANCE.loadFromFile();
|
||||
Objects.requireNonNull(minecraft).setScreen(parent);
|
||||
}));
|
||||
doneButton = addBtn(MakeBtn(Translatable("distanthorizons.general.done"), this.width / 2 + 4, this.height - 28, 150, 20, (button) -> {
|
||||
ConfigBase.INSTANCE.configFileINSTANCE.saveToFile();
|
||||
Objects.requireNonNull(minecraft).setScreen(parent);
|
||||
}));
|
||||
|
||||
this.list = new ConfigListWidget(this.minecraft, this.width * 2, this.height, 32, this.height - 32, 25);
|
||||
this.list = new ConfigListWidget(this.minecraft, this.width * 2, this.height, 32, 32, 25);
|
||||
|
||||
#if MC_VER < MC_1_20_6 // no background is rendered in MC 1.20.6+
|
||||
if (this.minecraft != null && this.minecraft.level != null)
|
||||
this.list.setRenderBackground(false);
|
||||
#endif
|
||||
|
||||
this.addWidget(this.list);
|
||||
|
||||
@@ -293,10 +317,13 @@ public class ClassicConfigGUI
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
System.out.println("ERROR: Failed to show [" + info.getNameWCategory() + "]");
|
||||
String message = "ERROR: Failed to show [\" + info.getNameWCategory() + \"], error: ["+e.getMessage()+"]";
|
||||
if (info.get() != null)
|
||||
System.out.print(" with the value [" + info.get() + "] with type [" + info.getType() + "]");
|
||||
e.printStackTrace();
|
||||
{
|
||||
message += " with the value [" + info.get() + "] with type [" + info.getType() + "]";
|
||||
}
|
||||
|
||||
LOGGER.error(message, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,6 +338,7 @@ public class ClassicConfigGUI
|
||||
initEntry(info, this.translationPrefix);
|
||||
Component name = Translatable(translationPrefix + info.getNameWCategory());
|
||||
|
||||
|
||||
if (ConfigEntry.class.isAssignableFrom(info.getClass()))
|
||||
{
|
||||
Button.OnPress btnAction = button -> {
|
||||
@@ -319,12 +347,12 @@ public class ClassicConfigGUI
|
||||
this.reload = true;
|
||||
Objects.requireNonNull(minecraft).setScreen(this);
|
||||
};
|
||||
int a = this.width - ConfigScreenConfigs.SpaceFromRightScreen - 150 - ConfigScreenConfigs.ButtonWidthSpacing - ConfigScreenConfigs.ResetButtonWidth;
|
||||
int b = 0;
|
||||
int c = ConfigScreenConfigs.ResetButtonWidth;
|
||||
int d = 20;
|
||||
int posX = this.width - ConfigScreenConfigs.SpaceFromRightScreen - 150 - ConfigScreenConfigs.ButtonWidthSpacing - ConfigScreenConfigs.ResetButtonWidth;
|
||||
int posZ = 0;
|
||||
|
||||
Button resetButton = MakeBtn(Translatable("distanthorizons.general.reset").withStyle(ChatFormatting.RED), a, b, c, d, btnAction);
|
||||
Button resetButton = MakeBtn(Translatable("distanthorizons.general.reset").withStyle(ChatFormatting.RED),
|
||||
posX, posZ, ConfigScreenConfigs.ResetButtonWidth, ConfigScreenConfigs.ResetButtonHeight,
|
||||
btnAction);
|
||||
|
||||
if (((EntryInfo) info.guiValue).widget instanceof Map.Entry)
|
||||
{
|
||||
@@ -369,9 +397,9 @@ public class ClassicConfigGUI
|
||||
this.list.addButton(null, null, null, name);
|
||||
return;
|
||||
}
|
||||
if (ConfigLinkedEntry.class.isAssignableFrom(info.getClass()))
|
||||
if (ConfigUiLinkedEntry.class.isAssignableFrom(info.getClass()))
|
||||
{
|
||||
this.addMenuItem(((ConfigLinkedEntry) info).get());
|
||||
this.addMenuItem(((ConfigUiLinkedEntry) info).get());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -379,13 +407,13 @@ public class ClassicConfigGUI
|
||||
}
|
||||
|
||||
@Override
|
||||
#if PRE_MC_1_20_1
|
||||
#if MC_VER < MC_1_20_1
|
||||
public void render(PoseStack matrices, int mouseX, int mouseY, float delta)
|
||||
#else
|
||||
public void render(GuiGraphics matrices, int mouseX, int mouseY, float delta)
|
||||
#endif
|
||||
{
|
||||
#if PRE_MC_1_20_2 // 1.20.2 now enables this by default in the `this.list.render` function
|
||||
#if MC_VER < MC_1_20_2 // 1.20.2 now enables this by default in the `this.list.render` function
|
||||
this.renderBackground(matrices); // Renders background
|
||||
#else
|
||||
super.render(matrices, mouseX, mouseY, delta);
|
||||
@@ -420,15 +448,17 @@ public class ClassicConfigGUI
|
||||
}
|
||||
|
||||
// A quick fix for tooltips on linked entries
|
||||
AbstractConfigType newInfo = ConfigLinkedEntry.class.isAssignableFrom(info.getClass()) ?
|
||||
((ConfigLinkedEntry) info).get() :
|
||||
AbstractConfigType newInfo = ConfigUiLinkedEntry.class.isAssignableFrom(info.getClass()) ?
|
||||
((ConfigUiLinkedEntry) info).get() :
|
||||
info;
|
||||
|
||||
Component name = Translatable(this.translationPrefix + (info.category.isEmpty() ? "" : info.category + ".") + info.getName());
|
||||
String key = translationPrefix + (newInfo.category.isEmpty() ? "" : newInfo.category + ".") + newInfo.getName() + ".@tooltip";
|
||||
|
||||
if (((EntryInfo) newInfo.guiValue).error != null && text.equals(name))
|
||||
{
|
||||
DhRenderTooltip(matrices, font, ((EntryInfo) newInfo.guiValue).error.getValue(), mouseX, mouseY);
|
||||
}
|
||||
else if (I18n.exists(key) && (text != null && text.equals(name)))
|
||||
{
|
||||
List<Component> list = new ArrayList<>();
|
||||
@@ -441,7 +471,7 @@ public class ClassicConfigGUI
|
||||
}
|
||||
}
|
||||
}
|
||||
#if PRE_MC_1_20_2
|
||||
#if MC_VER < MC_1_20_2
|
||||
super.render(matrices, mouseX, mouseY, delta);
|
||||
#endif
|
||||
}
|
||||
@@ -452,47 +482,47 @@ public class ClassicConfigGUI
|
||||
|
||||
|
||||
|
||||
private static void initEntry(AbstractConfigType info, String translationPrefix)
|
||||
private static void initEntry(AbstractConfigType configType, String translationPrefix)
|
||||
{
|
||||
info.guiValue = new EntryInfo();
|
||||
Class<?> fieldClass = info.getType();
|
||||
configType.guiValue = new EntryInfo();
|
||||
Class<?> fieldClass = configType.getType();
|
||||
|
||||
if (ConfigEntry.class.isAssignableFrom(info.getClass()))
|
||||
if (ConfigEntry.class.isAssignableFrom(configType.getClass()))
|
||||
{
|
||||
if (fieldClass == Integer.class)
|
||||
{
|
||||
// For int
|
||||
textField(info, Integer::parseInt, INTEGER_ONLY_REGEX, true);
|
||||
textField(configType, Integer::parseInt, INTEGER_ONLY_REGEX, true);
|
||||
}
|
||||
else if (fieldClass == Double.class)
|
||||
{
|
||||
// For double
|
||||
textField(info, Double::parseDouble, DECIMAL_ONLY_REGEX, false);
|
||||
textField(configType, Double::parseDouble, DECIMAL_ONLY_REGEX, false);
|
||||
}
|
||||
else if (fieldClass == String.class || fieldClass == List.class)
|
||||
{
|
||||
// For string or list
|
||||
textField(info, String::length, null, true);
|
||||
textField(configType, String::length, null, true);
|
||||
}
|
||||
else if (fieldClass == Boolean.class)
|
||||
{
|
||||
// For boolean
|
||||
Function<Object, Component> func = value -> Translatable("distanthorizons.general."+((Boolean) value ? "true" : "false")).withStyle((Boolean) value ? ChatFormatting.GREEN : ChatFormatting.RED);
|
||||
|
||||
((EntryInfo) info.guiValue).widget = new AbstractMap.SimpleEntry<Button.OnPress, Function<Object, Component>>(button -> {
|
||||
((ConfigEntry) info).uiSetWithoutSaving(!(Boolean) info.get());
|
||||
button.setMessage(func.apply(info.get()));
|
||||
((EntryInfo) configType.guiValue).widget = new AbstractMap.SimpleEntry<Button.OnPress, Function<Object, Component>>(button -> {
|
||||
((ConfigEntry) configType).uiSetWithoutSaving(!(Boolean) configType.get());
|
||||
button.setMessage(func.apply(configType.get()));
|
||||
}, func);
|
||||
}
|
||||
else if (fieldClass.isEnum())
|
||||
{
|
||||
// For enum
|
||||
List<?> values = Arrays.asList(info.getType().getEnumConstants());
|
||||
Function<Object, Component> func = value -> Translatable(translationPrefix + "enum." + fieldClass.getSimpleName() + "." + info.get().toString());
|
||||
((EntryInfo) info.guiValue).widget = new AbstractMap.SimpleEntry<Button.OnPress, Function<Object, Component>>(button -> {
|
||||
List<?> values = Arrays.asList(configType.getType().getEnumConstants());
|
||||
Function<Object, Component> func = value -> Translatable(translationPrefix + "enum." + fieldClass.getSimpleName() + "." + configType.get().toString());
|
||||
((EntryInfo) configType.guiValue).widget = new AbstractMap.SimpleEntry<Button.OnPress, Function<Object, Component>>(button -> {
|
||||
|
||||
// get the currently selected enum and enum index
|
||||
int startingIndex = values.indexOf(info.get());
|
||||
int startingIndex = values.indexOf(configType.get());
|
||||
Enum<?> enumValue = (Enum<?>) values.get(startingIndex);
|
||||
|
||||
// search for the next enum that is selectable
|
||||
@@ -520,12 +550,12 @@ public class ClassicConfigGUI
|
||||
}
|
||||
|
||||
|
||||
((ConfigEntry<Enum<?>>) info).uiSetWithoutSaving(enumValue);
|
||||
button.setMessage(func.apply(info.get()));
|
||||
((ConfigEntry<Enum<?>>) configType).uiSetWithoutSaving(enumValue);
|
||||
button.setMessage(func.apply(configType.get()));
|
||||
}, func);
|
||||
}
|
||||
}
|
||||
else if (ConfigCategory.class.isAssignableFrom(info.getClass()))
|
||||
else if (ConfigCategory.class.isAssignableFrom(configType.getClass()))
|
||||
{
|
||||
// if (!info.info.getName().equals(""))
|
||||
// info.name = new TranslatableComponent(info.info.getName());
|
||||
@@ -537,9 +567,13 @@ public class ClassicConfigGUI
|
||||
{
|
||||
Font textRenderer;
|
||||
|
||||
public ConfigListWidget(Minecraft minecraftClient, int i, int j, int k, int l, int m)
|
||||
public ConfigListWidget(Minecraft minecraftClient, int canvasWidth, int canvasHeight, int topMargin, int botMargin, int itemSpacing)
|
||||
{
|
||||
super(minecraftClient, i, j, k, l, m);
|
||||
#if MC_VER < MC_1_20_4
|
||||
super(minecraftClient, canvasWidth, canvasHeight, topMargin, canvasHeight - botMargin, itemSpacing);
|
||||
#else
|
||||
super(minecraftClient, canvasWidth, canvasHeight - (topMargin + botMargin), topMargin, itemSpacing);
|
||||
#endif
|
||||
this.centerListVertically = false;
|
||||
textRenderer = minecraftClient.font;
|
||||
}
|
||||
@@ -601,7 +635,7 @@ public class ClassicConfigGUI
|
||||
}
|
||||
|
||||
@Override
|
||||
#if PRE_MC_1_20_1
|
||||
#if MC_VER < MC_1_20_1
|
||||
public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta)
|
||||
#else
|
||||
public void render(GuiGraphics matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta)
|
||||
@@ -623,7 +657,7 @@ public class ClassicConfigGUI
|
||||
indexButton.render(matrices, mouseX, mouseY, tickDelta);
|
||||
}
|
||||
if (text != null && (!text.getString().contains("spacer") || button != null))
|
||||
#if PRE_MC_1_20_1
|
||||
#if MC_VER < MC_1_20_1
|
||||
GuiComponent.drawString(matrices, textRenderer, text, 12, y + 5, 0xFFFFFF);
|
||||
#else
|
||||
matrices.drawString(textRenderer, text, 12, y + 5, 0xFFFFFF);
|
||||
@@ -638,7 +672,7 @@ public class ClassicConfigGUI
|
||||
|
||||
// Only for 1.17 and over
|
||||
// Remove in 1.16 and below
|
||||
#if POST_MC_1_17_1
|
||||
#if MC_VER >= MC_1_17_1
|
||||
@Override
|
||||
public List<? extends NarratableEntry> narratables()
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.gui;
|
||||
|
||||
import net.minecraft.client.gui.Font;
|
||||
#if PRE_MC_1_20_1
|
||||
#if MC_VER < MC_1_20_1
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
#else
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
@@ -24,14 +24,14 @@ public class DhScreen extends Screen
|
||||
// addButton in 1.16 and below
|
||||
protected Button addBtn(Button button)
|
||||
{
|
||||
#if PRE_MC_1_17_1
|
||||
#if MC_VER < MC_1_17_1
|
||||
return this.addButton(button);
|
||||
#else
|
||||
return this.addRenderableWidget(button);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if PRE_MC_1_20_1
|
||||
#if MC_VER < MC_1_20_1
|
||||
protected void DhDrawCenteredString(PoseStack guiStack, Font font, Component text, int x, int y, int color)
|
||||
{
|
||||
drawCenteredString(guiStack, font, text, x, y, color);
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ public class GetConfigScreen
|
||||
case JavaFX:
|
||||
return MinecraftScreen.getScreen(parent, new JavaScreenHandlerScreen(new ConfigScreen()), ModInfo.ID + ".title");
|
||||
default:
|
||||
return null;
|
||||
throw new IllegalArgumentException("No config screen implementation defined for ["+useScreen+"].");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-10
@@ -5,7 +5,7 @@ import net.minecraft.client.gui.components.Button;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.MutableComponent;
|
||||
|
||||
#if PRE_MC_1_19_2
|
||||
#if MC_VER < MC_1_19_2
|
||||
import net.minecraft.network.chat.TextComponent;
|
||||
import net.minecraft.network.chat.TranslatableComponent;
|
||||
#endif
|
||||
@@ -15,18 +15,18 @@ public class GuiHelper
|
||||
/**
|
||||
* Helper static methods for versional compat
|
||||
*/
|
||||
public static Button MakeBtn(Component base, int a, int b, int c, int d, Button.OnPress action)
|
||||
public static Button MakeBtn(Component base, int posX, int posZ, int width, int height, Button.OnPress action)
|
||||
{
|
||||
#if PRE_MC_1_19_4
|
||||
return new Button(a, b, c, d, base, action);
|
||||
#if MC_VER < MC_1_19_4
|
||||
return new Button(posX, posZ, width, height, base, action);
|
||||
#else
|
||||
return Button.builder(base, action).bounds(a, b, c, d).build();
|
||||
return Button.builder(base, action).bounds(posX, posZ, width, height).build();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static MutableComponent TextOrLiteral(String text)
|
||||
{
|
||||
#if PRE_MC_1_19_2
|
||||
#if MC_VER < MC_1_19_2
|
||||
return new TextComponent(text);
|
||||
#else
|
||||
return Component.literal(text);
|
||||
@@ -35,7 +35,7 @@ public class GuiHelper
|
||||
|
||||
public static MutableComponent TextOrTranslatable(String text)
|
||||
{
|
||||
#if PRE_MC_1_19_2
|
||||
#if MC_VER < MC_1_19_2
|
||||
return new TextComponent(text);
|
||||
#else
|
||||
return Component.translatable(text);
|
||||
@@ -44,7 +44,7 @@ public class GuiHelper
|
||||
|
||||
public static MutableComponent Translatable(String text, Object... args)
|
||||
{
|
||||
#if PRE_MC_1_19_2
|
||||
#if MC_VER < MC_1_19_2
|
||||
return new TranslatableComponent(text, args);
|
||||
#else
|
||||
return Component.translatable(text, args);
|
||||
@@ -53,7 +53,7 @@ public class GuiHelper
|
||||
|
||||
public static void SetX(AbstractWidget w, int x)
|
||||
{
|
||||
#if PRE_MC_1_19_4
|
||||
#if MC_VER < MC_1_19_4
|
||||
w.x = x;
|
||||
#else
|
||||
w.setX(x);
|
||||
@@ -62,7 +62,7 @@ public class GuiHelper
|
||||
|
||||
public static void SetY(AbstractWidget w, int y)
|
||||
{
|
||||
#if PRE_MC_1_19_4
|
||||
#if MC_VER < MC_1_19_4
|
||||
w.y = y;
|
||||
#else
|
||||
w.setY(y);
|
||||
|
||||
+17
-9
@@ -4,7 +4,7 @@ import com.mojang.blaze3d.platform.Window;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.seibel.distanthorizons.core.config.gui.AbstractScreen;
|
||||
import net.minecraft.client.Minecraft;
|
||||
#if POST_MC_1_20_1
|
||||
#if MC_VER >= MC_1_20_1
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
#endif
|
||||
import net.minecraft.client.gui.components.ContainerObjectSelectionList;
|
||||
@@ -28,7 +28,7 @@ public class MinecraftScreen
|
||||
private AbstractScreen screen;
|
||||
|
||||
|
||||
#if PRE_MC_1_19_2
|
||||
#if MC_VER < MC_1_19_2
|
||||
public static net.minecraft.network.chat.TranslatableComponent translate(String str, Object... args)
|
||||
{
|
||||
return new net.minecraft.network.chat.TranslatableComponent(str, args);
|
||||
@@ -59,23 +59,27 @@ public class MinecraftScreen
|
||||
screen.scaledHeight = this.height;
|
||||
screen.init(); // Init our own config screen
|
||||
|
||||
this.list = new ConfigListWidget(this.minecraft, this.width, this.height, 0, this.height, 25); // Select the area to tint
|
||||
this.list = new ConfigListWidget(this.minecraft, this.width, this.height, 0, 0, 25); // Select the area to tint
|
||||
|
||||
#if MC_VER < MC_1_20_6 // no background is rendered in MC 1.20.6+
|
||||
if (this.minecraft != null && this.minecraft.level != null) // Check if in game
|
||||
this.list.setRenderBackground(false); // Disable from rendering
|
||||
#endif
|
||||
|
||||
this.addWidget(this.list); // Add the tint to the things to be rendered
|
||||
}
|
||||
|
||||
@Override
|
||||
#if PRE_MC_1_20_1
|
||||
#if MC_VER < MC_1_20_1
|
||||
public void render(PoseStack matrices, int mouseX, int mouseY, float delta)
|
||||
#else
|
||||
public void render(GuiGraphics matrices, int mouseX, int mouseY, float delta)
|
||||
#endif
|
||||
{
|
||||
#if MC_1_20_2
|
||||
this.renderBackground(matrices, mouseX, mouseY, delta); // Render background
|
||||
#else
|
||||
#if MC_VER < MC_1_20_2
|
||||
this.renderBackground(matrices); // Render background
|
||||
#else
|
||||
this.renderBackground(matrices, mouseX, mouseY, delta); // Render background
|
||||
#endif
|
||||
this.list.render(matrices, mouseX, mouseY, delta); // Renders the items in the render list (currently only used to tint background darker)
|
||||
|
||||
@@ -131,9 +135,13 @@ public class MinecraftScreen
|
||||
|
||||
public static class ConfigListWidget extends ContainerObjectSelectionList
|
||||
{
|
||||
public ConfigListWidget(Minecraft minecraftClient, int i, int j, int k, int l, int m)
|
||||
public ConfigListWidget(Minecraft minecraftClient, int canvasWidth, int canvasHeight, int topMargin, int botMargin, int itemSpacing)
|
||||
{
|
||||
super(minecraftClient, i, j, k, l, m);
|
||||
#if MC_VER < MC_1_20_4
|
||||
super(minecraftClient, canvasWidth, canvasHeight, topMargin, canvasHeight - botMargin, itemSpacing);
|
||||
#else
|
||||
super(minecraftClient, canvasWidth, canvasHeight - (topMargin + botMargin), topMargin, itemSpacing);
|
||||
#endif
|
||||
this.centerListVertically = false;
|
||||
}
|
||||
|
||||
|
||||
+76
-42
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -19,32 +19,38 @@
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.gui;
|
||||
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
#if MC_VER >= MC_1_17_1
|
||||
import net.minecraft.client.gui.components.Button;
|
||||
#endif
|
||||
|
||||
#if MC_VER < MC_1_17_1
|
||||
import net.minecraft.client.gui.components.ImageButton;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.Minecraft;
|
||||
#elif MC_VER < MC_1_20_1
|
||||
import net.minecraft.client.gui.components.ImageButton;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
#elif MC_VER < MC_1_20_2
|
||||
import net.minecraft.client.gui.components.ImageButton;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
#else
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Creates a button with a texture on it (and a background) that works with all mc versions
|
||||
*
|
||||
* @author coolGi
|
||||
* @version 2023-10-03
|
||||
*/
|
||||
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import net.minecraft.client.gui.components.AbstractButton;
|
||||
import net.minecraft.client.gui.components.Button;
|
||||
import net.minecraft.client.gui.components.ImageButton;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
#if POST_MC_1_17_1
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
#endif
|
||||
#if PRE_MC_1_20_1
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.Minecraft;
|
||||
#else
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
#endif
|
||||
|
||||
#if PRE_MC_1_20_2
|
||||
#if MC_VER < MC_1_20_2
|
||||
public class TexturedButtonWidget extends ImageButton
|
||||
#else
|
||||
public class TexturedButtonWidget extends Button
|
||||
@@ -52,25 +58,26 @@ public class TexturedButtonWidget extends Button
|
||||
{
|
||||
public final boolean renderBackground;
|
||||
|
||||
#if POST_MC_1_20_2
|
||||
#if MC_VER >= MC_1_20_2
|
||||
private final int u;
|
||||
private final int v;
|
||||
private final int hoveredVOffset;
|
||||
|
||||
private final ResourceLocation texture;
|
||||
private final ResourceLocation textureResourceLocation;
|
||||
|
||||
private final int textureWidth;
|
||||
private final int textureHeight;
|
||||
#endif
|
||||
|
||||
|
||||
public TexturedButtonWidget(int x, int y, int width, int height, int u, int v, int hoveredVOffset, ResourceLocation texture, int textureWidth, int textureHeight, OnPress pressAction, Component text) {
|
||||
this(x, y, width, height, u, v, hoveredVOffset, texture, textureWidth, textureHeight, pressAction, text, true);
|
||||
}
|
||||
public TexturedButtonWidget(int x, int y, int width, int height, int u, int v, int hoveredVOffset, ResourceLocation texture, int textureWidth, int textureHeight, OnPress pressAction, Component text, boolean renderBackground)
|
||||
public TexturedButtonWidget(int x, int y, int width, int height, int u, int v, int hoveredVOffset, ResourceLocation textureResourceLocation, int textureWidth, int textureHeight, OnPress pressAction, Component text)
|
||||
{
|
||||
#if PRE_MC_1_20_2
|
||||
super(x, y, width, height, u, v, hoveredVOffset, texture, textureWidth, textureHeight, pressAction, text);
|
||||
this(x, y, width, height, u, v, hoveredVOffset, textureResourceLocation, textureWidth, textureHeight, pressAction, text, true);
|
||||
}
|
||||
public TexturedButtonWidget(int x, int y, int width, int height, int u, int v, int hoveredVOffset, ResourceLocation textureResourceLocation, int textureWidth, int textureHeight, OnPress pressAction, Component text, boolean renderBackground)
|
||||
{
|
||||
#if MC_VER < MC_1_20_2
|
||||
super(x, y, width, height, u, v, hoveredVOffset, textureResourceLocation, textureWidth, textureHeight, pressAction, text);
|
||||
#else
|
||||
// We don't pass on the text option as otherwise it will render (we normally pass it for narration)
|
||||
// TODO: Find a fix for it
|
||||
@@ -80,7 +87,7 @@ public class TexturedButtonWidget extends Button
|
||||
this.v = v;
|
||||
this.hoveredVOffset = hoveredVOffset;
|
||||
|
||||
this.texture = texture;
|
||||
this.textureResourceLocation = textureResourceLocation;
|
||||
|
||||
this.textureWidth = textureWidth;
|
||||
this.textureHeight = textureHeight;
|
||||
@@ -89,13 +96,14 @@ public class TexturedButtonWidget extends Button
|
||||
this.renderBackground = renderBackground;
|
||||
}
|
||||
|
||||
#if PRE_MC_1_20_2
|
||||
#if PRE_MC_1_19_4
|
||||
#if MC_VER < MC_1_20_2
|
||||
#if MC_VER < MC_1_19_4
|
||||
@Override
|
||||
public void renderButton(PoseStack matrices, int mouseX, int mouseY, float delta) {
|
||||
public void renderButton(PoseStack matrices, int mouseX, int mouseY, float delta)
|
||||
{
|
||||
if (this.renderBackground) // Renders the background of the button
|
||||
{
|
||||
#if PRE_MC_1_17_1
|
||||
#if MC_VER < MC_1_17_1
|
||||
Minecraft.getInstance().getTextureManager().bind(WIDGETS_LOCATION);
|
||||
RenderSystem.color4f(1.0F, 1.0F, 1.0F, this.alpha);
|
||||
#else
|
||||
@@ -108,7 +116,7 @@ public class TexturedButtonWidget extends Button
|
||||
RenderSystem.enableBlend();
|
||||
RenderSystem.defaultBlendFunc();
|
||||
RenderSystem.enableDepthTest();
|
||||
#if PRE_MC_1_19_4
|
||||
#if MC_VER < MC_1_19_4
|
||||
this.blit(matrices, this.x, this.y, 0, 46 + i * 20, this.width / 2, this.height);
|
||||
this.blit(matrices, this.x + this.width / 2, this.y, 200 - this.width / 2, 46 + i * 20, this.width / 2, this.height);
|
||||
#else
|
||||
@@ -119,8 +127,9 @@ public class TexturedButtonWidget extends Button
|
||||
|
||||
super.renderButton(matrices, mouseX, mouseY, delta);
|
||||
}
|
||||
|
||||
#else
|
||||
#if PRE_MC_1_20_1
|
||||
#if MC_VER < MC_1_20_1
|
||||
@Override
|
||||
public void renderWidget(PoseStack matrices, int mouseX, int mouseY, float delta)
|
||||
{
|
||||
@@ -138,7 +147,7 @@ public class TexturedButtonWidget extends Button
|
||||
if (!this.active) i = 0;
|
||||
else if (this.isHovered) i = 2;
|
||||
|
||||
#if PRE_MC_1_20_1
|
||||
#if MC_VER < MC_1_20_1
|
||||
RenderSystem.enableBlend();
|
||||
RenderSystem.defaultBlendFunc();
|
||||
RenderSystem.enableDepthTest();
|
||||
@@ -154,24 +163,49 @@ public class TexturedButtonWidget extends Button
|
||||
super.renderWidget(matrices, mouseX, mouseY, delta);
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
@Override
|
||||
public void renderWidget(GuiGraphics matrices, int mouseX, int mouseY, float delta)
|
||||
{
|
||||
if (this.renderBackground)
|
||||
{
|
||||
//RenderSystem.enableBlend();
|
||||
//RenderSystem.enableDepthTest();
|
||||
#if MC_VER < MC_1_21_3
|
||||
matrices.blitSprite(SPRITES.get(this.active, this.isHoveredOrFocused()), this.getX(), this.getY(), this.getWidth(), this.getHeight());
|
||||
#else
|
||||
matrices.blitSprite(
|
||||
RenderType::guiTextured,
|
||||
SPRITES.get(this.active, this.isHoveredOrFocused()),
|
||||
this.getX(), this.getY(),
|
||||
this.getWidth(), this.getHeight());
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Renders the sprite
|
||||
int i = 0;
|
||||
if (!this.active) i = 2;
|
||||
else if (this.isHovered) i = 1;
|
||||
if (!this.active)
|
||||
{
|
||||
i = 2;
|
||||
}
|
||||
else if (this.isHovered)
|
||||
{
|
||||
i = 1;
|
||||
}
|
||||
|
||||
matrices.blit(this.texture, this.getX(), this.getY(), this.u, this.v + (this.hoveredVOffset * i), this.width, this.height, this.textureWidth, this.textureHeight);
|
||||
#if MC_VER < MC_1_21_3
|
||||
matrices.blit(this.textureResourceLocation, this.getX(), this.getY(), this.u, this.v + (this.hoveredVOffset * i), this.width, this.height, this.textureWidth, this.textureHeight);
|
||||
#else
|
||||
matrices.blit(
|
||||
RenderType::guiTextured,
|
||||
this.textureResourceLocation,
|
||||
this.getX(), this.getY(),
|
||||
this.u, this.v + (this.hoveredVOffset * i),
|
||||
this.width, this.height,
|
||||
this.textureWidth, this.textureHeight);
|
||||
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
+74
-36
@@ -1,8 +1,8 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.gui.updater;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.seibel.distanthorizons.common.wrappers.gui.DhScreen;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
|
||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||
import com.seibel.distanthorizons.core.jar.installer.MarkdownFormatter;
|
||||
@@ -14,13 +14,15 @@ import net.minecraft.client.gui.components.ContainerObjectSelectionList;
|
||||
import net.minecraft.client.gui.components.events.GuiEventListener;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
#if POST_MC_1_17_1
|
||||
#if MC_VER >= MC_1_17_1
|
||||
import net.minecraft.client.gui.narration.NarratableEntry;
|
||||
#endif
|
||||
|
||||
#if PRE_MC_1_20_1
|
||||
#if MC_VER < MC_1_20_1
|
||||
import net.minecraft.client.gui.GuiComponent;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
#else
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
#endif
|
||||
@@ -39,6 +41,9 @@ import java.util.*;
|
||||
// TODO: Make this
|
||||
public class ChangelogScreen extends DhScreen
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
|
||||
private Screen parent;
|
||||
private String versionID;
|
||||
private List<String> changelog;
|
||||
@@ -51,24 +56,32 @@ public class ChangelogScreen extends DhScreen
|
||||
this(parent, null);
|
||||
|
||||
if (!ModrinthGetter.initted) // Make sure the modrinth stuff is initted
|
||||
{
|
||||
ModrinthGetter.init();
|
||||
}
|
||||
if (!ModrinthGetter.initted) // If its not initted, then this isnt usable
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ModrinthGetter.mcVersions.contains(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
String versionID = ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion());
|
||||
if (versionID == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
setupChangelog(versionID);
|
||||
usable = true;
|
||||
this.setupChangelog(versionID);
|
||||
this.usable = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
LOGGER.error("failed to setup changelog, error: ["+e.getMessage()+"].", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,11 +93,13 @@ public class ChangelogScreen extends DhScreen
|
||||
|
||||
|
||||
if (versionID == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
setupChangelog(versionID);
|
||||
usable = true;
|
||||
this.setupChangelog(versionID);
|
||||
this.usable = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -101,11 +116,17 @@ public class ChangelogScreen extends DhScreen
|
||||
this.changelog.add("");
|
||||
this.changelog.add("");
|
||||
|
||||
String changelog = ModrinthGetter.changeLogs.get(versionID);
|
||||
if (changelog == null)
|
||||
{
|
||||
// in case something goes wrong this will prevent null pointers
|
||||
changelog = "";
|
||||
}
|
||||
|
||||
// Get the release changelog and split it by the new lines
|
||||
String[] unwrappedChangelog = // Arrays.asList could be used if a list object is desired here vs List.of which is only available for Java 9+
|
||||
new MarkdownFormatter.MinecraftFormat().convertTo( // This formats markdown to minecraft's "§" characters
|
||||
ModrinthGetter.changeLogs.get(versionID)
|
||||
).split("\\n");
|
||||
// This formats markdown to minecraft's "§" charactersnew MarkdownFormatter.MinecraftFormat().convertTo(
|
||||
new MarkdownFormatter.MinecraftFormat().convertTo(changelog).split("\\n");
|
||||
// Makes the words wrap around to not go off the screen
|
||||
for (String str : unwrappedChangelog)
|
||||
{
|
||||
@@ -123,8 +144,10 @@ public class ChangelogScreen extends DhScreen
|
||||
protected void init()
|
||||
{
|
||||
super.init();
|
||||
if (!usable)
|
||||
if (!this.usable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this.addBtn( // Close
|
||||
@@ -134,62 +157,77 @@ public class ChangelogScreen extends DhScreen
|
||||
);
|
||||
|
||||
|
||||
this.changelogArea = new TextArea(this.minecraft, this.width * 2, this.height, 32, this.height - 32, 10);
|
||||
for (int i = 0; i < changelog.size(); i++)
|
||||
this.changelogArea = new TextArea(this.minecraft, this.width * 2, this.height, 32, 32, 10);
|
||||
for (int i = 0; i < this.changelog.size(); i++)
|
||||
{
|
||||
this.changelogArea.addButton(TextOrLiteral(changelog.get(i)));
|
||||
this.changelogArea.addButton(TextOrLiteral(this.changelog.get(i)));
|
||||
// drawString(matrices, this.font, changelog.get(i), this.width / 2 - 175, this.height / 2 - 100 + i*10, 0xFFFFFF);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
#if PRE_MC_1_20_1
|
||||
#if MC_VER < MC_1_20_1
|
||||
public void render(PoseStack matrices, int mouseX, int mouseY, float delta)
|
||||
#else
|
||||
public void render(GuiGraphics matrices, int mouseX, int mouseY, float delta)
|
||||
#endif
|
||||
{
|
||||
#if MC_1_20_2
|
||||
this.renderBackground(matrices, mouseX, mouseY, delta); // Render background
|
||||
#else
|
||||
#if MC_VER < MC_1_20_2
|
||||
this.renderBackground(matrices); // Render background
|
||||
#else
|
||||
this.renderBackground(matrices, mouseX, mouseY, delta); // Render background
|
||||
#endif
|
||||
if (!usable)
|
||||
if (!this.usable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int maxScroll;
|
||||
#if MC_VER <= MC_1_21_3
|
||||
maxScroll = this.changelogArea.getMaxScroll();
|
||||
#else
|
||||
maxScroll = this.changelogArea.maxScrollAmount();
|
||||
#endif
|
||||
|
||||
// Set the scroll position to the mouse height relative to the screen
|
||||
// This is a bit of a hack as we cannot scroll on this area
|
||||
double scrollAmount = ((double) mouseY) / ((double) this.height) * 1.1 * this.changelogArea.getMaxScroll();
|
||||
#if MC_1_16_5 || MC_1_17_1
|
||||
double scrollAmount = ((double) mouseY) / ((double) this.height) * 1.1 * maxScroll;
|
||||
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
this.changelogArea.setScrollAmount(scrollAmount);
|
||||
#else
|
||||
#elif MC_VER <= MC_1_21_3
|
||||
this.changelogArea.scrollAmount = scrollAmount;
|
||||
#else
|
||||
this.changelogArea.setScrollAmount(scrollAmount);
|
||||
#endif
|
||||
|
||||
|
||||
this.changelogArea.render(matrices, mouseX, mouseY, delta); // Render the changelog
|
||||
|
||||
// render order matters, otherwise on 1.20.6+ the blurred background will render on top of the text
|
||||
super.render(matrices, mouseX, mouseY, delta); // Render the buttons
|
||||
|
||||
DhDrawCenteredString(matrices, font, title, width / 2, 15, 0xFFFFFF); // Render title
|
||||
this.changelogArea.render(matrices, mouseX, mouseY, delta); // Render the changelog
|
||||
this.DhDrawCenteredString(matrices, this.font, this.title, this.width / 2, 15, 0xFFFFFF); // Render title
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose()
|
||||
{
|
||||
Objects.requireNonNull(minecraft).setScreen(this.parent); // Goto the parent screen
|
||||
Objects.requireNonNull(this.minecraft).setScreen(this.parent); // Goto the parent screen
|
||||
}
|
||||
|
||||
public static class TextArea extends ContainerObjectSelectionList<ButtonEntry>
|
||||
{
|
||||
Font textRenderer;
|
||||
|
||||
public TextArea(Minecraft minecraftClient, int i, int j, int k, int l, int m)
|
||||
public TextArea(Minecraft minecraftClient, int canvasWidth, int canvasHeight, int topMargin, int botMargin, int itemSpacing)
|
||||
{
|
||||
super(minecraftClient, i, j, k, l, m);
|
||||
#if MC_VER < MC_1_20_4
|
||||
super(minecraftClient, canvasWidth, canvasHeight, topMargin, canvasHeight - botMargin, itemSpacing);
|
||||
#else
|
||||
super(minecraftClient, canvasWidth, canvasHeight - (topMargin + botMargin), topMargin, itemSpacing);
|
||||
#endif
|
||||
this.centerListVertically = false;
|
||||
textRenderer = minecraftClient.font;
|
||||
this.textRenderer = minecraftClient.font;
|
||||
}
|
||||
|
||||
public void addButton(Component text)
|
||||
@@ -221,7 +259,7 @@ public class ChangelogScreen extends DhScreen
|
||||
return new ButtonEntry(text);
|
||||
}
|
||||
|
||||
#if PRE_MC_1_20_1
|
||||
#if MC_VER < MC_1_20_1
|
||||
@Override
|
||||
public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta)
|
||||
{
|
||||
@@ -231,20 +269,20 @@ public class ChangelogScreen extends DhScreen
|
||||
@Override
|
||||
public void render(GuiGraphics matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta)
|
||||
{
|
||||
matrices.drawString(textRenderer, text, 12, y + 5, 0xFFFFFF);
|
||||
matrices.drawString(textRenderer, this.text, 12, y + 5, 0xFFFFFF);
|
||||
}
|
||||
#endif
|
||||
|
||||
@Override
|
||||
public List<? extends GuiEventListener> children()
|
||||
{
|
||||
return children;
|
||||
return this.children;
|
||||
}
|
||||
#if POST_MC_1_17_1
|
||||
#if MC_VER >= MC_1_17_1
|
||||
@Override
|
||||
public List<? extends NarratableEntry> narratables()
|
||||
{
|
||||
return children;
|
||||
return this.children;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
+51
-38
@@ -1,24 +1,22 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.gui.updater;
|
||||
|
||||
import com.mojang.blaze3d.platform.NativeImage;
|
||||
import com.seibel.distanthorizons.api.enums.config.EUpdateBranch;
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiUpdateBranch;
|
||||
import com.seibel.distanthorizons.common.wrappers.gui.DhScreen;
|
||||
import com.seibel.distanthorizons.common.wrappers.gui.TexturedButtonWidget;
|
||||
import com.seibel.distanthorizons.core.jar.ModJarInfo;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.jar.JarUtils;
|
||||
import com.seibel.distanthorizons.core.jar.installer.ModrinthGetter;
|
||||
import com.seibel.distanthorizons.core.jar.updater.SelfUpdater;
|
||||
import net.minecraft.client.Minecraft;
|
||||
#if POST_MC_1_20_1
|
||||
#if MC_VER >= MC_1_20_1
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
#else
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
#endif
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.client.renderer.texture.DynamicTexture;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import static com.seibel.distanthorizons.common.wrappers.gui.GuiHelper.*;
|
||||
|
||||
@@ -33,6 +31,9 @@ import java.util.*;
|
||||
// and also maybe add this suggestion https://discord.com/channels/881614130614767666/1035863487110467625/1035949054485594192
|
||||
public class UpdateModScreen extends DhScreen
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
|
||||
private Screen parent;
|
||||
private String newVersionID;
|
||||
|
||||
@@ -40,22 +41,31 @@ public class UpdateModScreen extends DhScreen
|
||||
private String nextVer;
|
||||
|
||||
|
||||
public UpdateModScreen(Screen parent, String newVersionID)
|
||||
public UpdateModScreen(Screen parent, String newVersionID) throws IllegalArgumentException
|
||||
{
|
||||
super(Translatable(ModInfo.ID + ".updater.title"));
|
||||
this.parent = parent;
|
||||
this.newVersionID = newVersionID;
|
||||
|
||||
switch (Config.Client.Advanced.AutoUpdater.updateBranch.get()) {
|
||||
case STABLE:
|
||||
currentVer = ModInfo.VERSION;
|
||||
nextVer = ModrinthGetter.releaseNames.get(this.newVersionID);
|
||||
break;
|
||||
case NIGHTLY:
|
||||
currentVer = ModJarInfo.Git_Commit.substring(0,7);
|
||||
nextVer = this.newVersionID.substring(0,7);
|
||||
break;
|
||||
|
||||
EDhApiUpdateBranch updateBranch = EDhApiUpdateBranch.convertAutoToStableOrNightly(Config.Client.Advanced.AutoUpdater.updateBranch.get());
|
||||
if (updateBranch == EDhApiUpdateBranch.STABLE)
|
||||
{
|
||||
this.currentVer = ModInfo.VERSION;
|
||||
this.nextVer = ModrinthGetter.releaseNames.get(this.newVersionID);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.currentVer = ModJarInfo.Git_Commit.substring(0,7);
|
||||
this.nextVer = this.newVersionID.substring(0,7);
|
||||
}
|
||||
|
||||
// done to prevent trying to update to "null"
|
||||
// (this can happen if no versions are available to check/download from modrinth/gitlab)
|
||||
if (this.nextVer == null)
|
||||
{
|
||||
throw new IllegalArgumentException("No new version found with the ID ["+newVersionID+"].");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -66,25 +76,24 @@ public class UpdateModScreen extends DhScreen
|
||||
|
||||
try
|
||||
{
|
||||
// We cannot get assets from the root of the mod so we use this hack
|
||||
// TODO: Load the icon.png and logo.png in the mod initialise rather than here
|
||||
ResourceLocation logoLocation = new ResourceLocation(ModInfo.ID, "logo.png");
|
||||
Minecraft.getInstance().getTextureManager().register(
|
||||
logoLocation,
|
||||
new DynamicTexture(NativeImage.read(JarUtils.accessFile("logo.png")))
|
||||
);
|
||||
|
||||
|
||||
// Logo image
|
||||
this.addBtn(new TexturedButtonWidget(
|
||||
// Where the button is on the screen
|
||||
this.width / 2 - 65, this.height / 2 - 110,
|
||||
this.width / 2 - 95, this.height / 2 - 110,
|
||||
// Width and height of the button
|
||||
130, 65,
|
||||
195, 65,
|
||||
// Offset
|
||||
0, 0,
|
||||
// Some textuary stuff
|
||||
0, logoLocation, 130, 65,
|
||||
0,
|
||||
#if MC_VER < MC_1_21_1
|
||||
new ResourceLocation(ModInfo.ID, "logo.png"),
|
||||
#else
|
||||
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "logo.png"),
|
||||
#endif
|
||||
195, 65,
|
||||
// Create the button and tell it where to go
|
||||
// For now it goes to the client option by default
|
||||
(buttonWidget) -> System.out.println("Nice, you found an easter egg :)"), // TODO: Add a proper easter egg to pressing the logo (maybe with confetti)
|
||||
@@ -96,10 +105,10 @@ public class UpdateModScreen extends DhScreen
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
LOGGER.error("Failed to setup update mod screen, error: ["+e.getMessage()+"].", e);
|
||||
}
|
||||
|
||||
if (Config.Client.Advanced.AutoUpdater.updateBranch.get() == EUpdateBranch.STABLE)
|
||||
if (!ModInfo.IS_DEV_BUILD)
|
||||
{
|
||||
this.addBtn(new TexturedButtonWidget(
|
||||
// Where the button is on the screen
|
||||
@@ -109,7 +118,13 @@ public class UpdateModScreen extends DhScreen
|
||||
// Offset
|
||||
0, 0,
|
||||
// Some textuary stuff
|
||||
0, new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"), 20, 20,
|
||||
0,
|
||||
#if MC_VER < MC_1_21_1
|
||||
new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"),
|
||||
#else
|
||||
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"),
|
||||
#endif
|
||||
20, 20,
|
||||
// Create the button and tell it where to go
|
||||
(buttonWidget) -> Objects.requireNonNull(minecraft).setScreen(new ChangelogScreen(this, this.newVersionID)), // TODO: Add a proper easter egg to pressing the logo (maybe with confetti)
|
||||
// Add a title to the button
|
||||
@@ -146,29 +161,27 @@ public class UpdateModScreen extends DhScreen
|
||||
}
|
||||
|
||||
@Override
|
||||
#if PRE_MC_1_20_1
|
||||
#if MC_VER < MC_1_20_1
|
||||
public void render(PoseStack matrices, int mouseX, int mouseY, float delta)
|
||||
#else
|
||||
public void render(GuiGraphics matrices, int mouseX, int mouseY, float delta)
|
||||
#endif
|
||||
{
|
||||
#if MC_1_20_2
|
||||
this.renderBackground(matrices, mouseX, mouseY, delta); // Render background
|
||||
#else
|
||||
#if MC_VER < MC_1_20_2
|
||||
this.renderBackground(matrices); // Render background
|
||||
#else
|
||||
this.renderBackground(matrices, mouseX, mouseY, delta); // Render background
|
||||
#endif
|
||||
|
||||
// TODO: add the tooltips for the buttons
|
||||
super.render(matrices, mouseX, mouseY, delta); // Render the buttons
|
||||
// TODO: Add tooltips
|
||||
|
||||
// Render the text's
|
||||
DhDrawCenteredString(matrices, this.font, Translatable(ModInfo.ID + ".updater.text1"), this.width / 2, this.height / 2 - 35, 0xFFFFFF);
|
||||
DhDrawCenteredString(matrices, this.font,
|
||||
Translatable(ModInfo.ID + ".updater.text2", currentVer, nextVer),
|
||||
this.width / 2, this.height / 2 - 20, 0x52FD52);
|
||||
|
||||
// TODO: add the tooltips for the buttons
|
||||
super.render(matrices, mouseX, mouseY, delta); // Render the buttons
|
||||
|
||||
// TODO: Add tooltips
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+18
-17
@@ -1,21 +1,22 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.level;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
|
||||
import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
|
||||
import java.util.Objects;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class KeyedClientLevelManager implements IKeyedClientLevelManager
|
||||
{
|
||||
public static final KeyedClientLevelManager INSTANCE = new KeyedClientLevelManager();
|
||||
|
||||
/** This is set and managed by the ClientApi for servers with support for DH. */
|
||||
private IServerKeyedClientLevel overrideWrapper = null;
|
||||
private boolean useOverrideWrapper = false;
|
||||
@Nullable
|
||||
private IServerKeyedClientLevel serverKeyedLevel = null;
|
||||
/** Allows to keep level manager enabled between loading different keyed levels */
|
||||
private boolean enabled = false;
|
||||
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
@@ -31,24 +32,24 @@ public class KeyedClientLevelManager implements IKeyedClientLevelManager
|
||||
//======================//
|
||||
|
||||
@Override
|
||||
public void setServerKeyedLevel(IServerKeyedClientLevel clientLevel) { this.overrideWrapper = clientLevel; }
|
||||
@Override
|
||||
public IServerKeyedClientLevel getOverrideWrapper() { return this.overrideWrapper; }
|
||||
@Nullable
|
||||
public IServerKeyedClientLevel getServerKeyedLevel() { return this.serverKeyedLevel; }
|
||||
|
||||
@Override
|
||||
public IServerKeyedClientLevel getServerKeyedLevel(ILevelWrapper level, String serverLevelKey)
|
||||
public IServerKeyedClientLevel setServerKeyedLevel(IClientLevelWrapper clientLevel, String levelKey)
|
||||
{
|
||||
Objects.requireNonNull(level);
|
||||
Objects.requireNonNull(serverLevelKey);
|
||||
return new ServerKeyedClientLevel((ClientLevel) level.getWrappedMcObject(), serverLevelKey);
|
||||
IServerKeyedClientLevel keyedLevel = new ServerKeyedClientLevel((ClientLevel) clientLevel.getWrappedMcObject(), levelKey);
|
||||
this.serverKeyedLevel = keyedLevel;
|
||||
this.enabled = true;
|
||||
return keyedLevel;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setUseOverrideWrapper(boolean useOverrideWrapper) { this.useOverrideWrapper = useOverrideWrapper; }
|
||||
public void clearKeyedLevel() { this.serverKeyedLevel = null; }
|
||||
@Override
|
||||
public boolean getUseOverrideWrapper() { return this.useOverrideWrapper; }
|
||||
|
||||
public boolean isEnabled() { return this.enabled; }
|
||||
@Override
|
||||
public void disable() { this.enabled = false; }
|
||||
|
||||
|
||||
}
|
||||
|
||||
+9
@@ -2,6 +2,8 @@ package com.seibel.distanthorizons.common.wrappers.level;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.util.StringUtil;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
|
||||
public class ServerKeyedClientLevel extends ClientLevelWrapper implements IServerKeyedClientLevel
|
||||
@@ -9,13 +11,20 @@ public class ServerKeyedClientLevel extends ClientLevelWrapper implements IServe
|
||||
/** A unique identifier (generally the level's name) for differentiating multiverse levels */
|
||||
private final String serverLevelKey;
|
||||
|
||||
|
||||
|
||||
public ServerKeyedClientLevel(ClientLevel level, String serverLevelKey)
|
||||
{
|
||||
super(level);
|
||||
this.serverLevelKey = serverLevelKey;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String getServerLevelKey() { return this.serverLevelKey; }
|
||||
|
||||
@Override
|
||||
public String getDhIdentifier() { return this.getServerLevelKey(); }
|
||||
|
||||
}
|
||||
|
||||
+140
-83
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -22,15 +22,17 @@ package com.seibel.distanthorizons.common.wrappers.minecraft;
|
||||
import java.io.File;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.mojang.blaze3d.platform.NativeImage;
|
||||
import com.seibel.distanthorizons.api.enums.config.ELodShading;
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiLodShading;
|
||||
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
|
||||
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.enums.EDhDirection;
|
||||
import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
@@ -38,37 +40,43 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftCli
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
|
||||
import net.minecraft.CrashReport;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.multiplayer.ServerData;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.client.resources.model.ModelManager;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
#if PRE_MC_1_19_2
|
||||
#if MC_VER < MC_1_19_2
|
||||
import net.minecraft.network.chat.TextComponent;
|
||||
#endif
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.util.profiling.ProfilerFiller;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
#if MC_VER < MC_1_21_3
|
||||
#else
|
||||
import net.minecraft.util.profiling.Profiler;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A singleton that wraps the Minecraft object.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 3-5-2022
|
||||
*/
|
||||
//@Environment(EnvType.CLIENT)
|
||||
public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecraftSharedWrapper
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
|
||||
private static final Minecraft MINECRAFT = Minecraft.getInstance();
|
||||
|
||||
public static final MinecraftClientWrapper INSTANCE = new MinecraftClientWrapper();
|
||||
|
||||
public final Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
|
||||
/**
|
||||
* The lightmap for the current:
|
||||
@@ -99,10 +107,7 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
|
||||
* This doesn't affect OpenGL objects in any way.
|
||||
*/
|
||||
@Override
|
||||
public void clearFrameObjectCache()
|
||||
{
|
||||
lightMap = null;
|
||||
}
|
||||
public void clearFrameObjectCache() { this.lightMap = null; }
|
||||
|
||||
|
||||
|
||||
@@ -113,22 +118,22 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
|
||||
@Override
|
||||
public float getShade(EDhDirection lodDirection)
|
||||
{
|
||||
ELodShading lodShading = Config.Client.Advanced.Graphics.AdvancedGraphics.lodShading.get();
|
||||
EDhApiLodShading lodShading = Config.Client.Advanced.Graphics.Quality.lodShading.get();
|
||||
switch (lodShading)
|
||||
{
|
||||
default:
|
||||
case MINECRAFT:
|
||||
if (this.mc.level != null)
|
||||
case AUTO:
|
||||
if (MINECRAFT.level != null)
|
||||
{
|
||||
Direction mcDir = McObjectConverter.Convert(lodDirection);
|
||||
return this.mc.level.getShade(mcDir, true);
|
||||
return MINECRAFT.level.getShade(mcDir, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
case OLD_LIGHTING:
|
||||
case ENABLED:
|
||||
switch (lodDirection)
|
||||
{
|
||||
case DOWN:
|
||||
@@ -144,100 +149,123 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
|
||||
return 0.6F;
|
||||
}
|
||||
|
||||
case NONE:
|
||||
case DISABLED:
|
||||
return 1.0F;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSinglePlayerServer() { return mc.hasSingleplayerServer(); }
|
||||
public boolean hasSinglePlayerServer() { return MINECRAFT.hasSingleplayerServer(); }
|
||||
@Override
|
||||
public boolean clientConnectedToDedicatedServer() { return mc.getCurrentServer() != null && !this.hasSinglePlayerServer(); }
|
||||
public boolean clientConnectedToDedicatedServer() { return MINECRAFT.getCurrentServer() != null && !this.hasSinglePlayerServer(); }
|
||||
@Override
|
||||
public boolean connectedToReplay() { return !MINECRAFT.hasSingleplayerServer() && MINECRAFT.getCurrentServer() == null; }
|
||||
|
||||
@Override
|
||||
public String getCurrentServerName() { return mc.getCurrentServer().name; }
|
||||
public String getCurrentServerName()
|
||||
{
|
||||
if (this.connectedToReplay())
|
||||
{
|
||||
return ClientOnlySaveStructure.REPLAY_SERVER_FOLDER_NAME;
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerData server = MINECRAFT.getCurrentServer();
|
||||
return (server != null) ? server.name : "NULL";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCurrentServerIp() { return mc.getCurrentServer().ip; }
|
||||
public String getCurrentServerIp()
|
||||
{
|
||||
if (this.connectedToReplay())
|
||||
{
|
||||
return "";
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerData server = MINECRAFT.getCurrentServer();
|
||||
return (server != null) ? server.ip : "NA";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCurrentServerVersion()
|
||||
{
|
||||
return mc.getCurrentServer().version.getString();
|
||||
ServerData server = MINECRAFT.getCurrentServer();
|
||||
return (server != null) ? server.version.getString() : "UNKOWN";
|
||||
}
|
||||
|
||||
//=============//
|
||||
// Simple gets //
|
||||
//=============//
|
||||
|
||||
public LocalPlayer getPlayer()
|
||||
{
|
||||
return mc.player;
|
||||
}
|
||||
public LocalPlayer getPlayer() { return MINECRAFT.player; }
|
||||
|
||||
@Override
|
||||
public boolean playerExists()
|
||||
{
|
||||
return mc.player != null;
|
||||
}
|
||||
public boolean playerExists() { return MINECRAFT.player != null; }
|
||||
|
||||
@Override
|
||||
public UUID getPlayerUUID()
|
||||
{
|
||||
return getPlayer().getUUID();
|
||||
}
|
||||
public UUID getPlayerUUID() { return this.getPlayer().getUUID(); }
|
||||
|
||||
@Override
|
||||
public String getUsername() { return MINECRAFT.getUser().getName(); }
|
||||
|
||||
@Override
|
||||
public DhBlockPos getPlayerBlockPos()
|
||||
{
|
||||
BlockPos playerPos = getPlayer().blockPosition();
|
||||
BlockPos playerPos = this.getPlayer().blockPosition();
|
||||
return new DhBlockPos(playerPos.getX(), playerPos.getY(), playerPos.getZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DhChunkPos getPlayerChunkPos()
|
||||
{
|
||||
#if PRE_MC_1_17_1
|
||||
ChunkPos playerPos = new ChunkPos(getPlayer().blockPosition());
|
||||
#if MC_VER < MC_1_17_1
|
||||
ChunkPos playerPos = new ChunkPos(this.getPlayer().blockPosition());
|
||||
#else
|
||||
ChunkPos playerPos = getPlayer().chunkPosition();
|
||||
ChunkPos playerPos = this.getPlayer().chunkPosition();
|
||||
#endif
|
||||
return new DhChunkPos(playerPos.x, playerPos.z);
|
||||
}
|
||||
|
||||
public ModelManager getModelManager()
|
||||
{
|
||||
return mc.getModelManager();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IClientLevelWrapper getWrappedClientLevel()
|
||||
public IClientLevelWrapper getWrappedClientLevel() { return this.getWrappedClientLevel(false); }
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public IClientLevelWrapper getWrappedClientLevel(boolean bypassLevelKeyManager)
|
||||
{
|
||||
if (this.mc.level == null)
|
||||
ClientLevel level = MINECRAFT.level;
|
||||
if (level == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return ClientLevelWrapper.getWrapperIgnoringOverride(this.mc.level);
|
||||
}
|
||||
|
||||
/** Please move over to getInstallationDirectory() */
|
||||
@Deprecated
|
||||
@Override
|
||||
public File getGameDirectory()
|
||||
{
|
||||
return getInstallationDirectory();
|
||||
return ClientLevelWrapper.getWrapper(level, bypassLevelKeyManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IProfilerWrapper getProfiler()
|
||||
{
|
||||
if (profilerWrapper == null)
|
||||
profilerWrapper = new ProfilerWrapper(mc.getProfiler());
|
||||
else if (mc.getProfiler() != profilerWrapper.profiler)
|
||||
profilerWrapper.profiler = mc.getProfiler();
|
||||
return profilerWrapper;
|
||||
ProfilerFiller profiler;
|
||||
#if MC_VER < MC_1_21_3
|
||||
profiler = MINECRAFT.getProfiler();
|
||||
#else
|
||||
profiler = Profiler.get();
|
||||
#endif
|
||||
|
||||
if (this.profilerWrapper == null)
|
||||
{
|
||||
this.profilerWrapper = new ProfilerWrapper(profiler);
|
||||
}
|
||||
else if (profiler != this.profilerWrapper.profiler)
|
||||
{
|
||||
this.profilerWrapper.profiler = profiler;
|
||||
}
|
||||
|
||||
return this.profilerWrapper;
|
||||
}
|
||||
|
||||
/** Returns all worlds available to the server */
|
||||
@@ -246,7 +274,7 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
|
||||
{
|
||||
ArrayList<ILevelWrapper> worlds = new ArrayList<ILevelWrapper>();
|
||||
|
||||
Iterable<ServerLevel> serverWorlds = mc.getSingleplayerServer().getAllLevels();
|
||||
Iterable<ServerLevel> serverWorlds = MINECRAFT.getSingleplayerServer().getAllLevels();
|
||||
for (ServerLevel world : serverWorlds)
|
||||
{
|
||||
worlds.add(ServerLevelWrapper.getWrapper(world));
|
||||
@@ -260,12 +288,32 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
|
||||
@Override
|
||||
public void sendChatMessage(String string)
|
||||
{
|
||||
LocalPlayer p = getPlayer();
|
||||
if (p == null) return;
|
||||
#if PRE_MC_1_19_2
|
||||
p.sendMessage(new TextComponent(string), getPlayer().getUUID());
|
||||
LocalPlayer player = this.getPlayer();
|
||||
if (player == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if MC_VER < MC_1_19_2
|
||||
player.sendMessage(new TextComponent(string), getPlayer().getUUID());
|
||||
#else
|
||||
p.sendSystemMessage(net.minecraft.network.chat.Component.translatable(string));
|
||||
player.displayClientMessage(net.minecraft.network.chat.Component.translatable(string), /*isOverlay*/false);
|
||||
#endif
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendOverlayMessage(String string)
|
||||
{
|
||||
LocalPlayer player = this.getPlayer();
|
||||
if (player == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if MC_VER < MC_1_19_2
|
||||
player.displayClientMessage(new TextComponent(string), /*isOverlay*/true);
|
||||
#else
|
||||
player.displayClientMessage(net.minecraft.network.chat.Component.translatable(string), /*isOverlay*/true);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -282,28 +330,37 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
|
||||
{
|
||||
LOGGER.error(ModInfo.READABLE_NAME + " had the following error: [" + errorMessage + "]. Crashing Minecraft...", exception);
|
||||
CrashReport report = new CrashReport(errorMessage, exception);
|
||||
#if MC_VER < MC_1_20_4
|
||||
Minecraft.crash(report);
|
||||
#else
|
||||
Minecraft.getInstance().delayCrash(report);
|
||||
#endif
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getOptionsObject()
|
||||
public Object getOptionsObject() { return MINECRAFT.options; }
|
||||
|
||||
@Override
|
||||
public boolean isDedicatedServer() { return false; }
|
||||
|
||||
@Override
|
||||
public File getInstallationDirectory() { return MINECRAFT.gameDirectory; }
|
||||
|
||||
@Override
|
||||
public void executeOnRenderThread(Runnable runnable) { MINECRAFT.execute(runnable); }
|
||||
|
||||
@Override
|
||||
public int getPlayerCount()
|
||||
{
|
||||
return mc.options;
|
||||
// can be null if the server hasn't finished booting up yet
|
||||
if (MINECRAFT.getSingleplayerServer() == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return MINECRAFT.getSingleplayerServer().getPlayerCount();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDedicatedServer()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getInstallationDirectory()
|
||||
{
|
||||
return mc.gameDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeOnRenderThread(Runnable runnable) { this.mc.execute(runnable); }
|
||||
|
||||
}
|
||||
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.minecraft;
|
||||
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
|
||||
import net.minecraft.server.dedicated.DedicatedServer;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
//@Environment(EnvType.SERVER)
|
||||
public class MinecraftDedicatedServerWrapper implements IMinecraftSharedWrapper
|
||||
{
|
||||
public static final MinecraftDedicatedServerWrapper INSTANCE = new MinecraftDedicatedServerWrapper();
|
||||
private MinecraftDedicatedServerWrapper() { }
|
||||
public DedicatedServer dedicatedServer = null;
|
||||
@Override
|
||||
public boolean isDedicatedServer()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public File getInstallationDirectory()
|
||||
{
|
||||
if (dedicatedServer == null)
|
||||
throw new IllegalStateException("Trying to get Installation Direction before Dedicated server complete initialization!");
|
||||
return dedicatedServer.getServerDirectory();
|
||||
}
|
||||
|
||||
}
|
||||
+237
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.minecraft;
|
||||
|
||||
import com.mojang.blaze3d.platform.GlStateManager;
|
||||
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
|
||||
|
||||
/**
|
||||
* <b>Why does DH often call GL methods twice? </b><br>
|
||||
* Once using the base {@link GL32} function and a second time using
|
||||
* Minecraft's {@link GlStateManager}?<br><br>
|
||||
*
|
||||
* <b>Answer: </b><br>
|
||||
* Compatibility and robustness<br>
|
||||
* In general all MC rendering should go through MC's {@link GlStateManager},
|
||||
* however that isn't always the case.
|
||||
* So, to prevent issues if a mod (or MC itself) calls a direct GL function
|
||||
* instead of the {@link GlStateManager} wrapper, we need to be sure about what the actual
|
||||
* set value is (whether setting or getting) and that MC knows what DH has done.
|
||||
* This way whether a mod (or MC) is using the {@link GlStateManager} or direct GL calls,
|
||||
* they should always have the correct value for anything DH has modified.
|
||||
* <br><br>
|
||||
* This may slow down some low end GPUs that are driver limited,
|
||||
* however James would rather have slow correct rendering vs fast broken rendering.
|
||||
*/
|
||||
public class MinecraftGLWrapper implements IMinecraftGLWrapper
|
||||
{
|
||||
public static final MinecraftGLWrapper INSTANCE = new MinecraftGLWrapper();
|
||||
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
|
||||
|
||||
/*
|
||||
private static final StencilState STENCIL;
|
||||
*/
|
||||
|
||||
|
||||
// scissor //
|
||||
|
||||
/** @see GL32#GL_SCISSOR_TEST */
|
||||
@Override
|
||||
public void enableScissorTest()
|
||||
{
|
||||
GL32.glEnable(GL32.GL_SCISSOR_TEST);
|
||||
GlStateManager._enableScissorTest();
|
||||
}
|
||||
/** @see GL32#GL_SCISSOR_TEST */
|
||||
@Override
|
||||
public void disableScissorTest()
|
||||
{
|
||||
GL32.glDisable(GL32.GL_SCISSOR_TEST);
|
||||
GlStateManager._disableScissorTest();
|
||||
}
|
||||
|
||||
|
||||
// stencil //
|
||||
//
|
||||
// /** @see GL32#GL_SCISSOR_TEST */
|
||||
// public void enableScissorTest() { GlStateManager._stencilFunc(); }
|
||||
// /** @see GL32#GL_SCISSOR_TEST */
|
||||
// public void disableScissorTest() { GlStateManager._disableScissorTest(); }
|
||||
|
||||
|
||||
// depth //
|
||||
|
||||
/** @see GL32#GL_DEPTH_TEST */
|
||||
@Override
|
||||
public void enableDepthTest()
|
||||
{
|
||||
GL32.glEnable(GL32.GL_DEPTH_TEST);
|
||||
GlStateManager._enableDepthTest();
|
||||
}
|
||||
/** @see GL32#GL_DEPTH_TEST */
|
||||
@Override
|
||||
public void disableDepthTest()
|
||||
{
|
||||
GL32.glDisable(GL32.GL_DEPTH_TEST);
|
||||
GlStateManager._disableDepthTest();
|
||||
}
|
||||
|
||||
/** @see GL32#glDepthFunc(int) */
|
||||
@Override
|
||||
public void glDepthFunc(int func)
|
||||
{
|
||||
GL32.glDepthFunc(func);
|
||||
GlStateManager._depthFunc(func);
|
||||
}
|
||||
|
||||
/** @see GL32#glDepthMask(boolean) */
|
||||
@Override
|
||||
public void enableDepthMask()
|
||||
{
|
||||
GL32.glDepthMask(true);
|
||||
GlStateManager._depthMask(true);
|
||||
}
|
||||
/** @see GL32#glDepthMask(boolean) */
|
||||
@Override
|
||||
public void disableDepthMask()
|
||||
{
|
||||
GL32.glDepthMask(false);
|
||||
GlStateManager._depthMask(false);
|
||||
}
|
||||
|
||||
|
||||
// blending //
|
||||
|
||||
/** @see GL32#GL_BLEND */
|
||||
@Override
|
||||
public void enableBlend()
|
||||
{
|
||||
GL32.glEnable(GL32.GL_BLEND);
|
||||
GlStateManager._enableBlend();
|
||||
}
|
||||
/** @see GL32#GL_BLEND */
|
||||
@Override
|
||||
public void disableBlend()
|
||||
{
|
||||
GL32.glDisable(GL32.GL_BLEND);
|
||||
GlStateManager._disableBlend();
|
||||
}
|
||||
|
||||
/** @see GL32#glBlendFunc */
|
||||
@Override
|
||||
public void glBlendFunc(int sfactor, int dfactor)
|
||||
{
|
||||
GL32.glBlendFunc(sfactor, dfactor);
|
||||
GlStateManager._blendFunc(sfactor, dfactor);
|
||||
}
|
||||
/** @see GL32#glBlendFuncSeparate */
|
||||
@Override
|
||||
public void glBlendFuncSeparate(int sfactorRGB, int dfactorRGB, int sfactorAlpha, int dfactorAlpha)
|
||||
{
|
||||
GL32.glBlendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha);
|
||||
GlStateManager._blendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha);
|
||||
}
|
||||
|
||||
|
||||
// frame buffers //
|
||||
|
||||
/** @see GL32#glBindFramebuffer */
|
||||
@Override
|
||||
public void glBindFramebuffer(int target, int framebuffer)
|
||||
{
|
||||
GL32.glBindFramebuffer(target, framebuffer);
|
||||
GlStateManager._glBindFramebuffer(target, framebuffer);
|
||||
}
|
||||
|
||||
|
||||
// buffers //
|
||||
|
||||
/** @see GL32#glGenBuffers() */
|
||||
@Override
|
||||
public int glGenBuffers()
|
||||
{ return GlStateManager._glGenBuffers(); }
|
||||
|
||||
/** @see GL32#glDeleteBuffers(int) */
|
||||
@Override
|
||||
public void glDeleteBuffers(int buffer)
|
||||
{ GlStateManager._glDeleteBuffers(buffer); }
|
||||
|
||||
|
||||
// culling //
|
||||
|
||||
/** @see GL32#GL_CULL_FACE */
|
||||
@Override
|
||||
public void enableFaceCulling()
|
||||
{
|
||||
GL32.glEnable(GL32.GL_CULL_FACE);
|
||||
GlStateManager._enableCull();
|
||||
}
|
||||
/** @see GL32#GL_CULL_FACE */
|
||||
@Override
|
||||
public void disableFaceCulling()
|
||||
{
|
||||
GL32.glDisable(GL32.GL_CULL_FACE);
|
||||
GlStateManager._disableCull();
|
||||
}
|
||||
|
||||
|
||||
// textures //
|
||||
|
||||
/** @see GL32#glGenTextures() */
|
||||
@Override
|
||||
public int glGenTextures() { return GlStateManager._genTexture(); }
|
||||
/** @see GL32#glDeleteTextures(int) */
|
||||
@Override
|
||||
public void glDeleteTextures(int texture) { GlStateManager._deleteTexture(texture); }
|
||||
|
||||
/** @see GL32#glActiveTexture(int) */
|
||||
@Override
|
||||
public void glActiveTexture(int textureId)
|
||||
{
|
||||
GL32.glActiveTexture(textureId);
|
||||
GlStateManager._activeTexture(textureId);
|
||||
}
|
||||
@Override
|
||||
public int getActiveTexture() { return GL32.glGetInteger(GL32.GL_ACTIVE_TEXTURE); }
|
||||
|
||||
/**
|
||||
* Always binds to {@link GL32#GL_TEXTURE_2D}
|
||||
* @see GL32#glBindTexture(int, int)
|
||||
*/
|
||||
@Override
|
||||
public void glBindTexture(int texture)
|
||||
{
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D, texture);
|
||||
GlStateManager._bindTexture(texture);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
+79
-151
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -21,53 +21,45 @@ package com.seibel.distanthorizons.common.wrappers.minecraft;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.mojang.blaze3d.pipeline.RenderTarget;
|
||||
import com.mojang.blaze3d.platform.NativeImage;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
|
||||
import com.seibel.distanthorizons.common.wrappers.WrapperFactory;
|
||||
import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
|
||||
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.render.DhApiRenderProxy;
|
||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
|
||||
|
||||
#if PRE_MC_1_19_4
|
||||
import com.mojang.math.Vector3f;
|
||||
#else
|
||||
import org.joml.Vector3f;
|
||||
#if MC_VER >= MC_1_17_1
|
||||
import net.minecraft.client.renderer.FogRenderer;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
#endif
|
||||
#if MC_1_20_2
|
||||
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
|
||||
|
||||
#if MC_VER < MC_1_19_4
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3f;
|
||||
#else
|
||||
#endif
|
||||
#if MC_VER >= MC_1_20_2
|
||||
#endif
|
||||
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.AbstractOptifineAccessor;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IDimensionTypeWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
|
||||
import com.seibel.distanthorizons.coreapi.util.math.Vec3d;
|
||||
import com.seibel.distanthorizons.coreapi.util.math.Vec3f;
|
||||
import com.seibel.distanthorizons.core.util.math.Vec3d;
|
||||
import com.seibel.distanthorizons.core.util.math.Vec3f;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ISodiumAccessor;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos;
|
||||
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.FogRenderer;
|
||||
import net.minecraft.client.renderer.LevelRenderer;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.effect.MobEffects;
|
||||
#if PRE_MC_1_17_1
|
||||
#if MC_VER < MC_1_17_1
|
||||
import net.minecraft.tags.FluidTags;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
@@ -75,9 +67,9 @@ import org.lwjgl.opengl.GL15;
|
||||
#else
|
||||
import net.minecraft.world.level.material.FogType;
|
||||
#endif
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.joml.Vector4f;
|
||||
|
||||
|
||||
/**
|
||||
@@ -102,7 +94,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
|
||||
* In the case of immersive portals multiple levels may be active at once, causing conflicting lightmaps. <br>
|
||||
* Requiring the use of multiple {@link LightMapWrapper}.
|
||||
*/
|
||||
public HashMap<IDimensionTypeWrapper, LightMapWrapper> lightmapByDimensionType = new HashMap<>();
|
||||
public ConcurrentHashMap<IDimensionTypeWrapper, LightMapWrapper> lightmapByDimensionType = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Holds the render buffer that should be used when displaying levels to the screen.
|
||||
@@ -116,16 +108,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
|
||||
public Vec3f getLookAtVector()
|
||||
{
|
||||
Camera camera = MC.gameRenderer.getMainCamera();
|
||||
Vector3f cameraDir = camera.getLookVector();
|
||||
return new Vec3f(cameraDir.x(), cameraDir.y(), cameraDir.z());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DhBlockPos getCameraBlockPosition()
|
||||
{
|
||||
Camera camera = MC.gameRenderer.getMainCamera();
|
||||
BlockPos blockPos = camera.getBlockPosition();
|
||||
return new DhBlockPos(blockPos.getX(), blockPos.getY(), blockPos.getZ());
|
||||
return new Vec3f(camera.getLookVector().x(), camera.getLookVector().y(), camera.getLookVector().z());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -133,7 +116,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
|
||||
public boolean playerHasBlindingEffect()
|
||||
{
|
||||
return MC.player.getActiveEffectsMap().get(MobEffects.BLINDNESS) != null
|
||||
#if POST_AND_MC_1_19_2
|
||||
#if MC_VER >= MC_1_19_2
|
||||
|| MC.player.getActiveEffectsMap().get(MobEffects.DARKNESS) != null // Deep dark effect
|
||||
#endif
|
||||
;
|
||||
@@ -148,42 +131,36 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
|
||||
return new Vec3d(projectedView.x, projectedView.y, projectedView.z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mat4f getDefaultProjectionMatrix(float partialTicks)
|
||||
{
|
||||
#if PRE_MC_1_17_1
|
||||
return McObjectConverter.Convert(Minecraft.getInstance().gameRenderer.getProjectionMatrix(Minecraft.getInstance().gameRenderer.getMainCamera(), partialTicks, true));
|
||||
#else
|
||||
return McObjectConverter.Convert(MC.gameRenderer.getProjectionMatrix(MC.gameRenderer.getFov(MC.gameRenderer.getMainCamera(), partialTicks, true)));
|
||||
#endif
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getGamma()
|
||||
{
|
||||
#if PRE_MC_1_19_2
|
||||
return MC.options.gamma;
|
||||
#else
|
||||
return MC.options.gamma().get();
|
||||
#endif
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getFogColor(float partialTicks)
|
||||
{
|
||||
#if PRE_MC_1_17_1
|
||||
#if MC_VER < MC_1_17_1
|
||||
float[] colorValues = new float[4];
|
||||
GL15.glGetFloatv(GL15.GL_FOG_COLOR, colorValues);
|
||||
#else
|
||||
return new Color(
|
||||
Math.max(0f, Math.min(colorValues[0], 1f)), // r
|
||||
Math.max(0f, Math.min(colorValues[1], 1f)), // g
|
||||
Math.max(0f, Math.min(colorValues[2], 1f)), // b
|
||||
Math.max(0f, Math.min(colorValues[3], 1f)) // a
|
||||
);
|
||||
#elif MC_VER < MC_1_21_3
|
||||
FogRenderer.setupColor(MC.gameRenderer.getMainCamera(), partialTicks, MC.level, 1, MC.gameRenderer.getDarkenWorldAmount(partialTicks));
|
||||
float[] colorValues = RenderSystem.getShaderFogColor();
|
||||
#endif
|
||||
return new Color(
|
||||
Math.max(0f, Math.min(colorValues[0], 1f)),
|
||||
Math.max(0f, Math.min(colorValues[1], 1f)),
|
||||
Math.max(0f, Math.min(colorValues[2], 1f)),
|
||||
Math.max(0f, Math.min(colorValues[3], 1f))
|
||||
Math.max(0f, Math.min(colorValues[0], 1f)), // r
|
||||
Math.max(0f, Math.min(colorValues[1], 1f)), // g
|
||||
Math.max(0f, Math.min(colorValues[2], 1f)), // b
|
||||
Math.max(0f, Math.min(colorValues[3], 1f)) // a
|
||||
);
|
||||
#else
|
||||
Vector4f colorValues = FogRenderer.computeFogColor(MC.gameRenderer.getMainCamera(), partialTicks, MC.level, 1, MC.gameRenderer.getDarkenWorldAmount(partialTicks));
|
||||
return new Color(
|
||||
Math.max(0f, Math.min(colorValues.x, 1f)), // r
|
||||
Math.max(0f, Math.min(colorValues.y, 1f)), // g
|
||||
Math.max(0f, Math.min(colorValues.z, 1f)), // b
|
||||
Math.max(0f, Math.min(colorValues.w, 1f)) // a
|
||||
);
|
||||
#endif
|
||||
}
|
||||
// getSpecialFogColor() is the same as getFogColor()
|
||||
|
||||
@@ -192,15 +169,30 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
|
||||
{
|
||||
if (MC.level.dimensionType().hasSkyLight())
|
||||
{
|
||||
#if PRE_MC_1_17_1
|
||||
Vec3 colorValues = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getBlockPosition(), MC.getFrameTime());
|
||||
float frameTime;
|
||||
#if MC_VER < MC_1_21_1
|
||||
frameTime = MC.getFrameTime();
|
||||
#elif MC_VER < MC_1_21_3
|
||||
frameTime = MC.getTimer().getRealtimeDeltaTicks();
|
||||
#else
|
||||
Vec3 colorValues = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getPosition(), MC.getFrameTime());
|
||||
frameTime = MC.deltaTracker.getGameTimeDeltaTicks();
|
||||
#endif
|
||||
|
||||
#if MC_VER < MC_1_17_1
|
||||
Vec3 colorValues = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getBlockPosition(), frameTime);
|
||||
return new Color((float) colorValues.x, (float) colorValues.y, (float) colorValues.z);
|
||||
#elif MC_VER < MC_1_21_3
|
||||
Vec3 colorValues = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getPosition(), frameTime);
|
||||
return new Color((float) colorValues.x, (float) colorValues.y, (float) colorValues.z);
|
||||
#else
|
||||
int argbColorInt = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getPosition(), frameTime);;
|
||||
return ColorUtil.toColorObjARGB(argbColorInt); // TODO MC changed color formats
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
return new Color(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -213,7 +205,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
|
||||
@Override
|
||||
public int getRenderDistance()
|
||||
{
|
||||
#if PRE_MC_1_18_2
|
||||
#if MC_VER < MC_1_18_2
|
||||
//FIXME: How to resolve this?
|
||||
return MC.options.renderDistance;
|
||||
#else
|
||||
@@ -261,12 +253,6 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
|
||||
@Override
|
||||
public int getTargetFrameBuffer()
|
||||
{
|
||||
int frameBufferOverrideId = DhApiRenderProxy.INSTANCE.targetFrameBufferOverride;
|
||||
if (frameBufferOverrideId != -1)
|
||||
{
|
||||
return frameBufferOverrideId;
|
||||
}
|
||||
|
||||
// used so we can access the framebuffer shaders end up rendering to
|
||||
if (AbstractOptifineAccessor.optifinePresent())
|
||||
{
|
||||
@@ -281,6 +267,8 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
|
||||
|
||||
@Override
|
||||
public int getDepthTextureId() { return this.getRenderTarget().getDepthTextureId(); }
|
||||
@Override
|
||||
public int getColorTextureId() { return this.getRenderTarget().getColorTextureId(); }
|
||||
|
||||
@Override
|
||||
public int getTargetFrameBufferViewportWidth()
|
||||
@@ -294,84 +282,13 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
|
||||
return getRenderTarget().viewHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the ChunkPos of all chunks that Minecraft
|
||||
* is going to render this frame. <br><br>
|
||||
* <p>
|
||||
*/
|
||||
|
||||
public boolean usingBackupGetVanillaRenderedChunks = false;
|
||||
@Override
|
||||
public HashSet<DhChunkPos> getVanillaRenderedChunks()
|
||||
{
|
||||
ISodiumAccessor sodium = ModAccessorInjector.INSTANCE.get(ISodiumAccessor.class);
|
||||
if (sodium != null)
|
||||
{
|
||||
return sodium.getNormalRenderedChunks();
|
||||
}
|
||||
IOptifineAccessor optifine = ModAccessorInjector.INSTANCE.get(IOptifineAccessor.class);
|
||||
if (optifine != null)
|
||||
{
|
||||
HashSet<DhChunkPos> pos = optifine.getNormalRenderedChunks();
|
||||
if (pos == null)
|
||||
pos = getMaximumRenderedChunks();
|
||||
return pos;
|
||||
}
|
||||
if (!usingBackupGetVanillaRenderedChunks)
|
||||
{
|
||||
try
|
||||
{
|
||||
#if MC_1_20_2
|
||||
LevelRenderer levelRenderer = MC.levelRenderer;
|
||||
Collection<SectionRenderDispatcher.RenderSection> chunks = levelRenderer.visibleSections;
|
||||
|
||||
return (chunks.stream().map((chunk) -> {
|
||||
AABB chunkBoundingBox = chunk.getBoundingBox();
|
||||
return new DhChunkPos(Math.floorDiv((int) chunkBoundingBox.minX, 16),
|
||||
Math.floorDiv((int) chunkBoundingBox.minZ, 16));
|
||||
}).collect(Collectors.toCollection(HashSet::new)));
|
||||
#else
|
||||
LevelRenderer levelRenderer = MC.levelRenderer;
|
||||
Collection<LevelRenderer.RenderChunkInfo> chunks =
|
||||
#if PRE_MC_1_18_2 levelRenderer.renderChunks;
|
||||
#else levelRenderer.renderChunkStorage.get().renderChunks; #endif
|
||||
|
||||
return (chunks.stream().map((chunk) -> {
|
||||
AABB chunkBoundingBox =
|
||||
#if PRE_MC_1_18_2 chunk.chunk.bb;
|
||||
#else chunk.chunk.getBoundingBox(); #endif
|
||||
return new DhChunkPos(Math.floorDiv((int) chunkBoundingBox.minX, 16),
|
||||
Math.floorDiv((int) chunkBoundingBox.minZ, 16));
|
||||
}).collect(Collectors.toCollection(HashSet::new)));
|
||||
#endif
|
||||
}
|
||||
catch (LinkageError e)
|
||||
{
|
||||
try
|
||||
{
|
||||
MinecraftClientWrapper.INSTANCE.sendChatMessage(
|
||||
"\u00A7e\u00A7l\u00A7uWARNING: Distant Horizons: getVanillaRenderedChunks method failed."
|
||||
+ " Using Backup Method.");
|
||||
MinecraftClientWrapper.INSTANCE.sendChatMessage(
|
||||
"\u00A7eOverdraw prevention will be worse than normal.");
|
||||
}
|
||||
catch (Exception e2)
|
||||
{
|
||||
}
|
||||
LOGGER.error("getVanillaRenderedChunks Error: ", e);
|
||||
usingBackupGetVanillaRenderedChunks = true;
|
||||
}
|
||||
}
|
||||
return getMaximumRenderedChunks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ILightMapWrapper getLightmapWrapper(ILevelWrapper level) { return this.lightmapByDimensionType.get(level.getDimensionType()); }
|
||||
|
||||
@Override
|
||||
public boolean isFogStateSpecial()
|
||||
{
|
||||
#if PRE_MC_1_17_1
|
||||
#if MC_VER < MC_1_17_1
|
||||
Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera();
|
||||
FluidState fluidState = camera.getFluidInCamera();
|
||||
Entity entity = camera.getEntity();
|
||||
@@ -385,6 +302,10 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* It's better to use {@link MinecraftRenderWrapper#setLightmapId(int, IClientLevelWrapper)} if possible,
|
||||
* however old MC versions don't support it.
|
||||
*/
|
||||
public void updateLightmap(NativeImage lightPixels, IClientLevelWrapper level)
|
||||
{
|
||||
// Using ClientLevelWrapper as the key would be better, but we don't have a consistent way to create the same
|
||||
@@ -392,11 +313,18 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
|
||||
// so this will have to do for now
|
||||
IDimensionTypeWrapper dimensionType = level.getDimensionType();
|
||||
|
||||
if (!this.lightmapByDimensionType.containsKey(dimensionType))
|
||||
{
|
||||
this.lightmapByDimensionType.put(dimensionType, new LightMapWrapper());
|
||||
}
|
||||
this.lightmapByDimensionType.get(dimensionType).uploadLightmap(lightPixels);
|
||||
LightMapWrapper wrapper = this.lightmapByDimensionType.computeIfAbsent(dimensionType, (dimType) -> new LightMapWrapper());
|
||||
wrapper.uploadLightmap(lightPixels);
|
||||
}
|
||||
public void setLightmapId(int tetxureId, IClientLevelWrapper level)
|
||||
{
|
||||
// Using ClientLevelWrapper as the key would be better, but we don't have a consistent way to create the same
|
||||
// object for the same MC level and/or the same hash,
|
||||
// so this will have to do for now
|
||||
IDimensionTypeWrapper dimensionType = level.getDimensionType();
|
||||
|
||||
LightMapWrapper wrapper = this.lightmapByDimensionType.computeIfAbsent(dimensionType, (dimType) -> new LightMapWrapper());
|
||||
wrapper.setLightmapId(tetxureId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.minecraft;
|
||||
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
|
||||
import net.minecraft.server.dedicated.DedicatedServer;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
//@Environment(EnvType.SERVER)
|
||||
public class MinecraftServerWrapper implements IMinecraftSharedWrapper
|
||||
{
|
||||
public static final MinecraftServerWrapper INSTANCE = new MinecraftServerWrapper();
|
||||
|
||||
public DedicatedServer dedicatedServer = null;
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
private MinecraftServerWrapper() { }
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// methods //
|
||||
//=========//
|
||||
|
||||
@Override
|
||||
public boolean isDedicatedServer() { return true; }
|
||||
|
||||
@Override
|
||||
public File getInstallationDirectory()
|
||||
{
|
||||
if (this.dedicatedServer == null)
|
||||
{
|
||||
throw new IllegalStateException("Trying to get Installation Direction before Dedicated server completed initialization!");
|
||||
}
|
||||
|
||||
#if MC_VER < MC_1_21_1
|
||||
return this.dedicatedServer.getServerDirectory();
|
||||
#else
|
||||
return this.dedicatedServer.getServerDirectory().toFile();
|
||||
#endif
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPlayerCount()
|
||||
{
|
||||
return this.dedicatedServer.getPlayerCount();
|
||||
}
|
||||
|
||||
}
|
||||
+5
-17
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -31,31 +31,19 @@ public class ProfilerWrapper implements IProfilerWrapper
|
||||
{
|
||||
public ProfilerFiller profiler;
|
||||
|
||||
public ProfilerWrapper(ProfilerFiller newProfiler)
|
||||
{
|
||||
profiler = newProfiler;
|
||||
}
|
||||
public ProfilerWrapper(ProfilerFiller newProfiler) { this.profiler = newProfiler; }
|
||||
|
||||
|
||||
/** starts a new section inside the currently running section */
|
||||
@Override
|
||||
public void push(String newSection)
|
||||
{
|
||||
profiler.push(newSection);
|
||||
}
|
||||
public void push(String newSection) { this.profiler.push(newSection); }
|
||||
|
||||
/** ends the currently running section and starts a new one */
|
||||
@Override
|
||||
public void popPush(String newSection)
|
||||
{
|
||||
profiler.popPush(newSection);
|
||||
}
|
||||
public void popPush(String newSection) { this.profiler.popPush(newSection); }
|
||||
|
||||
/** ends the currently running section */
|
||||
@Override
|
||||
public void pop()
|
||||
{
|
||||
profiler.pop();
|
||||
}
|
||||
public void pop() { this.profiler.pop(); }
|
||||
|
||||
}
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.misc;
|
||||
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface IMixinServerPlayer
|
||||
{
|
||||
@Nullable
|
||||
ServerLevel distantHorizons$getDimensionChangeDestination();
|
||||
|
||||
#if MC_VER == MC_1_16_5
|
||||
void distantHorizons$setDimensionChangeDestination(ServerLevel dimensionChangeDestination);
|
||||
#endif
|
||||
|
||||
}
|
||||
+47
-26
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -20,55 +20,76 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.misc;
|
||||
|
||||
import com.mojang.blaze3d.platform.NativeImage;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* @author James Seibel
|
||||
* @version 11-21-2021
|
||||
*/
|
||||
public class LightMapWrapper implements ILightMapWrapper
|
||||
{
|
||||
private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
|
||||
|
||||
private int textureId = 0;
|
||||
|
||||
public LightMapWrapper()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//==============//
|
||||
// constructors //
|
||||
//==============//
|
||||
|
||||
public LightMapWrapper() { }
|
||||
|
||||
|
||||
|
||||
//==================//
|
||||
// lightmap syncing //
|
||||
//==================//
|
||||
|
||||
public void uploadLightmap(NativeImage image)
|
||||
{
|
||||
int currentTexture = GLMC.getActiveTexture();
|
||||
if (this.textureId == 0)
|
||||
{
|
||||
this.createLightmap(image);
|
||||
}
|
||||
else
|
||||
{
|
||||
GLMC.glBindTexture(this.textureId);
|
||||
}
|
||||
image.upload(0, 0, 0, false);
|
||||
GLMC.glBindTexture(currentTexture);
|
||||
}
|
||||
private void createLightmap(NativeImage image)
|
||||
{
|
||||
textureId = GL32.glGenTextures();
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D, textureId);
|
||||
this.textureId = GLMC.glGenTextures();
|
||||
GLMC.glBindTexture(this.textureId);
|
||||
GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, image.format().glFormat(), image.getWidth(), image.getHeight(),
|
||||
0, image.format().glFormat(), GL32.GL_UNSIGNED_BYTE, (ByteBuffer) null);
|
||||
}
|
||||
|
||||
public void uploadLightmap(NativeImage image)
|
||||
public void setLightmapId(int minecraftLightmapTetxureId)
|
||||
{
|
||||
int currentBind = GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D);
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D, textureId);
|
||||
if (textureId == 0)
|
||||
{
|
||||
createLightmap(image);
|
||||
}
|
||||
// NativeImage::upload(int levelOfDetail, int xOffset, int yOffset, bool shouldCleanup?)
|
||||
image.upload(0, 0, 0, false);
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D, currentBind);
|
||||
// just use the MC texture ID
|
||||
this.textureId = minecraftLightmapTetxureId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// lightmap use //
|
||||
//==============//
|
||||
|
||||
@Override
|
||||
public void bind()
|
||||
{
|
||||
GL32.glActiveTexture(GL32.GL_TEXTURE0);
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.textureId);
|
||||
GLMC.glActiveTexture(GL32.GL_TEXTURE0);
|
||||
GLMC.glBindTexture(this.textureId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbind()
|
||||
{
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D, 0);
|
||||
}
|
||||
public void unbind() { GLMC.glBindTexture(0); }
|
||||
|
||||
}
|
||||
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.misc;
|
||||
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IMutableBlockPosWrapper;
|
||||
import net.minecraft.core.BlockPos;
|
||||
|
||||
public class MutableBlockPosWrapper implements IMutableBlockPosWrapper
|
||||
{
|
||||
public final BlockPos.MutableBlockPos pos;
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public MutableBlockPosWrapper()
|
||||
{
|
||||
this.pos = new BlockPos.MutableBlockPos();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//===========//
|
||||
// overrides //
|
||||
//===========//
|
||||
|
||||
@Override
|
||||
public Object getWrappedMcObject() { return this.pos; }
|
||||
|
||||
}
|
||||
+87
-26
@@ -1,54 +1,115 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.misc;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.MapMaker;
|
||||
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.util.math.Vec3d;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.network.ServerGamePacketListenerImpl;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* This wrapper transparently ensures that the underlying {@link ServerPlayer} is always valid,
|
||||
* unless the player has disconnected.
|
||||
*/
|
||||
public class ServerPlayerWrapper implements IServerPlayerWrapper
|
||||
{
|
||||
private static final ConcurrentMap<ServerPlayer, ServerPlayerWrapper>
|
||||
serverPlayerWrapperMap = new MapMaker().weakKeys().makeMap();
|
||||
private static final ConcurrentMap<ServerGamePacketListenerImpl, ServerPlayerWrapper> serverPlayerWrapperMap = new MapMaker().weakKeys().weakValues().makeMap();
|
||||
|
||||
private final ServerPlayer serverPlayer;
|
||||
private final ServerGamePacketListenerImpl connection;
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public static ServerPlayerWrapper getWrapper(ServerPlayer serverPlayer)
|
||||
{
|
||||
return serverPlayerWrapperMap.computeIfAbsent(serverPlayer, ServerPlayerWrapper::new);
|
||||
}
|
||||
{ return serverPlayerWrapperMap.computeIfAbsent(serverPlayer.connection, ignored -> new ServerPlayerWrapper(serverPlayer.connection)); }
|
||||
|
||||
private ServerPlayerWrapper(ServerPlayer serverPlayer)
|
||||
{
|
||||
this.serverPlayer = serverPlayer;
|
||||
}
|
||||
private ServerPlayerWrapper(ServerGamePacketListenerImpl connection) { this.connection = connection; }
|
||||
|
||||
public UUID getUUID()
|
||||
{
|
||||
return serverPlayer.getUUID();
|
||||
}
|
||||
|
||||
|
||||
//=========//
|
||||
// getters //
|
||||
//=========//
|
||||
|
||||
private ServerPlayer getServerPlayer() { return this.connection.player; }
|
||||
|
||||
@Override
|
||||
public String getName() { return this.getServerPlayer().getName().getString(); }
|
||||
|
||||
@Override
|
||||
public IServerLevelWrapper getLevel()
|
||||
{
|
||||
#if PRE_MC_1_20_1
|
||||
return ServerLevelWrapper.getWrapper(this.serverPlayer.getLevel());
|
||||
#else
|
||||
return ServerLevelWrapper.getWrapper(this.serverPlayer.serverLevel());
|
||||
#endif
|
||||
}
|
||||
ServerLevel level = ((IMixinServerPlayer) this.getServerPlayer()).distantHorizons$getDimensionChangeDestination();
|
||||
if (level == null)
|
||||
{
|
||||
#if MC_VER < MC_1_20_1
|
||||
level = this.getServerPlayer().getLevel();
|
||||
#else
|
||||
level = this.getServerPlayer().serverLevel();
|
||||
#endif
|
||||
}
|
||||
|
||||
public Object getWrappedMcObject()
|
||||
{
|
||||
return serverPlayer;
|
||||
return ServerLevelWrapper.getWrapper(level);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
public Vec3d getPosition()
|
||||
{
|
||||
return "Wrapped{" + serverPlayer.toString() + "}";
|
||||
Vec3 position = this.getServerPlayer().position();
|
||||
return new Vec3d(position.x, position.y, position.z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getViewDistance() { return this.getServerPlayer().server.getPlayerList().getViewDistance(); }
|
||||
|
||||
@Override
|
||||
public SocketAddress getRemoteAddress()
|
||||
{
|
||||
#if MC_VER >= MC_1_19_4
|
||||
return this.getServerPlayer().connection.getRemoteAddress();
|
||||
#else // < 1.19.4
|
||||
return this.getServerPlayer().connection.connection.getRemoteAddress();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// base overrides //
|
||||
//================//
|
||||
|
||||
@Override
|
||||
public Object getWrappedMcObject() { return this.getServerPlayer(); }
|
||||
|
||||
@Override
|
||||
public String toString() { return "Wrapped{" + this.getServerPlayer() + "}"; }
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof ServerPlayerWrapper))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
ServerPlayerWrapper that = (ServerPlayerWrapper) obj;
|
||||
return Objects.equal(this.connection, that.connection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() { return Objects.hashCode(this.connection); }
|
||||
|
||||
}
|
||||
|
||||
+173
-36
@@ -1,17 +1,17 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.world;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType;
|
||||
import com.seibel.distanthorizons.api.interfaces.world.IDhApiDimensionTypeWrapper;
|
||||
import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister;
|
||||
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
|
||||
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.block.cache.ClientBlockDetailMap;
|
||||
import com.seibel.distanthorizons.common.wrappers.block.ClientBlockStateColorCache;
|
||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager;
|
||||
import com.seibel.distanthorizons.core.level.*;
|
||||
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
@@ -19,27 +19,48 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IDimensionTypeWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.LightLayer;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkSource;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
#if MC_VER <= MC_1_20_4
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
#else
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
#endif
|
||||
|
||||
#if MC_VER < MC_1_21_3
|
||||
#else
|
||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
||||
#endif
|
||||
|
||||
public class ClientLevelWrapper implements IClientLevelWrapper
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger(ClientLevelWrapper.class.getSimpleName());
|
||||
private static final ConcurrentHashMap<ClientLevel, ClientLevelWrapper> LEVEL_WRAPPER_BY_CLIENT_LEVEL = new ConcurrentHashMap<>();
|
||||
private static final ConcurrentHashMap<ClientLevel, ClientLevelWrapper> LEVEL_WRAPPER_BY_CLIENT_LEVEL = new ConcurrentHashMap<>(); // TODO can leak
|
||||
private static final IKeyedClientLevelManager KEYED_CLIENT_LEVEL_MANAGER = SingletonInjector.INSTANCE.get(IKeyedClientLevelManager.class);
|
||||
|
||||
private static final Minecraft MINECRAFT = Minecraft.getInstance();
|
||||
|
||||
private final ClientLevel level;
|
||||
private final ClientBlockDetailMap blockMap = new ClientBlockDetailMap(this);
|
||||
private final ConcurrentHashMap<BlockState, ClientBlockStateColorCache> blockCache = new ConcurrentHashMap<>();
|
||||
|
||||
private BlockStateWrapper dirtBlockWrapper;
|
||||
private BiomeWrapper plainsBiomeWrapper;
|
||||
@Deprecated // TODO circular references are bad
|
||||
private IDhLevel parentDhLevel;
|
||||
|
||||
|
||||
|
||||
@@ -55,23 +76,28 @@ public class ClientLevelWrapper implements IClientLevelWrapper
|
||||
// wrapper logic //
|
||||
//===============//
|
||||
|
||||
public static IClientLevelWrapper getWrapper(@NotNull ClientLevel level) { return getWrapper(level, false); }
|
||||
|
||||
@Nullable
|
||||
public static IClientLevelWrapper getWrapper(@Nullable ClientLevel level)
|
||||
public static IClientLevelWrapper getWrapper(@Nullable ClientLevel level, boolean bypassLevelKeyManager)
|
||||
{
|
||||
if (level == null)
|
||||
if (!bypassLevelKeyManager)
|
||||
{
|
||||
return null;
|
||||
if (level == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// used if the client is connected to a server that defines the currently loaded level
|
||||
IServerKeyedClientLevel overrideLevel = KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel();
|
||||
if (overrideLevel != null)
|
||||
{
|
||||
return overrideLevel;
|
||||
}
|
||||
}
|
||||
|
||||
// used if the client is connected to a server that defines the currently loaded level
|
||||
if (KEYED_CLIENT_LEVEL_MANAGER.getUseOverrideWrapper())
|
||||
{
|
||||
return KEYED_CLIENT_LEVEL_MANAGER.getOverrideWrapper();
|
||||
}
|
||||
|
||||
return getWrapperIgnoringOverride(level);
|
||||
return LEVEL_WRAPPER_BY_CLIENT_LEVEL.computeIfAbsent(level, ClientLevelWrapper::new);
|
||||
}
|
||||
public static IClientLevelWrapper getWrapperIgnoringOverride(@NotNull ClientLevel level) { return LEVEL_WRAPPER_BY_CLIENT_LEVEL.computeIfAbsent(level, ClientLevelWrapper::new); }
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@@ -79,7 +105,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
|
||||
{
|
||||
try
|
||||
{
|
||||
Iterable<ServerLevel> serverLevels = MinecraftClientWrapper.INSTANCE.mc.getSingleplayerServer().getAllLevels();
|
||||
Iterable<ServerLevel> serverLevels = MINECRAFT.getSingleplayerServer().getAllLevels();
|
||||
|
||||
// attempt to find the server level with the same dimension type
|
||||
// TODO this assumes only one level per dimension type, the SubDimensionLevelMatcher will need to be added for supporting multiple levels per dimension
|
||||
@@ -99,7 +125,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOGGER.error("Failed to get server side wrapper for client level: " + level);
|
||||
LOGGER.error("Failed to get server side wrapper for client level: " + this.level);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -111,14 +137,71 @@ public class ClientLevelWrapper implements IClientLevelWrapper
|
||||
//====================//
|
||||
|
||||
@Override
|
||||
public int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper blockState)
|
||||
public int getBlockColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper blockWrapper)
|
||||
{
|
||||
return this.blockMap.getColor(((BlockStateWrapper) blockState).blockState, (BiomeWrapper) biome, pos);
|
||||
ClientBlockStateColorCache blockColorCache = this.blockCache.computeIfAbsent(
|
||||
((BlockStateWrapper) blockWrapper).blockState,
|
||||
(block) -> new ClientBlockStateColorCache(block, this));
|
||||
|
||||
return blockColorCache.getColor((BiomeWrapper) biome, pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDirtBlockColor()
|
||||
{
|
||||
if (this.dirtBlockWrapper == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.dirtBlockWrapper = (BlockStateWrapper) BlockStateWrapper.deserialize(BlockStateWrapper.DIRT_RESOURCE_LOCATION_STRING, this);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// shouldn't happen, but just in case
|
||||
LOGGER.warn("Unable to get dirt color with resource location ["+BlockStateWrapper.DIRT_RESOURCE_LOCATION_STRING+"] with level ["+this+"].", e);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return this.getBlockColor(DhBlockPos.ZERO,BiomeWrapper.EMPTY_WRAPPER, this.dirtBlockWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearBlockColorCache() { this.blockCache.clear(); }
|
||||
|
||||
@Override
|
||||
public IBiomeWrapper getPlainsBiomeWrapper()
|
||||
{
|
||||
if (this.plainsBiomeWrapper == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.plainsBiomeWrapper = (BiomeWrapper) BiomeWrapper.deserialize(BiomeWrapper.PLAINS_RESOURCE_LOCATION_STRING, this);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// shouldn't happen, but just in case
|
||||
LOGGER.warn("Unable to get planes biome with resource location ["+BiomeWrapper.PLAINS_RESOURCE_LOCATION_STRING+"] with level ["+this+"].", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return this.plainsBiomeWrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IDimensionTypeWrapper getDimensionType() { return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType()); }
|
||||
|
||||
|
||||
@Override
|
||||
public String getDimensionName() { return this.level.dimension().location().toString(); }
|
||||
|
||||
@Override
|
||||
public long getHashedSeed() { return this.level.getBiomeManager().biomeZoomSeed; }
|
||||
|
||||
@Override
|
||||
public String getDhIdentifier() { return this.getHashedSeedEncoded() + "@" + this.getDimensionName(); }
|
||||
|
||||
@Override
|
||||
public EDhApiLevelType getLevelType() { return EDhApiLevelType.CLIENT_LEVEL; }
|
||||
|
||||
@@ -131,33 +214,35 @@ public class ClientLevelWrapper implements IClientLevelWrapper
|
||||
public boolean hasSkyLight() { return this.level.dimensionType().hasSkyLight(); }
|
||||
|
||||
@Override
|
||||
public int getHeight() { return this.level.getHeight(); }
|
||||
public int getMaxHeight() { return this.level.getHeight(); }
|
||||
|
||||
@Override
|
||||
public int getMinHeight()
|
||||
{
|
||||
#if PRE_MC_1_17_1
|
||||
#if MC_VER < MC_1_17_1
|
||||
return 0;
|
||||
#else
|
||||
#elif MC_VER < MC_1_21_3
|
||||
return this.level.getMinBuildHeight();
|
||||
#else
|
||||
return this.level.getMinY();
|
||||
#endif
|
||||
}
|
||||
|
||||
@Override
|
||||
public IChunkWrapper tryGetChunk(DhChunkPos pos)
|
||||
{
|
||||
if (!this.level.hasChunk(pos.x, pos.z))
|
||||
if (!this.level.hasChunk(pos.getX(), pos.getZ()))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ChunkAccess chunk = this.level.getChunk(pos.x, pos.z, ChunkStatus.EMPTY, false);
|
||||
ChunkAccess chunk = this.level.getChunk(pos.getX(), pos.getZ(), ChunkStatus.EMPTY, false);
|
||||
if (chunk == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ChunkWrapper(chunk, this.level, this);
|
||||
return new ChunkWrapper(chunk, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -169,9 +254,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
|
||||
|
||||
@Override
|
||||
public IBlockStateWrapper getBlockState(DhBlockPos pos)
|
||||
{
|
||||
return BlockStateWrapper.fromBlockState(this.level.getBlockState(McObjectConverter.Convert(pos)), this);
|
||||
}
|
||||
{ return BlockStateWrapper.fromBlockState(this.level.getBlockState(McObjectConverter.Convert(pos)), this); }
|
||||
|
||||
@Override
|
||||
public IBiomeWrapper getBiome(DhBlockPos pos) { return BiomeWrapper.getBiomeWrapper(this.level.getBiome(McObjectConverter.Convert(pos)), this); }
|
||||
@@ -180,7 +263,61 @@ public class ClientLevelWrapper implements IClientLevelWrapper
|
||||
public ClientLevel getWrappedMcObject() { return this.level; }
|
||||
|
||||
@Override
|
||||
public void onUnload() { LEVEL_WRAPPER_BY_CLIENT_LEVEL.remove(this.level); }
|
||||
public void onUnload()
|
||||
{
|
||||
LEVEL_WRAPPER_BY_CLIENT_LEVEL.remove(this.level);
|
||||
this.parentDhLevel = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDhSaveFolder()
|
||||
{
|
||||
if (this.parentDhLevel == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.parentDhLevel.getSaveStructure().getSaveFolder(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//===================//
|
||||
// generic rendering //
|
||||
//===================//
|
||||
|
||||
@Override
|
||||
public void setParentLevel(IDhLevel parentLevel) { this.parentDhLevel = parentLevel; }
|
||||
|
||||
@Override
|
||||
public IDhApiCustomRenderRegister getRenderRegister()
|
||||
{
|
||||
if (this.parentDhLevel == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.parentDhLevel.getGenericRenderer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getCloudColor(float tickDelta)
|
||||
{
|
||||
#if MC_VER < MC_1_21_3
|
||||
Vec3 colorVec3 = this.level.getCloudColor(tickDelta);
|
||||
return new Color((float)colorVec3.x, (float)colorVec3.y, (float)colorVec3.z);
|
||||
#else
|
||||
int argbColor = this.level.getCloudColor(tickDelta);
|
||||
return ColorUtil.toColorObjARGB(argbColor);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// base overrides //
|
||||
//================//
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
@@ -190,7 +327,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
|
||||
return "Wrapped{null}";
|
||||
}
|
||||
|
||||
return "Wrapped{" + this.level.toString() + "@" + this.getDimensionType().getDimensionName() + "}";
|
||||
return "Wrapped{" + this.level.toString() + "@" + this.getDhIdentifier() + "}";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+15
-7
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -44,7 +44,9 @@ public class DimensionTypeWrapper implements IDimensionTypeWrapper
|
||||
{
|
||||
//first we check if the biome has already been wrapped
|
||||
if (dimensionTypeWrapperMap.containsKey(dimensionType) && dimensionTypeWrapperMap.get(dimensionType) != null)
|
||||
{
|
||||
return dimensionTypeWrapperMap.get(dimensionType);
|
||||
}
|
||||
|
||||
|
||||
//if it hasn't been created yet, we create it and save it in the map
|
||||
@@ -61,22 +63,26 @@ public class DimensionTypeWrapper implements IDimensionTypeWrapper
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getDimensionName()
|
||||
private String getDimensionName()
|
||||
{
|
||||
return dimensionType.effectsLocation().getPath();
|
||||
#if MC_VER <= MC_1_16_5
|
||||
// effectsLocation() is marked as client only, so using the backing field directly
|
||||
return dimensionType.effectsLocation.getPath();
|
||||
#else
|
||||
return this.dimensionType.effectsLocation().getPath();
|
||||
#endif
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCeiling()
|
||||
{
|
||||
return dimensionType.hasCeiling();
|
||||
return this.dimensionType.hasCeiling();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSkyLight()
|
||||
{
|
||||
return dimensionType.hasSkyLight();
|
||||
return this.dimensionType.hasSkyLight();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -85,7 +91,9 @@ public class DimensionTypeWrapper implements IDimensionTypeWrapper
|
||||
return this.dimensionType;
|
||||
}
|
||||
|
||||
|
||||
// there's definitely a better way of doing this, but it should work well enough for now
|
||||
@Override
|
||||
public boolean isTheEnd() { return this.getDimensionName().equalsIgnoreCase("the_end"); }
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
|
||||
+99
-62
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -23,39 +23,45 @@ import java.io.File;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType;
|
||||
import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister;
|
||||
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
|
||||
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.block.cache.ServerBlockDetailMap;
|
||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper;
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkSource;
|
||||
|
||||
#if MC_VER <= MC_1_20_4
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
#else
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
#endif
|
||||
|
||||
#if MC_VER < MC_1_21_3
|
||||
#else
|
||||
import java.nio.file.Path;
|
||||
#endif
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @version 2022-9-16
|
||||
*/
|
||||
public class ServerLevelWrapper implements IServerLevelWrapper
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
private static final ConcurrentHashMap<ServerLevel, ServerLevelWrapper> LEVEL_WRAPPER_BY_SERVER_LEVEL = new ConcurrentHashMap<>();
|
||||
|
||||
final ServerLevel level;
|
||||
ServerBlockDetailMap blockMap = new ServerBlockDetailMap(this);
|
||||
private final ServerLevel level;
|
||||
@Deprecated // TODO circular references are bad
|
||||
private IDhLevel parentDhLevel;
|
||||
|
||||
|
||||
|
||||
@@ -63,12 +69,10 @@ public class ServerLevelWrapper implements IServerLevelWrapper
|
||||
// constructors //
|
||||
//==============//
|
||||
|
||||
public static ServerLevelWrapper getWrapper(ServerLevel level) { return LEVEL_WRAPPER_BY_SERVER_LEVEL.computeIfAbsent(level, ServerLevelWrapper::new); }
|
||||
public static ServerLevelWrapper getWrapper(ServerLevel level)
|
||||
{ return LEVEL_WRAPPER_BY_SERVER_LEVEL.computeIfAbsent(level, ServerLevelWrapper::new); }
|
||||
|
||||
public ServerLevelWrapper(ServerLevel level)
|
||||
{
|
||||
this.level = level;
|
||||
}
|
||||
public ServerLevelWrapper(ServerLevel level) { this.level = level; }
|
||||
|
||||
|
||||
|
||||
@@ -76,108 +80,141 @@ public class ServerLevelWrapper implements IServerLevelWrapper
|
||||
// methods //
|
||||
//=========//
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IClientLevelWrapper tryGetClientLevelWrapper()
|
||||
public File getMcSaveFolder()
|
||||
{
|
||||
MinecraftClientWrapper client = MinecraftClientWrapper.INSTANCE;
|
||||
if (client.mc.level == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return ClientLevelWrapper.getWrapper(client.mc.level);
|
||||
#if MC_VER < MC_1_21_3
|
||||
return this.level.getChunkSource().getDataStorage().dataFolder;
|
||||
#else
|
||||
return this.level.getChunkSource().getDataStorage().dataFolder.toFile();
|
||||
#endif
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getSaveFolder()
|
||||
public String getWorldFolderName()
|
||||
{
|
||||
return level.getChunkSource().getDataStorage().dataFolder;
|
||||
#if MC_VER >= MC_1_17_1
|
||||
return this.level.getServer().getWorldScreenshotFile().get().getParent().getFileName().toString();
|
||||
#else // <= 1.16.5
|
||||
return this.level.getServer().getWorldScreenshotFile().getParentFile().getName();
|
||||
#endif
|
||||
}
|
||||
|
||||
@Override
|
||||
public DimensionTypeWrapper getDimensionType()
|
||||
{
|
||||
return DimensionTypeWrapper.getDimensionTypeWrapper(level.dimensionType());
|
||||
}
|
||||
public DimensionTypeWrapper getDimensionType() { return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType()); }
|
||||
|
||||
@Override
|
||||
public String getDimensionName() { return this.level.dimension().location().toString(); }
|
||||
|
||||
@Override
|
||||
public long getHashedSeed() { return this.level.getBiomeManager().biomeZoomSeed; }
|
||||
|
||||
@Override
|
||||
public String getDhIdentifier() { return this.getDimensionName(); }
|
||||
|
||||
@Override
|
||||
public EDhApiLevelType getLevelType() { return EDhApiLevelType.SERVER_LEVEL; }
|
||||
|
||||
public ServerLevel getLevel()
|
||||
{
|
||||
return level;
|
||||
}
|
||||
public ServerLevel getLevel() { return this.level; }
|
||||
|
||||
@Override
|
||||
public boolean hasCeiling()
|
||||
{
|
||||
return level.dimensionType().hasCeiling();
|
||||
}
|
||||
public boolean hasCeiling() { return this.level.dimensionType().hasCeiling(); }
|
||||
|
||||
@Override
|
||||
public boolean hasSkyLight()
|
||||
{
|
||||
return level.dimensionType().hasSkyLight();
|
||||
}
|
||||
public boolean hasSkyLight() { return this.level.dimensionType().hasSkyLight(); }
|
||||
|
||||
@Override
|
||||
public int getHeight()
|
||||
{
|
||||
return level.getHeight();
|
||||
}
|
||||
public int getMaxHeight() { return this.level.getHeight(); }
|
||||
|
||||
@Override
|
||||
public int getMinHeight()
|
||||
{
|
||||
#if PRE_MC_1_17_1
|
||||
#if MC_VER < MC_1_17_1
|
||||
return 0;
|
||||
#elif MC_VER < MC_1_21_3
|
||||
return this.level.getMinBuildHeight();
|
||||
#else
|
||||
return level.getMinBuildHeight();
|
||||
return this.level.getMinY();
|
||||
#endif
|
||||
}
|
||||
|
||||
@Override
|
||||
public IChunkWrapper tryGetChunk(DhChunkPos pos)
|
||||
{
|
||||
if (!level.hasChunk(pos.x, pos.z)) return null;
|
||||
ChunkAccess chunk = level.getChunk(pos.x, pos.z, ChunkStatus.FULL, false);
|
||||
if (chunk == null) return null;
|
||||
return new ChunkWrapper(chunk, level, this);
|
||||
if (!this.level.hasChunk(pos.getX(), pos.getZ()))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ChunkAccess chunk = this.level.getChunk(pos.getX(), pos.getZ(), ChunkStatus.FULL, false);
|
||||
if (chunk == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ChunkWrapper(chunk, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasChunkLoaded(int chunkX, int chunkZ)
|
||||
{
|
||||
// world.hasChunk(chunkX, chunkZ); THIS DOES NOT WORK FOR CLIENT LEVEL CAUSE MOJANG ALWAYS RETURN TRUE FOR THAT!
|
||||
ChunkSource source = level.getChunkSource();
|
||||
ChunkSource source = this.level.getChunkSource();
|
||||
return source.hasChunk(chunkX, chunkZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBlockStateWrapper getBlockState(DhBlockPos pos)
|
||||
{
|
||||
return BlockStateWrapper.fromBlockState(level.getBlockState(McObjectConverter.Convert(pos)), this);
|
||||
return BlockStateWrapper.fromBlockState(this.level.getBlockState(McObjectConverter.Convert(pos)), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBiomeWrapper getBiome(DhBlockPos pos)
|
||||
{
|
||||
return BiomeWrapper.getBiomeWrapper(level.getBiome(McObjectConverter.Convert(pos)), this);
|
||||
return BiomeWrapper.getBiomeWrapper(this.level.getBiome(McObjectConverter.Convert(pos)), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerLevel getWrappedMcObject()
|
||||
{
|
||||
return level;
|
||||
}
|
||||
public ServerLevel getWrappedMcObject() { return this.level; }
|
||||
|
||||
@Override
|
||||
public void onUnload() { LEVEL_WRAPPER_BY_SERVER_LEVEL.remove(this.level); }
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
public void setParentLevel(IDhLevel parentLevel) { this.parentDhLevel = parentLevel; }
|
||||
|
||||
@Override
|
||||
public IDhApiCustomRenderRegister getRenderRegister()
|
||||
{
|
||||
return "Wrapped{" + level.toString() + "@" + getDimensionType().getDimensionName() + "}";
|
||||
if (this.parentDhLevel == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.parentDhLevel.getGenericRenderer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDhSaveFolder()
|
||||
{
|
||||
if (this.parentDhLevel == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.parentDhLevel.getSaveStructure().getSaveFolder(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// base overrides //
|
||||
//================//
|
||||
|
||||
@Override
|
||||
public String toString() { return "Wrapped{" + this.level.toString() + "@" + this.getDhIdentifier() + "}"; }
|
||||
|
||||
}
|
||||
|
||||
+1127
-597
File diff suppressed because it is too large
Load Diff
+69
-36
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -20,19 +20,16 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode;
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep;
|
||||
import com.seibel.distanthorizons.core.generation.WorldGenerationQueue;
|
||||
import com.seibel.distanthorizons.core.util.ThreadUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.util.objects.EventTimer;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPools;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -45,8 +42,10 @@ public final class GenerationEvent
|
||||
public final int id;
|
||||
public final ThreadedParameters threadedParam;
|
||||
public final DhChunkPos minPos;
|
||||
/** the number of chunks wide this event is */
|
||||
public final int size;
|
||||
public final EDhApiWorldGenerationStep targetGenerationStep;
|
||||
public final EDhApiDistantGeneratorMode generatorMode;
|
||||
public EventTimer timer = null;
|
||||
public long inQueueTime;
|
||||
public long timeoutTime = -1;
|
||||
@@ -57,12 +56,13 @@ public final class GenerationEvent
|
||||
|
||||
public GenerationEvent(
|
||||
DhChunkPos minPos, int size, BatchGenerationEnvironment generationGroup,
|
||||
EDhApiWorldGenerationStep targetGenerationStep, Consumer<IChunkWrapper> resultConsumer)
|
||||
EDhApiDistantGeneratorMode generatorMode, EDhApiWorldGenerationStep targetGenerationStep, Consumer<IChunkWrapper> resultConsumer)
|
||||
{
|
||||
this.inQueueTime = System.nanoTime();
|
||||
this.id = generationFutureDebugIDs++;
|
||||
this.minPos = minPos;
|
||||
this.size = size;
|
||||
this.generatorMode = generatorMode;
|
||||
this.targetGenerationStep = targetGenerationStep;
|
||||
this.threadedParam = ThreadedParameters.getOrMake(generationGroup.params);
|
||||
this.resultConsumer = resultConsumer;
|
||||
@@ -72,17 +72,11 @@ public final class GenerationEvent
|
||||
|
||||
public static GenerationEvent startEvent(
|
||||
DhChunkPos minPos, int size, BatchGenerationEnvironment genEnvironment,
|
||||
EDhApiWorldGenerationStep target, Consumer<IChunkWrapper> resultConsumer,
|
||||
EDhApiDistantGeneratorMode generatorMode, EDhApiWorldGenerationStep target, Consumer<IChunkWrapper> resultConsumer,
|
||||
ExecutorService worldGeneratorThreadPool)
|
||||
{
|
||||
if (size % 2 == 0)
|
||||
{
|
||||
size += 1; // size must be odd for vanilla world gen regions to work
|
||||
}
|
||||
|
||||
|
||||
GenerationEvent generationEvent = new GenerationEvent(minPos, size, genEnvironment, target, resultConsumer);
|
||||
generationEvent.future = CompletableFuture.runAsync(() ->
|
||||
GenerationEvent generationEvent = new GenerationEvent(minPos, size, genEnvironment, generatorMode, target, resultConsumer);
|
||||
generationEvent.future = CompletableFuture.supplyAsync(() ->
|
||||
{
|
||||
long runStartTime = System.nanoTime();
|
||||
generationEvent.timeoutTime = runStartTime;
|
||||
@@ -90,21 +84,75 @@ public final class GenerationEvent
|
||||
generationEvent.timer = new EventTimer("setup");
|
||||
|
||||
BatchGenerationEnvironment.isDistantGeneratorThread.set(true);
|
||||
|
||||
try
|
||||
{
|
||||
//LOGGER.info("generating [{}]", event.minPos);
|
||||
genEnvironment.generateLodFromList(generationEvent);
|
||||
genEnvironment.generateLodFromListAsync(generationEvent, (runnable) ->
|
||||
{
|
||||
worldGeneratorThreadPool.execute(() ->
|
||||
{
|
||||
boolean alreadyMarked = BatchGenerationEnvironment.isCurrentThreadDistantGeneratorThread();
|
||||
if (!alreadyMarked)
|
||||
{
|
||||
BatchGenerationEnvironment.isDistantGeneratorThread.set(true);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
runnable.run();
|
||||
}
|
||||
catch (Throwable throwable)
|
||||
{
|
||||
handleWorldGenThrowable(generationEvent, throwable);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!alreadyMarked)
|
||||
{
|
||||
BatchGenerationEnvironment.isDistantGeneratorThread.set(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
catch (InterruptedException ignored)
|
||||
catch (Throwable initialThrowable)
|
||||
{
|
||||
handleWorldGenThrowable(generationEvent, initialThrowable);
|
||||
}
|
||||
finally
|
||||
{
|
||||
BatchGenerationEnvironment.isDistantGeneratorThread.remove();
|
||||
}
|
||||
|
||||
return null;
|
||||
}, worldGeneratorThreadPool);
|
||||
return generationEvent;
|
||||
}
|
||||
/** There's probably a better way to handle this, but it'll work for now */
|
||||
private static void handleWorldGenThrowable(GenerationEvent generationEvent, Throwable initialThrowable)
|
||||
{
|
||||
Throwable throwable = initialThrowable;
|
||||
while (throwable instanceof CompletionException)
|
||||
{
|
||||
throwable = throwable.getCause();
|
||||
}
|
||||
|
||||
if (throwable instanceof InterruptedException
|
||||
|| throwable instanceof UncheckedInterruptedException
|
||||
|| throwable instanceof RejectedExecutionException)
|
||||
{
|
||||
// these exceptions can be ignored, generally they just mean
|
||||
// the thread is busy so it'll need to try again later.
|
||||
// FIXME this should cause the world gen task to be re-queued so we can try again later
|
||||
// however, currently it can cause large gaps in the world gen instead.
|
||||
// These gaps will generate correctly if the level is reloaded and the world gen is re-queued,
|
||||
// however this is makes it look like the generator isn't working or skipped something.
|
||||
}
|
||||
else
|
||||
{
|
||||
generationEvent.future.completeExceptionally(throwable);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isComplete() { return this.future.isDone(); }
|
||||
|
||||
@@ -123,26 +171,11 @@ public final class GenerationEvent
|
||||
public boolean terminate()
|
||||
{
|
||||
LOGGER.info("======================DUMPING ALL THREADS FOR WORLD GEN=======================");
|
||||
ThreadPools.WORLD_GEN_THREAD_FACTORY.dumpAllThreadStacks();
|
||||
ThreadPoolUtil.WORLD_GEN_THREAD_FACTORY.dumpAllThreadStacks();
|
||||
this.future.cancel(true);
|
||||
return this.future.isCancelled();
|
||||
}
|
||||
|
||||
public boolean tooClose(int minX, int minZ, int width)
|
||||
{
|
||||
int aMinX = this.minPos.x;
|
||||
int aMinZ = this.minPos.z;
|
||||
int aSize = this.size;
|
||||
// Account for required empty chunks in the border
|
||||
aSize += 1;
|
||||
width += 1;
|
||||
// Do a AABB to AABB intersection test
|
||||
return (aMinX + aSize >= minX &&
|
||||
aMinX <= minX + width &&
|
||||
aMinZ + aSize >= minZ &&
|
||||
aMinZ <= minZ + width);
|
||||
}
|
||||
|
||||
public void refreshTimeout()
|
||||
{
|
||||
this.timeoutTime = System.nanoTime();
|
||||
|
||||
+42
-34
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -31,16 +31,16 @@ import net.minecraft.server.level.ThreadedLevelLightEngine;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeManager;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
#if POST_MC_1_18_2
|
||||
#if MC_VER >= MC_1_18_2
|
||||
import net.minecraft.world.level.chunk.storage.ChunkScanAccess;
|
||||
#endif
|
||||
import net.minecraft.world.level.levelgen.WorldGenSettings;
|
||||
#if PRE_MC_1_19_2
|
||||
#if MC_VER < MC_1_19_2
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
|
||||
#else
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import net.minecraft.world.level.levelgen.RandomState;
|
||||
#if POST_MC_1_19_4
|
||||
#if MC_VER >= MC_1_19_4
|
||||
import net.minecraft.world.level.levelgen.WorldOptions;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
#endif
|
||||
@@ -50,24 +50,27 @@ import net.minecraft.world.level.storage.WorldData;
|
||||
public final class GlobalParameters
|
||||
{
|
||||
public final ChunkGenerator generator;
|
||||
#if PRE_MC_1_19_2
|
||||
public final StructureManager structures;
|
||||
#else
|
||||
public final StructureTemplateManager structures;
|
||||
public final RandomState randomState;
|
||||
#endif
|
||||
#if PRE_MC_1_19_4
|
||||
public final WorldGenSettings worldGenSettings;
|
||||
#else
|
||||
public final WorldOptions worldOptions;
|
||||
#endif
|
||||
public final IDhServerLevel lodLevel;
|
||||
public final ServerLevel level;
|
||||
public final Registry<Biome> biomes;
|
||||
public final RegistryAccess registry;
|
||||
public final long worldSeed;
|
||||
public final DataFixer fixerUpper;
|
||||
#if POST_MC_1_18_2
|
||||
|
||||
#if MC_VER < MC_1_19_2
|
||||
public final StructureManager structures;
|
||||
#else
|
||||
public final StructureTemplateManager structures;
|
||||
public final RandomState randomState;
|
||||
#endif
|
||||
|
||||
#if MC_VER < MC_1_19_4
|
||||
public final WorldGenSettings worldGenSettings;
|
||||
#else
|
||||
public final WorldOptions worldOptions;
|
||||
#endif
|
||||
|
||||
#if MC_VER >= MC_1_18_2
|
||||
public final BiomeManager biomeManager;
|
||||
public final ChunkScanAccess chunkScanner; // FIXME: Figure out if this is actually needed
|
||||
#endif
|
||||
@@ -76,29 +79,34 @@ public final class GlobalParameters
|
||||
{
|
||||
this.lodLevel = lodLevel;
|
||||
|
||||
level = ((ServerLevelWrapper) lodLevel.getServerLevelWrapper()).getWrappedMcObject();
|
||||
MinecraftServer server = level.getServer();
|
||||
this.level = ((ServerLevelWrapper) lodLevel.getServerLevelWrapper()).getWrappedMcObject();
|
||||
MinecraftServer server = this.level.getServer();
|
||||
WorldData worldData = server.getWorldData();
|
||||
registry = server.registryAccess();
|
||||
this.registry = server.registryAccess();
|
||||
|
||||
#if PRE_MC_1_19_4
|
||||
worldGenSettings = worldData.worldGenSettings();
|
||||
biomes = registry.registryOrThrow(Registry.BIOME_REGISTRY);
|
||||
worldSeed = worldGenSettings.seed();
|
||||
#if MC_VER < MC_1_19_4
|
||||
this.worldGenSettings = worldData.worldGenSettings();
|
||||
this.biomes = registry.registryOrThrow(Registry.BIOME_REGISTRY);
|
||||
this.worldSeed = worldGenSettings.seed();
|
||||
#elif MC_VER < MC_1_21_3
|
||||
this.worldOptions = worldData.worldGenOptions();
|
||||
this.biomes = registry.registryOrThrow(Registries.BIOME);
|
||||
this.worldSeed = worldOptions.seed();
|
||||
#else
|
||||
worldOptions = worldData.worldGenOptions();
|
||||
biomes = registry.registryOrThrow(Registries.BIOME);
|
||||
worldSeed = worldOptions.seed();
|
||||
this.worldOptions = worldData.worldGenOptions();
|
||||
this.biomes = this.registry.lookupOrThrow(Registries.BIOME);
|
||||
this.worldSeed = this.worldOptions.seed();
|
||||
#endif
|
||||
#if POST_MC_1_18_2
|
||||
biomeManager = new BiomeManager(level, BiomeManager.obfuscateSeed(worldSeed));
|
||||
chunkScanner = level.getChunkSource().chunkScanner();
|
||||
|
||||
#if MC_VER >= MC_1_18_2
|
||||
this.biomeManager = new BiomeManager(this.level, BiomeManager.obfuscateSeed(this.worldSeed));
|
||||
this.chunkScanner = this.level.getChunkSource().chunkScanner();
|
||||
#endif
|
||||
structures = server.getStructureManager();
|
||||
generator = level.getChunkSource().getGenerator();
|
||||
fixerUpper = server.getFixerUpper();
|
||||
#if POST_MC_1_19_2
|
||||
randomState = level.getChunkSource().randomState();
|
||||
this.structures = server.getStructureManager();
|
||||
this.generator = this.level.getChunkSource().getGenerator();
|
||||
this.fixerUpper = server.getFixerUpper();
|
||||
#if MC_VER >= MC_1_19_2
|
||||
this.randomState = this.level.getChunkSource().randomState();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
|
||||
+8
-8
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -25,7 +25,7 @@ import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.Wo
|
||||
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
#if POST_MC_1_18_2
|
||||
#if MC_VER >= MC_1_18_2
|
||||
import net.minecraft.world.level.levelgen.structure.StructureCheck;
|
||||
#endif
|
||||
|
||||
@@ -35,7 +35,7 @@ public final class ThreadedParameters
|
||||
|
||||
final ServerLevel level;
|
||||
public WorldGenStructFeatManager structFeat = null;
|
||||
#if POST_MC_1_18_2
|
||||
#if MC_VER >= MC_1_18_2
|
||||
public StructureCheck structCheck;
|
||||
#endif
|
||||
boolean isValid = true;
|
||||
@@ -63,9 +63,9 @@ public final class ThreadedParameters
|
||||
previousGlobalParameters = param;
|
||||
|
||||
this.level = param.level;
|
||||
#if PRE_MC_1_18_2
|
||||
#if MC_VER < MC_1_18_2
|
||||
this.structFeat = new WorldGenStructFeatManager(param.worldGenSettings, level);
|
||||
#elif PRE_MC_1_19_2
|
||||
#elif MC_VER < MC_1_19_2
|
||||
this.structCheck = this.createStructureCheck(param);
|
||||
#else
|
||||
this.structCheck = new StructureCheck(param.chunkScanner, param.registry, param.structures,
|
||||
@@ -80,15 +80,15 @@ public final class ThreadedParameters
|
||||
|
||||
public void makeStructFeat(WorldGenLevel genLevel, GlobalParameters param)
|
||||
{
|
||||
#if PRE_MC_1_19_4
|
||||
structFeat = new WorldGenStructFeatManager(param.worldGenSettings, genLevel #if POST_MC_1_18_2 , structCheck #endif );
|
||||
#if MC_VER < MC_1_19_4
|
||||
structFeat = new WorldGenStructFeatManager(param.worldGenSettings, genLevel #if MC_VER >= MC_1_18_2 , structCheck #endif );
|
||||
#else
|
||||
structFeat = new WorldGenStructFeatManager(param.worldOptions, genLevel, structCheck);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#if POST_MC_1_18_2 && PRE_MC_1_19_2
|
||||
#if MC_VER >= MC_1_18_2 && MC_VER < MC_1_19_2
|
||||
public void recreateStructureCheck()
|
||||
{
|
||||
if (previousGlobalParameters != null)
|
||||
|
||||
+450
-168
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -19,32 +19,29 @@
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.Dynamic;
|
||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
|
||||
|
||||
import com.seibel.distanthorizons.core.logging.ConfigBasedLogger;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
|
||||
import it.unimi.dsi.fastutil.shorts.ShortList;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.SectionPos;
|
||||
#if POST_MC_1_19_4
|
||||
#if MC_VER >= MC_1_19_4
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
#endif
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.nbt.NbtOps;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.*;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
@@ -53,37 +50,49 @@ import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.*;
|
||||
|
||||
#if MC_VER < MC_1_21_3
|
||||
import net.minecraft.world.level.chunk.storage.ChunkSerializer;
|
||||
#else
|
||||
#endif
|
||||
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
#if POST_MC_1_18_2
|
||||
#if MC_VER >= MC_1_18_2
|
||||
import net.minecraft.world.level.levelgen.blending.BlendingData;
|
||||
#if PRE_MC_1_19_2
|
||||
#if MC_VER < MC_1_19_2
|
||||
import net.minecraft.world.level.levelgen.feature.StructureFeature;
|
||||
#endif
|
||||
import net.minecraft.world.level.levelgen.structure.StructureStart;
|
||||
import net.minecraft.world.level.levelgen.structure.pieces.StructurePieceSerializationContext;
|
||||
import net.minecraft.world.ticks.LevelChunkTicks;
|
||||
#endif
|
||||
#if POST_MC_1_18_2
|
||||
#if MC_VER >= MC_1_18_2
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
#if PRE_MC_1_19_2
|
||||
#if MC_VER < MC_1_19_2
|
||||
import net.minecraft.world.level.levelgen.feature.ConfiguredStructureFeature;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if MC_1_16_5 || MC_1_17_1
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
import net.minecraft.world.level.material.Fluids;
|
||||
#endif
|
||||
|
||||
#if MC_VER == MC_1_20_6
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.status.ChunkType;
|
||||
#elif MC_VER >= MC_1_21_1
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.status.ChunkType;
|
||||
#endif
|
||||
|
||||
import net.minecraft.world.level.material.Fluid;
|
||||
|
||||
|
||||
public class ChunkLoader
|
||||
{
|
||||
#if POST_MC_1_19_2
|
||||
private static boolean zeroChunkPosErrorLogged = false;
|
||||
|
||||
#if MC_VER >= MC_1_19_2
|
||||
private static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codecRW(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState());
|
||||
#elif POST_MC_1_18_2
|
||||
#elif MC_VER >= MC_1_18_2
|
||||
private static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codec(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState());
|
||||
#endif
|
||||
private static final String TAG_UPGRADE_DATA = "UpgradeData";
|
||||
@@ -93,137 +102,19 @@ public class ChunkLoader
|
||||
private static final String FLUID_TICKS_TAG_PRE18 = "LiquidTicks";
|
||||
private static final ConfigBasedLogger LOGGER = BatchGenerationEnvironment.LOAD_LOGGER;
|
||||
|
||||
#if POST_MC_1_18_2
|
||||
private static BlendingData readBlendingData(CompoundTag chunkData)
|
||||
{
|
||||
BlendingData blendingData = null;
|
||||
if (chunkData.contains("blending_data", 10))
|
||||
{
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
Dynamic<CompoundTag> blendingDataTag = new Dynamic(NbtOps.INSTANCE, chunkData.getCompound("blending_data"));
|
||||
blendingData = BlendingData.CODEC.parse(blendingDataTag).resultOrPartial(LOGGER::error).orElse(null);
|
||||
}
|
||||
return blendingData;
|
||||
}
|
||||
#endif
|
||||
private static boolean lightingSectionErrorLogged = false;
|
||||
|
||||
private static LevelChunkSection[] readSections(LevelAccessor level, ChunkPos chunkPos, CompoundTag chunkData)
|
||||
{
|
||||
#if POST_MC_1_18_2
|
||||
#if PRE_MC_1_19_4
|
||||
Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY);
|
||||
#else
|
||||
Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registries.BIOME);
|
||||
#endif
|
||||
#if PRE_MC_1_18_2
|
||||
Codec<PalettedContainer<Biome>> biomeCodec = PalettedContainer.codec(
|
||||
biomes, biomes.byNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getOrThrow(Biomes.PLAINS));
|
||||
#elif PRE_MC_1_19_2
|
||||
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codec(
|
||||
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS));
|
||||
#else
|
||||
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codecRW(
|
||||
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS));
|
||||
#endif
|
||||
#endif
|
||||
int i = #if PRE_MC_1_17_1 16; #else level.getSectionsCount(); #endif
|
||||
LevelChunkSection[] chunkSections = new LevelChunkSection[i];
|
||||
private static final ConcurrentHashMap<String, Object> LOGGED_ERROR_MESSAGE_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
boolean isLightOn = chunkData.getBoolean("isLightOn");
|
||||
boolean hasSkyLight = level.dimensionType().hasSkyLight();
|
||||
ListTag tagSections = chunkData.getList("Sections", 10);
|
||||
if (tagSections.isEmpty()) tagSections = chunkData.getList("sections", 10);
|
||||
|
||||
for (int j = 0; j < tagSections.size(); ++j)
|
||||
{
|
||||
CompoundTag tagSection = tagSections.getCompound(j);
|
||||
int sectionYPos = tagSection.getByte("Y");
|
||||
|
||||
#if PRE_MC_1_18_2
|
||||
if (tagSection.contains("Palette", 9) && tagSection.contains("BlockStates", 12))
|
||||
{
|
||||
LevelChunkSection levelChunkSection = new LevelChunkSection(sectionYPos << 4);
|
||||
levelChunkSection.getStates().read(tagSection.getList("Palette", 10),
|
||||
tagSection.getLongArray("BlockStates"));
|
||||
levelChunkSection.recalcBlockCounts();
|
||||
if (!levelChunkSection.isEmpty())
|
||||
chunkSections[#if PRE_MC_1_17_1 sectionYPos #else level.getSectionIndexFromSectionY(sectionYPos) #endif ]
|
||||
= levelChunkSection;
|
||||
}
|
||||
#else
|
||||
int sectionId = level.getSectionIndexFromSectionY(sectionYPos);
|
||||
if (sectionId >= 0 && sectionId < chunkSections.length)
|
||||
{
|
||||
PalettedContainer<BlockState> blockStateContainer;
|
||||
#if PRE_MC_1_18_2
|
||||
PalettedContainer<Biome> biomeContainer;
|
||||
#else
|
||||
PalettedContainer<Holder<Biome>> biomeContainer;
|
||||
#endif
|
||||
|
||||
blockStateContainer = tagSection.contains("block_states", 10)
|
||||
? BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, tagSection.getCompound("block_states")).promotePartial(string -> logErrors(chunkPos, sectionYPos, string)).getOrThrow(false, LOGGER::error)
|
||||
: new PalettedContainer<BlockState>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
|
||||
|
||||
#if PRE_MC_1_18_2
|
||||
biomeContainer = tagSection.contains("biomes", 10)
|
||||
? biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes")).promotePartial(string -> logErrors(chunkPos, sectionYPos, string)).getOrThrow(false, LOGGER::error)
|
||||
: new PalettedContainer<Biome>(biomes, biomes.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
|
||||
#else
|
||||
biomeContainer = tagSection.contains("biomes", 10)
|
||||
? biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes")).promotePartial(string -> logErrors(chunkPos, i, (String) string)).getOrThrow(false, LOGGER::error)
|
||||
: new PalettedContainer<Holder<Biome>>(biomes.asHolderIdMap(), biomes.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
|
||||
#endif
|
||||
#if PRE_MC_1_20_1
|
||||
chunkSections[sectionId] = new LevelChunkSection(sectionYPos, blockStateContainer, biomeContainer);
|
||||
#else
|
||||
chunkSections[sectionId] = new LevelChunkSection(blockStateContainer, biomeContainer);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
return chunkSections;
|
||||
}
|
||||
|
||||
private static void readHeightmaps(LevelChunk chunk, CompoundTag chunkData)
|
||||
{
|
||||
CompoundTag tagHeightmaps = chunkData.getCompound("Heightmaps");
|
||||
for (Heightmap.Types type : ChunkStatus.FULL.heightmapsAfter())
|
||||
{
|
||||
String heightmap = type.getSerializationKey();
|
||||
if (tagHeightmaps.contains(heightmap, 12))
|
||||
chunk.setHeightmap(type, tagHeightmaps.getLongArray(heightmap));
|
||||
}
|
||||
Heightmap.primeHeightmaps(chunk, ChunkStatus.FULL.heightmapsAfter());
|
||||
}
|
||||
|
||||
private static void readPostPocessings(LevelChunk chunk, CompoundTag chunkData)
|
||||
{
|
||||
ListTag tagPostProcessings = chunkData.getList("PostProcessing", 9);
|
||||
for (int n = 0; n < tagPostProcessings.size(); ++n)
|
||||
{
|
||||
ListTag listTag3 = tagPostProcessings.getList(n);
|
||||
for (int o = 0; o < listTag3.size(); ++o)
|
||||
{
|
||||
chunk.addPackedPostProcess(listTag3.getShort(o), n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ChunkStatus.ChunkType readChunkType(CompoundTag tagLevel)
|
||||
{
|
||||
ChunkStatus chunkStatus = ChunkStatus.byName(tagLevel.getString("Status"));
|
||||
if (chunkStatus != null)
|
||||
{
|
||||
return chunkStatus.getChunkType();
|
||||
}
|
||||
return ChunkStatus.ChunkType.PROTOCHUNK;
|
||||
}
|
||||
//============//
|
||||
// read chunk //
|
||||
//============//
|
||||
|
||||
public static LevelChunk read(WorldGenLevel level, ChunkPos chunkPos, CompoundTag chunkData)
|
||||
{
|
||||
#if PRE_MC_1_18_2
|
||||
#if MC_VER < MC_1_18_2
|
||||
CompoundTag tagLevel = chunkData.getCompound("Level");
|
||||
#else
|
||||
CompoundTag tagLevel = chunkData;
|
||||
@@ -232,50 +123,82 @@ public class ChunkLoader
|
||||
ChunkPos actualPos = new ChunkPos(tagLevel.getInt("xPos"), tagLevel.getInt("zPos"));
|
||||
if (!Objects.equals(chunkPos, actualPos))
|
||||
{
|
||||
LOGGER.error("Chunk file at {} is in the wrong location; Ignoring. (Expected {}, got {})", chunkPos, chunkPos, actualPos);
|
||||
return null;
|
||||
#if MC_VER >= MC_1_18_2
|
||||
if (actualPos.equals(ChunkPos.ZERO))
|
||||
#else
|
||||
if (actualPos.equals(ChunkPos.INVALID_CHUNK_POS))
|
||||
#endif
|
||||
{
|
||||
if (!zeroChunkPosErrorLogged)
|
||||
{
|
||||
zeroChunkPosErrorLogged = true;
|
||||
|
||||
// explicit chunkPos toString is necessary otherwise the JDK 17 compiler breaks
|
||||
LOGGER.warn("Chunk file at ["+chunkPos.toString()+"] doesn't have a chunk pos. \n" +
|
||||
"This might happen if the world was created using an external program. \n" +
|
||||
"DH will attempt to parse the chunk anyway and won't log this message again.\n" +
|
||||
"If issues arise please try optimizing your world to fix this issue. \n" +
|
||||
"World optimization can be done from the singleplayer world selection screen."+
|
||||
"");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// everything is on one line to fix a JDK 17 compiler issue
|
||||
// if the issue is ever resolved, feel free to make this multi-line for readability
|
||||
LOGGER.error("Chunk file at ["+chunkPos.toString()+"] is in the wrong location. \nPlease try optimizing your world to fix this issue. \nWorld optimization can be done from the singleplayer world selection screen. \n(Expected pos: ["+chunkPos.toString()+"], actual ["+actualPos.toString()+"])");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
ChunkStatus.ChunkType chunkType = readChunkType(tagLevel);
|
||||
#if PRE_MC_1_18_2
|
||||
if (chunkType != ChunkStatus.ChunkType.LEVELCHUNK)
|
||||
return null;
|
||||
#if MC_VER < MC_1_20_6
|
||||
ChunkStatus.ChunkType chunkType;
|
||||
#else
|
||||
BlendingData blendingData = readBlendingData(tagLevel);
|
||||
#if PRE_MC_1_19_2
|
||||
if (chunkType == ChunkStatus.ChunkType.PROTOCHUNK && (blendingData == null || !blendingData.oldNoise()))
|
||||
return null;
|
||||
#else
|
||||
if (chunkType == ChunkStatus.ChunkType.PROTOCHUNK && blendingData == null)
|
||||
return null;
|
||||
ChunkType chunkType;
|
||||
#endif
|
||||
chunkType = readChunkType(tagLevel);
|
||||
|
||||
#if MC_VER < MC_1_18_2
|
||||
if (chunkType != ChunkStatus.ChunkType.LEVELCHUNK)
|
||||
return null;
|
||||
#else
|
||||
|
||||
BlendingData blendingData = readBlendingData(tagLevel);
|
||||
#if MC_VER < MC_1_19_2
|
||||
if (chunkType == ChunkStatus.ChunkType.PROTOCHUNK && (blendingData == null || !blendingData.oldNoise()))
|
||||
return null;
|
||||
#else
|
||||
if (chunkType == #if MC_VER < MC_1_20_6 ChunkStatus.ChunkType.PROTOCHUNK #else ChunkType.PROTOCHUNK #endif && blendingData == null)
|
||||
return null;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
long inhabitedTime = tagLevel.getLong("InhabitedTime");
|
||||
|
||||
//================== Read params for making the LevelChunk ==================
|
||||
UpgradeData upgradeData = tagLevel.contains(TAG_UPGRADE_DATA, 10)
|
||||
? new UpgradeData(tagLevel.getCompound(TAG_UPGRADE_DATA)#if POST_MC_1_17_1 , level #endif )
|
||||
? new UpgradeData(tagLevel.getCompound(TAG_UPGRADE_DATA)#if MC_VER >= MC_1_17_1 , level #endif )
|
||||
: UpgradeData.EMPTY;
|
||||
|
||||
boolean isLightOn = tagLevel.getBoolean("isLightOn");
|
||||
#if PRE_MC_1_18_2
|
||||
#if MC_VER < MC_1_18_2
|
||||
ChunkBiomeContainer chunkBiomeContainer = new ChunkBiomeContainer(
|
||||
level.getLevel().registryAccess().registryOrThrow(Registry.BIOME_REGISTRY)#if POST_MC_1_17_1 , level #endif ,
|
||||
level.getLevel().registryAccess().registryOrThrow(Registry.BIOME_REGISTRY)#if MC_VER >= MC_1_17_1 , level #endif ,
|
||||
chunkPos, level.getLevel().getChunkSource().getGenerator().getBiomeSource(),
|
||||
tagLevel.contains("Biomes", 11) ? tagLevel.getIntArray("Biomes") : null);
|
||||
|
||||
TickList<Block> blockTicks = tagLevel.contains(BLOCK_TICKS_TAG_PRE18, 9)
|
||||
? ChunkTickList.create(tagLevel.getList(BLOCK_TICKS_TAG_PRE18, 10), Registry.BLOCK::getKey, Registry.BLOCK::get)
|
||||
: new ProtoTickList<Block>(block -> (block == null || block.defaultBlockState().isAir()), chunkPos,
|
||||
tagLevel.getList("ToBeTicked", 9)#if POST_MC_1_17_1 , level #endif );
|
||||
tagLevel.getList("ToBeTicked", 9)#if MC_VER >= MC_1_17_1 , level #endif );
|
||||
|
||||
TickList<Fluid> fluidTicks = tagLevel.contains(FLUID_TICKS_TAG_PRE18, 9)
|
||||
? ChunkTickList.create(tagLevel.getList(FLUID_TICKS_TAG_PRE18, 10), Registry.FLUID::getKey, Registry.FLUID::get)
|
||||
: new ProtoTickList<Fluid>(fluid -> (fluid == null || fluid == Fluids.EMPTY), chunkPos,
|
||||
tagLevel.getList("LiquidsToBeTicked", 9)#if POST_MC_1_17_1 , level #endif );
|
||||
tagLevel.getList("LiquidsToBeTicked", 9)#if MC_VER >= MC_1_17_1 , level #endif );
|
||||
#else
|
||||
#if PRE_MC_1_19_4
|
||||
#if MC_VER < MC_1_19_4
|
||||
LevelChunkTicks<Block> blockTicks = LevelChunkTicks.load(tagLevel.getList(BLOCK_TICKS_TAG_18, 10),
|
||||
string -> Registry.BLOCK.getOptional(ResourceLocation.tryParse(string)), chunkPos);
|
||||
LevelChunkTicks<Fluid> fluidTicks = LevelChunkTicks.load(tagLevel.getList(FLUID_TICKS_TAG_18, 10),
|
||||
@@ -291,7 +214,7 @@ public class ChunkLoader
|
||||
LevelChunkSection[] levelChunkSections = readSections(level, chunkPos, tagLevel);
|
||||
|
||||
// ====================== Make the chunk =========================
|
||||
#if PRE_MC_1_18_2
|
||||
#if MC_VER < MC_1_18_2
|
||||
LevelChunk chunk = new LevelChunk((Level) level.getLevel(), chunkPos, chunkBiomeContainer, upgradeData, blockTicks,
|
||||
fluidTicks, inhabitedTime, levelChunkSections, null);
|
||||
#else
|
||||
@@ -305,10 +228,369 @@ public class ChunkLoader
|
||||
readPostPocessings(chunk, chunkData);
|
||||
return chunk;
|
||||
}
|
||||
|
||||
private static void logErrors(ChunkPos chunkPos, int i, String string)
|
||||
private static LevelChunkSection[] readSections(LevelAccessor level, ChunkPos chunkPos, CompoundTag chunkData)
|
||||
{
|
||||
LOGGER.error("Distant Horizons: Recoverable errors when loading section [" + chunkPos.x + ", " + i + ", " + chunkPos.z + "]: " + string);
|
||||
#if MC_VER >= MC_1_18_2
|
||||
#if MC_VER < MC_1_19_4
|
||||
Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY);
|
||||
#elif MC_VER < MC_1_21_3
|
||||
Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registries.BIOME);
|
||||
#else
|
||||
Registry<Biome> biomes = level.registryAccess().lookupOrThrow(Registries.BIOME);
|
||||
#endif
|
||||
#if MC_VER < MC_1_18_2
|
||||
Codec<PalettedContainer<Biome>> biomeCodec = PalettedContainer.codec(
|
||||
biomes, biomes.byNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getOrThrow(Biomes.PLAINS));
|
||||
#elif MC_VER < MC_1_19_2
|
||||
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codec(
|
||||
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS));
|
||||
#elif MC_VER < MC_1_21_3
|
||||
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codecRW(
|
||||
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS));
|
||||
#else
|
||||
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codecRW(
|
||||
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getOrThrow(Biomes.PLAINS));
|
||||
#endif
|
||||
#endif
|
||||
int sectionYIndex = #if MC_VER < MC_1_17_1 16; #else level.getSectionsCount(); #endif
|
||||
LevelChunkSection[] chunkSections = new LevelChunkSection[sectionYIndex];
|
||||
|
||||
boolean isLightOn = chunkData.getBoolean("isLightOn");
|
||||
boolean hasSkyLight = level.dimensionType().hasSkyLight();
|
||||
ListTag tagSections = chunkData.getList("Sections", 10);
|
||||
if (tagSections.isEmpty()) tagSections = chunkData.getList("sections", 10);
|
||||
|
||||
for (int j = 0; j < tagSections.size(); ++j)
|
||||
{
|
||||
CompoundTag tagSection = tagSections.getCompound(j);
|
||||
int sectionYPos = tagSection.getByte("Y");
|
||||
|
||||
#if MC_VER < MC_1_18_2
|
||||
if (tagSection.contains("Palette", 9) && tagSection.contains("BlockStates", 12))
|
||||
{
|
||||
LevelChunkSection levelChunkSection = new LevelChunkSection(sectionYPos << 4);
|
||||
levelChunkSection.getStates().read(tagSection.getList("Palette", 10),
|
||||
tagSection.getLongArray("BlockStates"));
|
||||
levelChunkSection.recalcBlockCounts();
|
||||
if (!levelChunkSection.isEmpty())
|
||||
chunkSections[#if MC_VER < MC_1_17_1 sectionYPos #else level.getSectionIndexFromSectionY(sectionYPos) #endif ]
|
||||
= levelChunkSection;
|
||||
}
|
||||
#else
|
||||
int sectionId = level.getSectionIndexFromSectionY(sectionYPos);
|
||||
if (sectionId >= 0 && sectionId < chunkSections.length)
|
||||
{
|
||||
PalettedContainer<BlockState> blockStateContainer;
|
||||
#if MC_VER < MC_1_18_2
|
||||
PalettedContainer<Biome> biomeContainer;
|
||||
#else
|
||||
PalettedContainer<Holder<Biome>> biomeContainer;
|
||||
#endif
|
||||
|
||||
blockStateContainer = tagSection.contains("block_states", 10)
|
||||
? BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, tagSection.getCompound("block_states"))
|
||||
.promotePartial(string -> logBlockDeserializationWarning(chunkPos, sectionYPos, string))
|
||||
#if MC_VER < MC_1_20_6
|
||||
.getOrThrow(false, (message) -> logWarningOnce(message))
|
||||
#else
|
||||
.getOrThrow((message) -> logErrorAndReturnException(message))
|
||||
#endif
|
||||
: new PalettedContainer<BlockState>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
|
||||
|
||||
#if MC_VER < MC_1_18_2
|
||||
biomeContainer = tagSection.contains("biomes", 10)
|
||||
? biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes")).promotePartial(string -> logErrors(chunkPos, sectionYPos, string)).getOrThrow(false, (message) -> logWarningOnce(message))
|
||||
: new PalettedContainer<Biome>(biomes, biomes.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
|
||||
#else
|
||||
|
||||
|
||||
if (tagSection.contains("biomes", 10))
|
||||
{
|
||||
biomeContainer =
|
||||
biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes"))
|
||||
.promotePartial(string -> logBiomeDeserializationWarning(chunkPos, sectionYIndex, (String) string))
|
||||
#if MC_VER < MC_1_20_6
|
||||
.getOrThrow(false, (message) -> logWarningOnce(message));
|
||||
#else
|
||||
.getOrThrow((message) -> logErrorAndReturnException(message));
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
biomeContainer = new PalettedContainer<Holder<Biome>>(biomes.asHolderIdMap(),
|
||||
#if MC_VER < MC_1_21_3
|
||||
biomes.getHolderOrThrow(Biomes.PLAINS),
|
||||
#else
|
||||
biomes.getOrThrow(Biomes.PLAINS),
|
||||
#endif
|
||||
PalettedContainer.Strategy.SECTION_BIOMES);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if MC_VER < MC_1_20_1
|
||||
chunkSections[sectionId] = new LevelChunkSection(sectionYPos, blockStateContainer, biomeContainer);
|
||||
#else
|
||||
chunkSections[sectionId] = new LevelChunkSection(blockStateContainer, biomeContainer);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
return chunkSections;
|
||||
}
|
||||
private static
|
||||
#if MC_VER < MC_1_20_6 ChunkStatus.ChunkType
|
||||
#elif MC_VER < MC_1_21_1 ChunkType
|
||||
#else ChunkType #endif
|
||||
readChunkType(CompoundTag tagLevel)
|
||||
{
|
||||
ChunkStatus chunkStatus = ChunkStatus.byName(tagLevel.getString("Status"));
|
||||
if (chunkStatus != null)
|
||||
{
|
||||
return chunkStatus.getChunkType();
|
||||
}
|
||||
|
||||
return
|
||||
#if MC_VER <= MC_1_20_4 ChunkStatus.ChunkType.PROTOCHUNK;
|
||||
#else ChunkType.PROTOCHUNK; #endif
|
||||
}
|
||||
private static void readHeightmaps(LevelChunk chunk, CompoundTag chunkData)
|
||||
{
|
||||
CompoundTag tagHeightmaps = chunkData.getCompound("Heightmaps");
|
||||
for (Heightmap.Types type : ChunkStatus.FULL.heightmapsAfter())
|
||||
{
|
||||
String heightmap = type.getSerializationKey();
|
||||
if (tagHeightmaps.contains(heightmap, 12))
|
||||
chunk.setHeightmap(type, tagHeightmaps.getLongArray(heightmap));
|
||||
}
|
||||
Heightmap.primeHeightmaps(chunk, ChunkStatus.FULL.heightmapsAfter());
|
||||
}
|
||||
private static void readPostPocessings(LevelChunk chunk, CompoundTag chunkData)
|
||||
{
|
||||
ListTag tagPostProcessings = chunkData.getList("PostProcessing", 9);
|
||||
for (int i = 0; i < tagPostProcessings.size(); ++i)
|
||||
{
|
||||
ListTag listTag3 = tagPostProcessings.getList(i);
|
||||
for (int j = 0; j < listTag3.size(); ++j)
|
||||
{
|
||||
#if MC_VER < MC_1_21_3
|
||||
chunk.addPackedPostProcess(listTag3.getShort(j), i);
|
||||
#else
|
||||
chunk.addPackedPostProcess(ShortList.of(listTag3.getShort(j)), i);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#if MC_VER >= MC_1_18_2
|
||||
private static BlendingData readBlendingData(CompoundTag chunkData)
|
||||
{
|
||||
BlendingData blendingData = null;
|
||||
if (chunkData.contains("blending_data", 10))
|
||||
{
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
Dynamic<CompoundTag> blendingDataTag = new Dynamic(NbtOps.INSTANCE, chunkData.getCompound("blending_data"));
|
||||
|
||||
#if MC_VER < MC_1_21_3
|
||||
blendingData = BlendingData.CODEC.parse(blendingDataTag).resultOrPartial((message) -> logWarningOnce(message)).orElse(null);
|
||||
#else
|
||||
blendingData = BlendingData.unpack(BlendingData.Packed.CODEC.parse(blendingDataTag).resultOrPartial((message) -> logWarningOnce(message)).orElse(null));
|
||||
#endif
|
||||
}
|
||||
return blendingData;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
//=====================//
|
||||
// read chunk lighting //
|
||||
//=====================//
|
||||
|
||||
/**
|
||||
* https://minecraft.wiki/w/Chunk_format
|
||||
*/
|
||||
public static CombinedChunkLightStorage readLight(ChunkAccess chunk, CompoundTag chunkData)
|
||||
{
|
||||
#if MC_VER <= MC_1_17_1
|
||||
// MC 1.16 and 1.17 doesn't have the necessary NBT info
|
||||
return null;
|
||||
#else
|
||||
|
||||
CombinedChunkLightStorage combinedStorage = new CombinedChunkLightStorage(ChunkWrapper.getInclusiveMinBuildHeight(chunk), ChunkWrapper.getExclusiveMaxBuildHeight(chunk));
|
||||
ChunkLightStorage blockLightStorage = combinedStorage.blockLightStorage;
|
||||
ChunkLightStorage skyLightStorage = combinedStorage.skyLightStorage;
|
||||
|
||||
boolean foundSkyLight = false;
|
||||
|
||||
|
||||
|
||||
//===================//
|
||||
// get NBT tags info //
|
||||
//===================//
|
||||
|
||||
Tag chunkSectionTags = chunkData.get("sections");
|
||||
if (chunkSectionTags == null)
|
||||
{
|
||||
if (!lightingSectionErrorLogged)
|
||||
{
|
||||
lightingSectionErrorLogged = true;
|
||||
LOGGER.error("No sections found for chunk at pos ["+chunk.getPos()+"] chunk data may be out of date.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
else if (!(chunkSectionTags instanceof ListTag))
|
||||
{
|
||||
if (!lightingSectionErrorLogged)
|
||||
{
|
||||
lightingSectionErrorLogged = true;
|
||||
LOGGER.error("Chunk section tag list have unexpected type ["+chunkSectionTags.getClass().getName()+"], expected ["+ListTag.class.getName()+"].");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
ListTag chunkSectionListTag = (ListTag) chunkSectionTags;
|
||||
|
||||
|
||||
|
||||
//===================//
|
||||
// get lighting info //
|
||||
//===================//
|
||||
|
||||
for (int sectionIndex = 0; sectionIndex < chunkSectionListTag.size(); sectionIndex++)
|
||||
{
|
||||
Tag chunkSectionTag = chunkSectionListTag.get(sectionIndex);
|
||||
if (!(chunkSectionTag instanceof CompoundTag))
|
||||
{
|
||||
if (!lightingSectionErrorLogged)
|
||||
{
|
||||
lightingSectionErrorLogged = true;
|
||||
LOGGER.error("Chunk section tag has an unexpected type ["+chunkSectionTag.getClass().getName()+"], expected ["+CompoundTag.class.getName()+"].");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
CompoundTag chunkSectionCompoundTag = (CompoundTag) chunkSectionTag;
|
||||
|
||||
|
||||
// if null all lights = 0
|
||||
byte[] blockLightNibbleArray = chunkSectionCompoundTag.getByteArray("BlockLight");
|
||||
byte[] skyLightNibbleArray = chunkSectionCompoundTag.getByteArray("SkyLight");
|
||||
|
||||
// if any sky light was found then all lights above will be max brightness
|
||||
if (skyLightNibbleArray.length != 0)
|
||||
{
|
||||
foundSkyLight = true;
|
||||
}
|
||||
|
||||
for (int relX = 0; relX < LodUtil.CHUNK_WIDTH; relX++)
|
||||
{
|
||||
for (int relZ = 0; relZ < LodUtil.CHUNK_WIDTH; relZ++)
|
||||
{
|
||||
// chunk sections are also 16 blocks tall
|
||||
for (int relY = 0; relY < LodUtil.CHUNK_WIDTH; relY++)
|
||||
{
|
||||
int blockPosIndex = relY*16*16 + relZ*16 + relX;
|
||||
byte blockLight = (blockLightNibbleArray.length == 0) ? 0 : getNibbleAtIndex(blockLightNibbleArray, blockPosIndex);
|
||||
byte skyLight = (skyLightNibbleArray.length == 0) ? 0 : getNibbleAtIndex(skyLightNibbleArray, blockPosIndex);
|
||||
if (skyLightNibbleArray.length == 0 && foundSkyLight)
|
||||
{
|
||||
skyLight = LodUtil.MAX_MC_LIGHT;
|
||||
}
|
||||
|
||||
int y = relY + (sectionIndex * LodUtil.CHUNK_WIDTH) + ChunkWrapper.getInclusiveMinBuildHeight(chunk);
|
||||
blockLightStorage.set(relX, y, relZ, blockLight);
|
||||
skyLightStorage.set(relX, y, relZ, skyLight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return combinedStorage;
|
||||
#endif
|
||||
}
|
||||
/** source: https://minecraft.wiki/w/Chunk_format#Block_Format */
|
||||
private static byte getNibbleAtIndex(byte[] arr, int index)
|
||||
{
|
||||
if (index % 2 == 0)
|
||||
{
|
||||
return (byte)(arr[index/2] & 0x0F);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (byte)((arr[index/2]>>4) & 0x0F);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// logging //
|
||||
//=========//
|
||||
|
||||
private static void logBlockDeserializationWarning(ChunkPos chunkPos, int sectionYIndex, String message)
|
||||
{
|
||||
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
|
||||
{
|
||||
LOGGER.warn("Unable to deserialize blocks for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+newMessage+"]. " +
|
||||
"This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
|
||||
|
||||
return newMessage;
|
||||
});
|
||||
}
|
||||
private static void logBiomeDeserializationWarning(ChunkPos chunkPos, int sectionYIndex, String message)
|
||||
{
|
||||
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
|
||||
{
|
||||
LOGGER.warn("Unable to deserialize biomes for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+newMessage+"]. " +
|
||||
"This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
|
||||
|
||||
return newMessage;
|
||||
});
|
||||
}
|
||||
|
||||
private static void logWarningOnce(String message) { logWarningOnce(message, null); }
|
||||
private static void logWarningOnce(String message, Exception e)
|
||||
{
|
||||
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
|
||||
{
|
||||
LOGGER.warn("Parsing error: ["+newMessage+"]. " +
|
||||
"This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.",
|
||||
e);
|
||||
|
||||
return newMessage;
|
||||
});
|
||||
}
|
||||
|
||||
private static RuntimeException logErrorAndReturnException(String message)
|
||||
{
|
||||
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
|
||||
{
|
||||
LOGGER.warn("Parsing error: ["+newMessage+"]. " +
|
||||
"This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
|
||||
|
||||
return newMessage;
|
||||
});
|
||||
|
||||
// Currently we want to ignore these errors, if returning null is a problem, we can change this later
|
||||
return null; //new RuntimeException(message);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper classes //
|
||||
//================//
|
||||
|
||||
public static class CombinedChunkLightStorage
|
||||
{
|
||||
public ChunkLightStorage blockLightStorage;
|
||||
public ChunkLightStorage skyLightStorage;
|
||||
|
||||
public CombinedChunkLightStorage(int minY, int maxY)
|
||||
{
|
||||
this.blockLightStorage = ChunkLightStorage.createBlockLightStorage(minY, maxY);
|
||||
this.skyLightStorage = ChunkLightStorage.createSkyLightStorage(minY, maxY);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
|
||||
|
||||
#if MC_VER >= MC_1_21_1
|
||||
|
||||
import net.minecraft.server.level.GenerationChunkHolder;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class DhGenerationChunkHolder extends GenerationChunkHolder
|
||||
{
|
||||
|
||||
public DhGenerationChunkHolder(ChunkPos pos) { super(pos); }
|
||||
|
||||
@Override
|
||||
public int getTicketLevel() { return 0; }
|
||||
@Override
|
||||
public int getQueueLevel() { return 0; }
|
||||
|
||||
#if MC_VER < MC_1_21_3
|
||||
#else
|
||||
@Override
|
||||
protected void addSaveDependency(CompletableFuture<?> completableFuture) { }
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
+176
-74
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -22,12 +22,14 @@ package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import net.minecraft.world.level.block.EntityBlock;
|
||||
import net.minecraft.world.level.block.SpawnerBlock;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
@@ -41,7 +43,7 @@ import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.ColorResolver;
|
||||
#if POST_MC_1_17_1
|
||||
#if MC_VER >= MC_1_17_1
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
#endif
|
||||
import net.minecraft.world.level.LightLayer;
|
||||
@@ -50,34 +52,58 @@ 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.ImposterProtoChunk;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.lighting.LevelLightEngine;
|
||||
|
||||
#if MC_VER <= MC_1_20_4
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
#else
|
||||
import net.minecraft.world.level.chunk.status.*;
|
||||
#endif
|
||||
|
||||
#if MC_VER >= MC_1_21_1
|
||||
import net.minecraft.util.StaticCache2D;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import net.minecraft.server.level.GenerationChunkHolder;
|
||||
#endif
|
||||
|
||||
#if MC_VER >= MC_1_18_2
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.material.Fluid;
|
||||
import net.minecraft.world.ticks.BlackholeTickAccess;
|
||||
import net.minecraft.world.ticks.LevelTickAccess;
|
||||
#endif
|
||||
|
||||
|
||||
public class DhLitWorldGenRegion extends WorldGenRegion
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
|
||||
|
||||
private static ChunkStatus debugTriggeredForStatus = null;
|
||||
|
||||
|
||||
public final ServerLevel serverLevel;
|
||||
public final DummyLightEngine lightEngine;
|
||||
public final BatchGenerationEnvironment.EmptyChunkGenerator generator;
|
||||
public final BatchGenerationEnvironment.IEmptyChunkRetrievalFunc generator;
|
||||
public final int writeRadius;
|
||||
public final int size;
|
||||
|
||||
private final ChunkPos firstPos;
|
||||
private final List<ChunkAccess> cache;
|
||||
Long2ObjectOpenHashMap<ChunkAccess> chunkMap = new Long2ObjectOpenHashMap<ChunkAccess>();
|
||||
private final Long2ObjectOpenHashMap<ChunkAccess> chunkMap = new Long2ObjectOpenHashMap<ChunkAccess>();
|
||||
|
||||
/**
|
||||
* Present to reduce the chance that we accidentally break underlying MC code that isn't thread safe,
|
||||
* specifically: "it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap.getAndMoveToFirst()"
|
||||
*/
|
||||
ReentrantLock getChunkLock = new ReentrantLock();
|
||||
private final ReentrantLock getChunkLock = new ReentrantLock();
|
||||
|
||||
#if PRE_MC_1_18_2
|
||||
#if MC_VER < MC_1_18_2
|
||||
private ChunkPos overrideCenterPos = null;
|
||||
|
||||
public void setOverrideCenter(ChunkPos pos) { overrideCenterPos = pos; }
|
||||
#if PRE_MC_1_17_1
|
||||
#if MC_VER < MC_1_17_1
|
||||
@Override
|
||||
public int getCenterX()
|
||||
{
|
||||
@@ -100,12 +126,31 @@ public class DhLitWorldGenRegion extends WorldGenRegion
|
||||
|
||||
|
||||
public DhLitWorldGenRegion(
|
||||
int centerChunkX, int centerChunkZ,
|
||||
ChunkAccess centerChunk,
|
||||
ServerLevel serverLevel, DummyLightEngine lightEngine,
|
||||
List<ChunkAccess> chunkList, ChunkStatus chunkStatus, int writeRadius,
|
||||
BatchGenerationEnvironment.EmptyChunkGenerator generator)
|
||||
BatchGenerationEnvironment.IEmptyChunkRetrievalFunc generator)
|
||||
{
|
||||
super(serverLevel, chunkList #if POST_MC_1_17_1 , chunkStatus, writeRadius #endif );
|
||||
#if MC_VER == MC_1_16_5
|
||||
super(serverLevel, chunkList);
|
||||
#elif MC_VER < MC_1_21_1
|
||||
super(serverLevel, chunkList, chunkStatus, writeRadius);
|
||||
#else
|
||||
super(serverLevel,
|
||||
StaticCache2D.create(
|
||||
centerChunkX, centerChunkZ,
|
||||
writeRadius * 2, (x,z) -> new DhGenerationChunkHolder(new ChunkPos(x, z))),
|
||||
new ChunkStep(chunkStatus,
|
||||
// reverse is needed because MC uses the index of the chunkStatus to determine how many items are in the list instead of the actual list count
|
||||
new ChunkDependencies(ImmutableList.copyOf(ChunkStatus.getStatusList()).reverse()),
|
||||
new ChunkDependencies(ImmutableList.copyOf(ChunkStatus.getStatusList()).reverse()),
|
||||
writeRadius, (WorldGenContext var1, ChunkStep var2, StaticCache2D<GenerationChunkHolder> var3, ChunkAccess var4) -> null),
|
||||
centerChunk);
|
||||
#endif
|
||||
|
||||
this.firstPos = chunkList.get(0).getPos();
|
||||
this.serverLevel = serverLevel;
|
||||
this.generator = generator;
|
||||
this.lightEngine = lightEngine;
|
||||
this.writeRadius = writeRadius;
|
||||
@@ -115,7 +160,7 @@ public class DhLitWorldGenRegion extends WorldGenRegion
|
||||
|
||||
|
||||
|
||||
#if POST_MC_1_17_1
|
||||
#if MC_VER >= MC_1_17_1
|
||||
// Bypass BCLib mixin overrides.
|
||||
@Override
|
||||
public boolean ensureCanWrite(BlockPos blockPos)
|
||||
@@ -130,11 +175,22 @@ public class DhLitWorldGenRegion extends WorldGenRegion
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#if POST_MC_1_18_2
|
||||
#if MC_VER >= MC_1_18_2
|
||||
if (center.isUpgrading())
|
||||
{
|
||||
LevelHeightAccessor levelHeightAccessor = center.getHeightAccessorForGeneration();
|
||||
if (blockPos.getY() < levelHeightAccessor.getMinBuildHeight() || blockPos.getY() >= levelHeightAccessor.getMaxBuildHeight())
|
||||
|
||||
int minY;
|
||||
int maxY;
|
||||
#if MC_VER < MC_1_21_3
|
||||
minY = levelHeightAccessor.getMinBuildHeight();
|
||||
maxY = levelHeightAccessor.getMaxBuildHeight();
|
||||
#else
|
||||
minY = levelHeightAccessor.getMinY();
|
||||
maxY = levelHeightAccessor.getMaxY();
|
||||
#endif
|
||||
|
||||
if (blockPos.getY() < minY || blockPos.getY() >= maxY)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -144,6 +200,22 @@ public class DhLitWorldGenRegion extends WorldGenRegion
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MC_VER >= MC_1_18_2
|
||||
@Override
|
||||
@NotNull
|
||||
public LevelTickAccess<Block> getBlockTicks()
|
||||
{
|
||||
// DH world gen doesn't need ticking, so return the BlackholeTickAccess list (which causes all ticks to be ignored).
|
||||
// If this isn't done the server may attempt to tick chunks outside the vanilla render distance,
|
||||
// which can throw warnings or cause other issues
|
||||
return BlackholeTickAccess.emptyLevelList();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public LevelTickAccess<Fluid> getFluidTicks() { return BlackholeTickAccess.emptyLevelList(); }
|
||||
#endif
|
||||
|
||||
// TODO Check this
|
||||
// @Override
|
||||
// public List<? extends StructureStart<?>> startsForFeature(SectionPos sectionPos,
|
||||
@@ -158,7 +230,7 @@ public class DhLitWorldGenRegion extends WorldGenRegion
|
||||
ChunkAccess chunkAccess = this.getChunk(blockPos);
|
||||
if (chunkAccess instanceof LevelChunk)
|
||||
return true;
|
||||
chunkAccess.setBlockState(blockPos, blockState, false);
|
||||
chunkAccess.setBlockState(blockPos, blockState, /*isBlockMoving*/false);
|
||||
// This is for post ticking for water on gen and stuff like that. Not enabled
|
||||
// for now.
|
||||
// if (blockState.hasPostProcess(this, blockPos))
|
||||
@@ -185,7 +257,7 @@ public class DhLitWorldGenRegion extends WorldGenRegion
|
||||
BlockState blockState = this.getBlockState(blockPos);
|
||||
|
||||
// This is a bypass for the spawner block since MC complains about not having it
|
||||
#if POST_MC_1_17_1
|
||||
#if MC_VER >= MC_1_17_1
|
||||
if (blockState.getBlock() instanceof SpawnerBlock)
|
||||
{
|
||||
return ((EntityBlock) blockState.getBlock()).newBlockEntity(blockPos, blockState);
|
||||
@@ -198,13 +270,26 @@ public class DhLitWorldGenRegion extends WorldGenRegion
|
||||
#endif
|
||||
}
|
||||
|
||||
// Skip BlockEntity stuff. It aren't really needed
|
||||
/**
|
||||
* This needs to be manually overridden to make sure Lithium 0.11.2 and lower
|
||||
* don't try to get null chunks. <br><br>
|
||||
*
|
||||
* Problematic Lithium code was removed in 0.13.0 (MC 1.21.1) and higher: <br>
|
||||
* https://github.com/CaffeineMC/lithium-fabric/commit/b7cfd53a1ed0197e1d13dea2799b898eb52ecab3
|
||||
*/
|
||||
@NotNull
|
||||
@Override
|
||||
public boolean addFreshEntity(Entity entity)
|
||||
public BlockState getBlockState(BlockPos blockPos)
|
||||
{
|
||||
return true;
|
||||
int chunkX = SectionPos.blockToSectionCoord(blockPos.getX());
|
||||
int chunkZ = SectionPos.blockToSectionCoord(blockPos.getZ());
|
||||
return this.getChunk(chunkX, chunkZ).getBlockState(blockPos);
|
||||
}
|
||||
|
||||
/** Skip BlockEntity stuff. They aren't needed for our use case. */
|
||||
@Override
|
||||
public boolean addFreshEntity(@NotNull Entity entity) { return true; }
|
||||
|
||||
// Allays have empty chunks even if it's outside the worldGenRegion
|
||||
// @Override
|
||||
// public boolean hasChunk(int i, int j) {
|
||||
@@ -214,13 +299,13 @@ public class DhLitWorldGenRegion extends WorldGenRegion
|
||||
// Override to ensure no other mod mixins cause skipping the overrided
|
||||
// getChunk(...)
|
||||
@Override
|
||||
public ChunkAccess getChunk(int i, int j)
|
||||
public @NotNull ChunkAccess getChunk(int chunkX, int chunkZ)
|
||||
{
|
||||
try
|
||||
{
|
||||
// lock is to prevent issues with underlying MC code that doesn't support multithreading
|
||||
this.getChunkLock.lock();
|
||||
return this.getChunk(i, j, ChunkStatus.EMPTY);
|
||||
return this.getChunk(chunkX, chunkZ, ChunkStatus.EMPTY);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -231,13 +316,19 @@ public class DhLitWorldGenRegion extends WorldGenRegion
|
||||
// Override to ensure no other mod mixins cause skipping the overrided
|
||||
// getChunk(...)
|
||||
@Override
|
||||
public ChunkAccess getChunk(int i, int j, ChunkStatus chunkStatus)
|
||||
public @NotNull ChunkAccess getChunk(int chunkX, int chunkZ, @NotNull ChunkStatus chunkStatus)
|
||||
{
|
||||
try
|
||||
{
|
||||
// lock is to prevent issues with underlying MC code that doesn't support multithreading
|
||||
this.getChunkLock.lock();
|
||||
return this.getChunk(i, j, chunkStatus, true);
|
||||
|
||||
ChunkAccess chunk = this.getChunk(chunkX, chunkZ, chunkStatus, true);
|
||||
if (chunk == null)
|
||||
{
|
||||
LodUtil.assertNotReach("getChunk shouldn't return null values");
|
||||
}
|
||||
return chunk;
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -245,102 +336,113 @@ public class DhLitWorldGenRegion extends WorldGenRegion
|
||||
}
|
||||
}
|
||||
|
||||
// Use this instead of super.getChunk() to bypass C2ME concurrency checks
|
||||
private ChunkAccess superGetChunk(int x, int z, ChunkStatus cs)
|
||||
{
|
||||
int k = x - firstPos.x;
|
||||
int l = z - firstPos.z;
|
||||
return cache.get(k + l * size);
|
||||
}
|
||||
|
||||
// Use this instead of super.hasChunk() to bypass C2ME concurrency checks
|
||||
public boolean superHasChunk(int x, int z)
|
||||
{
|
||||
int k = x - firstPos.x;
|
||||
int l = z - firstPos.z;
|
||||
return l >= 0 && l < size && k >= 0 && k < size;
|
||||
}
|
||||
|
||||
// Allow creating empty chunks even if it's outside the worldGenRegion
|
||||
/** Allows creating empty chunks even if they're outside the worldGenRegion */
|
||||
@Override
|
||||
@Nullable
|
||||
public ChunkAccess getChunk(int i, int j, ChunkStatus chunkStatus, boolean bl)
|
||||
public ChunkAccess getChunk(int chunkX, int chunkZ, @NotNull ChunkStatus chunkStatus, boolean returnNonNull)
|
||||
{
|
||||
ChunkAccess chunk = getChunkAccess(i, j, chunkStatus, bl);
|
||||
ChunkAccess chunk = this.getChunkAccess(chunkX, chunkZ, chunkStatus, returnNonNull);
|
||||
if (chunk instanceof LevelChunk)
|
||||
{
|
||||
chunk = new ImposterProtoChunk((LevelChunk) chunk #if POST_MC_1_18_2 , true #endif );
|
||||
chunk = new ImposterProtoChunk((LevelChunk) chunk #if MC_VER >= MC_1_18_2 , true #endif );
|
||||
}
|
||||
return chunk;
|
||||
}
|
||||
|
||||
private static ChunkStatus debugTriggeredForStatus = null;
|
||||
|
||||
private ChunkAccess getChunkAccess(int i, int j, ChunkStatus chunkStatus, boolean bl)
|
||||
/**
|
||||
* @param returnNonNull if true this method will always return a non-null chunk,
|
||||
* if false it will return null if no chunk exists at the given position with the given status
|
||||
*/
|
||||
private ChunkAccess getChunkAccess(int chunkX, int chunkZ, ChunkStatus chunkStatus, boolean returnNonNull)
|
||||
{
|
||||
ChunkAccess chunk = superHasChunk(i, j) ? superGetChunk(i, j, ChunkStatus.EMPTY) : null;
|
||||
if (chunk != null && chunk.getStatus().isOrAfter(chunkStatus))
|
||||
ChunkAccess chunk = this.superHasChunk(chunkX, chunkZ) ? this.superGetChunk(chunkX, chunkZ) : null;
|
||||
if (chunk != null && ChunkWrapper.getStatus(chunk).isOrAfter(chunkStatus))
|
||||
{
|
||||
return chunk;
|
||||
}
|
||||
if (!bl)
|
||||
else if (!returnNonNull)
|
||||
{
|
||||
// no chunk found with the necessary status and null return values are allowed
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// we need a non-null chunk
|
||||
if (chunk == null)
|
||||
{
|
||||
chunk = chunkMap.get(ChunkPos.asLong(i, j));
|
||||
// check memory
|
||||
chunk = this.chunkMap.get(ChunkPos.asLong(chunkX, chunkZ));
|
||||
if (chunk == null)
|
||||
{
|
||||
chunk = generator.generate(i, j);
|
||||
// chunk isn't in memory, generate a new one
|
||||
chunk = this.generator.getChunk(chunkX, chunkZ);
|
||||
if (chunk == null)
|
||||
{
|
||||
throw new NullPointerException("The provided generator should not return null!");
|
||||
chunkMap.put(ChunkPos.asLong(i, j), chunk);
|
||||
}
|
||||
this.chunkMap.put(ChunkPos.asLong(chunkX, chunkZ), chunk);
|
||||
}
|
||||
}
|
||||
|
||||
if (chunkStatus != ChunkStatus.EMPTY && chunkStatus != debugTriggeredForStatus)
|
||||
{
|
||||
LOGGER.info("WorldGen requiring " + chunkStatus
|
||||
+ " outside expected range detected. Force passing EMPTY chunk and seeing if it works.");
|
||||
debugTriggeredForStatus = chunkStatus;
|
||||
}
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
/** Overriding allows us to use our own lighting engine */
|
||||
@Override
|
||||
public LevelLightEngine getLightEngine() { return this.lightEngine; }
|
||||
|
||||
/** Overriding allows us to use our own lighting engine */
|
||||
@Override
|
||||
public int getBrightness(LightLayer lightLayer, BlockPos blockPos) { return 0; }
|
||||
|
||||
/** Overriding allows us to use our own lighting engine */
|
||||
@Override
|
||||
public int getRawBrightness(BlockPos blockPos, int i) { return 0; }
|
||||
|
||||
/** Overriding allows us to use our own lighting engine */
|
||||
@Override
|
||||
public boolean canSeeSky(BlockPos blockPos)
|
||||
/** Use this instead of super.hasChunk() to bypass C2ME concurrency checks */
|
||||
public boolean superHasChunk(int x, int z)
|
||||
{
|
||||
return (getBrightness(LightLayer.SKY, blockPos) >= getMaxLightLevel());
|
||||
int k = x - this.firstPos.x;
|
||||
int l = z - this.firstPos.z;
|
||||
return l >= 0 && l < this.size && k >= 0 && k < this.size;
|
||||
}
|
||||
|
||||
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver)
|
||||
/** Use this instead of super.getChunk() to bypass C2ME concurrency checks */
|
||||
private ChunkAccess superGetChunk(int x, int z)
|
||||
{
|
||||
return calculateBlockTint(blockPos, colorResolver);
|
||||
int k = x - this.firstPos.x;
|
||||
int l = z - this.firstPos.z;
|
||||
return this.cache.get(k + l * this.size);
|
||||
}
|
||||
|
||||
|
||||
/** Overriding allows us to use our own lighting engine */
|
||||
@Override
|
||||
public @NotNull LevelLightEngine getLightEngine() { return this.lightEngine; }
|
||||
|
||||
/** Overriding allows us to use our own lighting engine */
|
||||
@Override
|
||||
public int getBrightness(@NotNull LightLayer lightLayer, @NotNull BlockPos blockPos) { return 0; }
|
||||
|
||||
/** Overriding allows us to use our own lighting engine */
|
||||
@Override
|
||||
public int getRawBrightness(@NotNull BlockPos blockPos, int i) { return 0; }
|
||||
|
||||
/** Overriding allows us to use our own lighting engine */
|
||||
@Override
|
||||
public boolean canSeeSky(@NotNull BlockPos blockPos)
|
||||
{ return (this.getBrightness(LightLayer.SKY, blockPos) >= LodUtil.MAX_MC_LIGHT); }
|
||||
|
||||
public int getBlockTint(@NotNull BlockPos blockPos, @NotNull ColorResolver colorResolver)
|
||||
{ return this.calculateBlockTint(blockPos, colorResolver); }
|
||||
|
||||
private Biome _getBiome(BlockPos pos)
|
||||
{
|
||||
#if POST_MC_1_18_2
|
||||
return getBiome(pos).value();
|
||||
#if MC_VER >= MC_1_18_2
|
||||
return this.getBiome(pos).value();
|
||||
#else
|
||||
return getBiome(pos);
|
||||
return this.getBiome(pos);
|
||||
#endif
|
||||
}
|
||||
|
||||
public int calculateBlockTint(BlockPos blockPos, ColorResolver colorResolver)
|
||||
{
|
||||
#if PRE_MC_1_19_2
|
||||
#if MC_VER < MC_1_19_2
|
||||
int i = (Minecraft.getInstance()).options.biomeBlendRadius;
|
||||
#else
|
||||
int i = (Minecraft.getInstance()).options.biomeBlendRadius().get();
|
||||
|
||||
+4
-4
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -39,7 +39,7 @@ public class DummyLightEngine extends LevelLightEngine
|
||||
}
|
||||
|
||||
|
||||
#if PRE_MC_1_20_1
|
||||
#if MC_VER < MC_1_20_1
|
||||
@Override
|
||||
public void onBlockEmissionIncrease(BlockPos blockPos, int i) { }
|
||||
|
||||
@@ -63,7 +63,7 @@ public class DummyLightEngine extends LevelLightEngine
|
||||
#endif
|
||||
|
||||
@Override
|
||||
public void queueSectionData(LightLayer lightLayer, SectionPos sectionPos, @Nullable DataLayer dataLayer #if PRE_MC_1_20_1 , boolean bl #endif ) { }
|
||||
public void queueSectionData(LightLayer lightLayer, SectionPos sectionPos, @Nullable DataLayer dataLayer #if MC_VER < MC_1_20_1 , boolean bl #endif ) { }
|
||||
|
||||
@Override
|
||||
public void checkBlock(BlockPos blockPos) { }
|
||||
@@ -87,7 +87,7 @@ public class DummyLightEngine extends LevelLightEngine
|
||||
@Override
|
||||
public void retainData(ChunkPos chunkPos, boolean bl) { }
|
||||
|
||||
#if POST_MC_1_17_1
|
||||
#if MC_VER >= MC_1_17_1
|
||||
@Override
|
||||
public int getLightSectionCount() { throw new UnsupportedOperationException("This should never be used!"); }
|
||||
@Override
|
||||
|
||||
+16
-7
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -23,15 +23,24 @@ import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IStarlightAccessor;
|
||||
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
#if POST_MC_1_17_1
|
||||
import net.minecraft.world.level.chunk.LightChunkGetter;
|
||||
|
||||
#if MC_VER >= MC_1_17_1
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
#endif
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.LightChunkGetter;
|
||||
#if POST_MC_1_20_1
|
||||
|
||||
#if MC_VER >= MC_1_20_1
|
||||
import net.minecraft.world.level.chunk.LightChunk;
|
||||
#endif
|
||||
|
||||
#if MC_VER <= MC_1_20_4
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
#else
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
public class LightGetterAdaptor implements LightChunkGetter
|
||||
{
|
||||
private final BlockGetter heightGetter;
|
||||
@@ -50,7 +59,7 @@ public class LightGetterAdaptor implements LightChunkGetter
|
||||
}
|
||||
|
||||
@Override
|
||||
public #if PRE_MC_1_20_1 BlockGetter #else LightChunk #endif getChunkForLighting(int chunkX, int chunkZ)
|
||||
public #if MC_VER < MC_1_20_1 BlockGetter #else LightChunk #endif getChunkForLighting(int chunkX, int chunkZ)
|
||||
{
|
||||
if (genRegion == null)
|
||||
throw new IllegalStateException("World Gen region has not been set!");
|
||||
@@ -64,7 +73,7 @@ public class LightGetterAdaptor implements LightChunkGetter
|
||||
return shouldReturnNull ? null : (genRegion != null ? genRegion : heightGetter);
|
||||
}
|
||||
|
||||
#if POST_MC_1_17_1
|
||||
#if MC_VER >= MC_1_17_1
|
||||
public LevelHeightAccessor getLevelHeightAccessor()
|
||||
{
|
||||
return heightGetter;
|
||||
|
||||
+73
-20
@@ -1,13 +1,15 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtIo;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.chunk.storage.RegionFile;
|
||||
import net.minecraft.world.level.chunk.storage.RegionFileStorage;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
@@ -15,11 +17,27 @@ import java.nio.file.Path;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
#if MC_VER >= MC_1_20_6
|
||||
import net.minecraft.world.level.chunk.storage.RegionStorageInfo;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Shouldn't be used when the C2ME mod is present,
|
||||
* otherwise there may be potential file corruption.
|
||||
* When C2ME is present use (via MC ServerLevel) level.getChunkSource().chunkMap.worker#loadAsync()
|
||||
* instead.
|
||||
*/
|
||||
public class RegionFileStorageExternalCache implements AutoCloseable
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
/** Can be null due to the C2ME mod */
|
||||
@Nullable
|
||||
public final RegionFileStorage storage;
|
||||
public static final int MAX_CACHE_SIZE = 16;
|
||||
|
||||
public static boolean regionCacheNullPointerWarningSent = false;
|
||||
|
||||
/**
|
||||
* Present to reduce the chance that we accidentally break underlying MC code that isn't thread safe,
|
||||
* specifically: "it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap.getAndMoveToFirst()"
|
||||
@@ -28,28 +46,30 @@ public class RegionFileStorageExternalCache implements AutoCloseable
|
||||
|
||||
|
||||
|
||||
static class RegionFileCache
|
||||
{
|
||||
public final long pos;
|
||||
public final RegionFile file;
|
||||
|
||||
public RegionFileCache(long pos, RegionFile file)
|
||||
{
|
||||
this.pos = pos;
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private final ConcurrentLinkedQueue<RegionFileCache> regionFileCache = new ConcurrentLinkedQueue<>();
|
||||
|
||||
|
||||
public ConcurrentLinkedQueue<RegionFileCache> regionFileCache = new ConcurrentLinkedQueue<>();
|
||||
|
||||
public RegionFileStorageExternalCache(RegionFileStorage storage) { this.storage = storage; }
|
||||
|
||||
@Nullable
|
||||
public RegionFile getRegionFile(ChunkPos pos) throws IOException
|
||||
{
|
||||
if (this.storage == null)
|
||||
{
|
||||
if (!regionCacheNullPointerWarningSent)
|
||||
{
|
||||
regionCacheNullPointerWarningSent = true;
|
||||
LOGGER.warn("Unable to access Minecraft's chunk cache. This may be due to another mod changing said cache. DH will be unable to access any Minecraft chunk data until said mod is removed.");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
long posLong = ChunkPos.asLong(pos.getRegionX(), pos.getRegionZ());
|
||||
RegionFile rFile = null;
|
||||
|
||||
@@ -64,7 +84,7 @@ public class RegionFileStorageExternalCache implements AutoCloseable
|
||||
{
|
||||
this.getRegionFileLock.lock();
|
||||
|
||||
#if MC_1_16_5 || MC_1_17_1
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
rFile = this.storage.getRegionFile(pos);
|
||||
|
||||
// keeping the region cache size low helps prevent concurrency issues
|
||||
@@ -84,7 +104,7 @@ public class RegionFileStorageExternalCache implements AutoCloseable
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException e)
|
||||
{
|
||||
#if MC_1_16_5 || MC_1_17_1
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
// the file just wasn't cached
|
||||
break;
|
||||
#else
|
||||
@@ -98,6 +118,19 @@ public class RegionFileStorageExternalCache implements AutoCloseable
|
||||
}
|
||||
#endif
|
||||
}
|
||||
catch (NullPointerException e)
|
||||
{
|
||||
// Can sometimes happen when other mods modify the region cache system (IE C2ME)
|
||||
// instead of blowing up, just use DH's cache instead
|
||||
|
||||
if (!regionCacheNullPointerWarningSent)
|
||||
{
|
||||
regionCacheNullPointerWarningSent = true;
|
||||
LOGGER.warn("Unable to access Minecraft's chunk cache. This may be due to another mod changing said cache. Falling back to DH's internal cache.");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.getRegionFileLock.unlock();
|
||||
@@ -106,7 +139,7 @@ public class RegionFileStorageExternalCache implements AutoCloseable
|
||||
|
||||
if (retryCount >= maxRetryCount)
|
||||
{
|
||||
BatchGenerationEnvironment.LOAD_LOGGER.warn("Concurrency issue detected when getting region file for chunk at " + pos + ".");
|
||||
BatchGenerationEnvironment.LOAD_LOGGER.warn("Concurrency issue detected when getting region file for chunk at [" + pos + "].");
|
||||
}
|
||||
|
||||
|
||||
@@ -126,7 +159,7 @@ public class RegionFileStorageExternalCache implements AutoCloseable
|
||||
|
||||
// Otherwise, check if file exist, and if so, add it to the cache
|
||||
Path storageFolderPath;
|
||||
#if MC_1_16_5 || MC_1_17_1
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
storageFolderPath = this.storage.folder.toPath();
|
||||
#else
|
||||
storageFolderPath = this.storage.folder;
|
||||
@@ -138,10 +171,12 @@ public class RegionFileStorageExternalCache implements AutoCloseable
|
||||
}
|
||||
|
||||
Path regionFilePath = storageFolderPath.resolve("r." + pos.getRegionX() + "." + pos.getRegionZ() + ".mca");
|
||||
#if MC_1_16_5 || MC_1_17_1
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
rFile = new RegionFile(regionFilePath.toFile(), storageFolderPath.toFile(), false);
|
||||
#else
|
||||
#elif MC_VER <= MC_1_20_4
|
||||
rFile = new RegionFile(regionFilePath, storageFolderPath, false);
|
||||
#else
|
||||
rFile = new RegionFile(new RegionStorageInfo("level", null, "level type"), regionFilePath, storageFolderPath, false);
|
||||
#endif
|
||||
|
||||
this.regionFileCache.add(new RegionFileCache(ChunkPos.asLong(pos.getRegionX(), pos.getRegionZ()), rFile));
|
||||
@@ -191,4 +226,22 @@ public class RegionFileStorageExternalCache implements AutoCloseable
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper classes //
|
||||
//================//
|
||||
|
||||
private static class RegionFileCache
|
||||
{
|
||||
public final long pos;
|
||||
public final RegionFile file;
|
||||
|
||||
public RegionFileCache(long pos, RegionFile file)
|
||||
{
|
||||
this.pos = pos;
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+22
-17
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -35,51 +35,56 @@ import net.minecraft.server.level.WorldGenRegion;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.levelgen.WorldGenSettings;
|
||||
#if PRE_MC_1_19_2
|
||||
#if MC_VER < MC_1_19_2
|
||||
import net.minecraft.world.level.levelgen.feature.ConfiguredStructureFeature;
|
||||
import net.minecraft.world.level.StructureFeatureManager;
|
||||
#else
|
||||
#if POST_MC_1_19_4
|
||||
#if MC_VER >= MC_1_19_4
|
||||
import net.minecraft.world.level.levelgen.WorldOptions;
|
||||
#endif
|
||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
||||
import net.minecraft.world.level.StructureManager;
|
||||
#endif
|
||||
#if POST_MC_1_18_2
|
||||
#if MC_VER >= MC_1_18_2
|
||||
import net.minecraft.world.level.levelgen.structure.StructureCheck;
|
||||
#endif
|
||||
|
||||
import net.minecraft.world.level.levelgen.structure.StructureStart;
|
||||
|
||||
#if PRE_MC_1_18_2
|
||||
#if MC_VER < MC_1_18_2
|
||||
import net.minecraft.world.level.levelgen.feature.StructureFeature;
|
||||
#endif
|
||||
|
||||
#if MC_VER <= MC_1_20_4
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
#else
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
public class WorldGenStructFeatManager extends #if PRE_MC_1_19_2 StructureFeatureManager #else StructureManager #endif
|
||||
public class WorldGenStructFeatManager extends #if MC_VER < MC_1_19_2 StructureFeatureManager #else StructureManager #endif
|
||||
{
|
||||
final WorldGenLevel genLevel;
|
||||
|
||||
#if PRE_MC_1_19_4
|
||||
#if MC_VER < MC_1_19_4
|
||||
WorldGenSettings worldGenSettings;
|
||||
#else
|
||||
WorldOptions worldOptions;
|
||||
#endif
|
||||
|
||||
#if POST_MC_1_18_2
|
||||
#if MC_VER >= MC_1_18_2
|
||||
StructureCheck structureCheck;
|
||||
#endif
|
||||
|
||||
#if PRE_MC_1_19_4
|
||||
#if MC_VER < MC_1_19_4
|
||||
public WorldGenStructFeatManager(
|
||||
WorldGenSettings worldGenSettings,
|
||||
WorldGenLevel genLevel #if POST_MC_1_18_2 , StructureCheck structureCheck #endif )
|
||||
WorldGenLevel genLevel #if MC_VER >= MC_1_18_2 , StructureCheck structureCheck #endif )
|
||||
{
|
||||
|
||||
super(genLevel, worldGenSettings #if POST_MC_1_18_2 , structureCheck #endif );
|
||||
super(genLevel, worldGenSettings #if MC_VER >= MC_1_18_2 , structureCheck #endif );
|
||||
this.genLevel = genLevel;
|
||||
this.worldGenSettings = worldGenSettings;
|
||||
}
|
||||
@@ -100,8 +105,8 @@ public class WorldGenStructFeatManager extends #if PRE_MC_1_19_2 StructureFeatur
|
||||
{
|
||||
if (worldGenRegion == genLevel)
|
||||
return this;
|
||||
#if PRE_MC_1_19_4
|
||||
return new WorldGenStructFeatManager(worldGenSettings, worldGenRegion #if POST_MC_1_18_2 , structureCheck #endif );
|
||||
#if MC_VER < MC_1_19_4
|
||||
return new WorldGenStructFeatManager(worldGenSettings, worldGenRegion #if MC_VER >= MC_1_18_2 , structureCheck #endif );
|
||||
#else
|
||||
return new WorldGenStructFeatManager(worldOptions, worldGenRegion, structureCheck);
|
||||
#endif
|
||||
@@ -113,7 +118,7 @@ public class WorldGenStructFeatManager extends #if PRE_MC_1_19_2 StructureFeatur
|
||||
return genLevel.getChunk(x, z, status, false);
|
||||
}
|
||||
|
||||
#if PRE_MC_1_18_2
|
||||
#if MC_VER < MC_1_18_2
|
||||
@Override
|
||||
public Stream<? extends StructureStart<?>> startsForFeature(
|
||||
SectionPos sectionPos2,
|
||||
@@ -140,7 +145,7 @@ public class WorldGenStructFeatManager extends #if PRE_MC_1_19_2 StructureFeatur
|
||||
return chunk.hasAnyStructureReferences();
|
||||
}
|
||||
|
||||
#if MC_1_18_1
|
||||
#if MC_VER == MC_1_18_1
|
||||
@Override
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public List<? extends StructureStart<?>> startsForFeature(SectionPos sectionPos,
|
||||
@@ -165,7 +170,7 @@ public class WorldGenStructFeatManager extends #if PRE_MC_1_19_2 StructureFeatur
|
||||
return builder.build();
|
||||
}
|
||||
#else
|
||||
#if PRE_MC_1_19_2
|
||||
#if MC_VER < MC_1_19_2
|
||||
@Override
|
||||
public List<StructureStart> startsForFeature(SectionPos sectionPos, Predicate<ConfiguredStructureFeature<?, ?>> predicate)
|
||||
{
|
||||
|
||||
+56
-20
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -27,15 +27,19 @@ import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGeneratio
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters;
|
||||
|
||||
import net.minecraft.server.level.WorldGenRegion;
|
||||
#if PRE_MC_1_19_2
|
||||
#endif
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
#if POST_MC_1_18_2
|
||||
|
||||
#if MC_VER >= MC_1_18_2
|
||||
import net.minecraft.world.level.levelgen.blending.Blender;
|
||||
#endif
|
||||
|
||||
#if MC_VER <= MC_1_20_4
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
#else
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
#endif
|
||||
|
||||
public final class StepBiomes
|
||||
{
|
||||
public static final ChunkStatus STATUS = ChunkStatus.BIOMES;
|
||||
@@ -52,30 +56,62 @@ public final class StepBiomes
|
||||
List<ChunkWrapper> chunkWrappers)
|
||||
{
|
||||
|
||||
ArrayList<ChunkAccess> chunksToDo = new ArrayList<ChunkAccess>();
|
||||
ArrayList<ChunkAccess> chunksToDo = new ArrayList<>();
|
||||
|
||||
for (ChunkWrapper chunkWrapper : chunkWrappers)
|
||||
{
|
||||
ChunkAccess chunk = chunkWrapper.getChunk();
|
||||
if (chunk.getStatus().isOrAfter(STATUS)) continue;
|
||||
((ProtoChunk) chunk).setStatus(STATUS);
|
||||
chunksToDo.add(chunk);
|
||||
if (chunkWrapper.getStatus().isOrAfter(STATUS))
|
||||
{
|
||||
// this chunk has already generated this step
|
||||
continue;
|
||||
}
|
||||
else if (chunk instanceof ProtoChunk)
|
||||
{
|
||||
chunkWrapper.trySetStatus(STATUS);
|
||||
chunksToDo.add(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
for (ChunkAccess chunk : chunksToDo)
|
||||
{
|
||||
// System.out.println("StepBiomes: "+chunk.getPos());
|
||||
#if PRE_MC_1_18_2
|
||||
environment.params.generator.createBiomes(environment.params.biomes, chunk);
|
||||
#elif PRE_MC_1_19_2
|
||||
chunk = environment.joinSync(environment.params.generator.createBiomes(environment.params.biomes, Runnable::run, Blender.of(worldGenRegion),
|
||||
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
|
||||
#elif PRE_MC_1_19_4
|
||||
chunk = environment.joinSync(environment.params.generator.createBiomes(environment.params.biomes, Runnable::run, environment.params.randomState, Blender.of(worldGenRegion),
|
||||
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
|
||||
#if MC_VER < MC_1_18_2
|
||||
this.environment.params.generator.createBiomes(this.environment.params.biomes, chunk);
|
||||
#elif MC_VER < MC_1_19_2
|
||||
chunk = this.environment.confirmFutureWasRunSynchronously(
|
||||
this.environment.params.generator.createBiomes(
|
||||
this.environment.params.biomes,
|
||||
Runnable::run,
|
||||
Blender.of(worldGenRegion),
|
||||
tParams.structFeat.forWorldGenRegion(worldGenRegion),
|
||||
chunk)
|
||||
);
|
||||
#elif MC_VER < MC_1_19_4
|
||||
chunk = this.environment.confirmFutureWasRunSynchronously(
|
||||
this.environment.params.generator.createBiomes(
|
||||
this.environment.params.biomes,
|
||||
Runnable::run,
|
||||
this.environment.params.randomState, Blender.of(worldGenRegion),
|
||||
tParams.structFeat.forWorldGenRegion(worldGenRegion),
|
||||
chunk)
|
||||
);
|
||||
#elif MC_VER < MC_1_21_1
|
||||
chunk = this.environment.confirmFutureWasRunSynchronously(
|
||||
this.environment.params.generator.createBiomes(
|
||||
Runnable::run,
|
||||
this.environment.params.randomState,
|
||||
Blender.of(worldGenRegion),
|
||||
tParams.structFeat.forWorldGenRegion(worldGenRegion),
|
||||
chunk)
|
||||
);
|
||||
#else
|
||||
chunk = environment.joinSync(environment.params.generator.createBiomes(Runnable::run, environment.params.randomState, Blender.of(worldGenRegion),
|
||||
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
|
||||
chunk = this.environment.confirmFutureWasRunSynchronously(
|
||||
this.environment.params.generator.createBiomes(
|
||||
this.environment.params.randomState,
|
||||
Blender.of(worldGenRegion),
|
||||
tParams.structFeat.forWorldGenRegion(worldGenRegion),
|
||||
chunk)
|
||||
);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
+33
-21
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -19,24 +19,29 @@
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration.step;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
|
||||
|
||||
import net.minecraft.ReportedException;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
#if POST_MC_1_18_2
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
#if MC_VER <= MC_1_20_4
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
#else
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
#endif
|
||||
|
||||
|
||||
public final class StepFeatures
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
public static final ChunkStatus STATUS = ChunkStatus.FEATURES;
|
||||
|
||||
private final BatchGenerationEnvironment environment;
|
||||
@@ -51,36 +56,43 @@ public final class StepFeatures
|
||||
ThreadedParameters tParams, DhLitWorldGenRegion worldGenRegion,
|
||||
ArrayGridList<ChunkWrapper> chunkWrappers)
|
||||
{
|
||||
ArrayList<ChunkAccess> chunksToDo = new ArrayList<ChunkAccess>();
|
||||
|
||||
for (ChunkWrapper chunkWrapper : chunkWrappers)
|
||||
{
|
||||
ChunkAccess chunk = chunkWrapper.getChunk();
|
||||
if (chunk.getStatus().isOrAfter(STATUS)) continue;
|
||||
((ProtoChunk) chunk).setStatus(STATUS);
|
||||
chunksToDo.add(chunk);
|
||||
}
|
||||
if (chunkWrapper.getStatus().isOrAfter(STATUS))
|
||||
{
|
||||
// this chunk has already generated this step
|
||||
continue;
|
||||
}
|
||||
else if (chunk instanceof ProtoChunk)
|
||||
{
|
||||
chunkWrapper.trySetStatus(STATUS);
|
||||
}
|
||||
|
||||
|
||||
for (ChunkAccess chunk : chunksToDo)
|
||||
{
|
||||
try
|
||||
{
|
||||
#if PRE_MC_1_18_2
|
||||
#if MC_VER < MC_1_18_2
|
||||
worldGenRegion.setOverrideCenter(chunk.getPos());
|
||||
environment.params.generator.applyBiomeDecoration(worldGenRegion, tParams.structFeat);
|
||||
#else
|
||||
environment.params.generator.applyBiomeDecoration(worldGenRegion, chunk,
|
||||
tParams.structFeat.forWorldGenRegion(worldGenRegion));
|
||||
if (worldGenRegion.hasChunk(chunkWrapper.getChunkPos().getX(), chunkWrapper.getChunkPos().getZ()))
|
||||
{
|
||||
this.environment.params.generator.applyBiomeDecoration(worldGenRegion, chunk, tParams.structFeat.forWorldGenRegion(worldGenRegion));
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGGER.warn("Unable to generate features for chunk at pos ["+chunkWrapper.getChunkPos()+"], world gen region doesn't contain the chunk.");
|
||||
}
|
||||
#endif
|
||||
|
||||
Heightmap.primeHeightmaps(chunk, STATUS.heightmapsAfter());
|
||||
BatchGenerationEnvironment.clearDistantGenerationMixinData();
|
||||
}
|
||||
catch (ReportedException e)
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
LOGGER.warn("Unexpected issue when generating features for chunk at pos ["+chunkWrapper.getChunkPos()+"], error: ["+e.getMessage()+"].", e);
|
||||
// FIXME: Features concurrent modification issue. Something about cocobeans might just
|
||||
// error out. For now just retry.
|
||||
// error out. For now just retry.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+44
-21
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
@@ -28,17 +28,19 @@ import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParame
|
||||
|
||||
import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException;
|
||||
import net.minecraft.server.level.WorldGenRegion;
|
||||
#if POST_MC_1_17_1
|
||||
#endif
|
||||
#if PRE_MC_1_19_2
|
||||
#endif
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
#if POST_MC_1_18_2
|
||||
|
||||
#if MC_VER >= MC_1_18_2
|
||||
import net.minecraft.world.level.levelgen.blending.Blender;
|
||||
#endif
|
||||
|
||||
#if MC_VER <= MC_1_20_4
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
#else
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
#endif
|
||||
|
||||
public final class StepNoise
|
||||
{
|
||||
private static final ChunkStatus STATUS = ChunkStatus.NOISE;
|
||||
@@ -56,30 +58,51 @@ public final class StepNoise
|
||||
List<ChunkWrapper> chunkWrappers)
|
||||
{
|
||||
|
||||
ArrayList<ChunkAccess> chunksToDo = new ArrayList<ChunkAccess>();
|
||||
ArrayList<ChunkAccess> chunksToDo = new ArrayList<>();
|
||||
|
||||
for (ChunkWrapper chunkWrapper : chunkWrappers)
|
||||
{
|
||||
ChunkAccess chunk = chunkWrapper.getChunk();
|
||||
if (chunk.getStatus().isOrAfter(STATUS)) continue;
|
||||
((ProtoChunk) chunk).setStatus(STATUS);
|
||||
if (chunkWrapper.getStatus().isOrAfter(STATUS))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
chunkWrapper.trySetStatus(STATUS);
|
||||
chunksToDo.add(chunk);
|
||||
}
|
||||
|
||||
for (ChunkAccess chunk : chunksToDo)
|
||||
{
|
||||
// System.out.println("StepNoise: "+chunk.getPos());
|
||||
#if PRE_MC_1_17_1
|
||||
environment.params.generator.fillFromNoise(worldGenRegion, tParams.structFeat, chunk);
|
||||
#elif PRE_MC_1_18_2
|
||||
chunk = environment.joinSync(environment.params.generator.fillFromNoise(Runnable::run,
|
||||
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
|
||||
#elif PRE_MC_1_19_2
|
||||
chunk = environment.joinSync(environment.params.generator.fillFromNoise(Runnable::run, Blender.of(worldGenRegion),
|
||||
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
|
||||
#if MC_VER < MC_1_17_1
|
||||
this.environment.params.generator.fillFromNoise(worldGenRegion, tParams.structFeat, chunk);
|
||||
#elif MC_VER < MC_1_18_2
|
||||
chunk = this.environment.confirmFutureWasRunSynchronously(
|
||||
this.environment.params.generator.fillFromNoise(
|
||||
Runnable::run,
|
||||
tParams.structFeat.forWorldGenRegion(worldGenRegion),
|
||||
chunk));
|
||||
#elif MC_VER < MC_1_19_2
|
||||
chunk = this.environment.confirmFutureWasRunSynchronously(
|
||||
this.environment.params.generator.fillFromNoise(
|
||||
Runnable::run,
|
||||
Blender.of(worldGenRegion),
|
||||
tParams.structFeat.forWorldGenRegion(worldGenRegion),
|
||||
chunk));
|
||||
#elif MC_VER < MC_1_21_1
|
||||
chunk = this.environment.confirmFutureWasRunSynchronously(
|
||||
this.environment.params.generator.fillFromNoise(
|
||||
Runnable::run,
|
||||
Blender.of(worldGenRegion),
|
||||
this.environment.params.randomState,
|
||||
tParams.structFeat.forWorldGenRegion(worldGenRegion),
|
||||
chunk));
|
||||
#else
|
||||
chunk = environment.joinSync(environment.params.generator.fillFromNoise(Runnable::run, Blender.of(worldGenRegion), environment.params.randomState,
|
||||
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
|
||||
chunk = this.environment.confirmFutureWasRunSynchronously(
|
||||
this.environment.params.generator.fillFromNoise(
|
||||
Blender.of(worldGenRegion),
|
||||
this.environment.params.randomState,
|
||||
tParams.structFeat.forWorldGenRegion(worldGenRegion),
|
||||
chunk));
|
||||
#endif
|
||||
UncheckedInterruptedException.throwIfInterrupted();
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user