Compare commits

..

242 Commits

Author SHA1 Message Date
dfsek 5354cdebf6 still doesnt work but it works more™️ 2021-05-09 14:31:51 -07:00
dfsek 66758859bb create TerraOptionsScreen 2021-05-09 01:24:04 -07:00
dfsek 1186fc6624 bump version 2021-05-05 15:24:55 -07:00
dfsek a1b3680643 initialize later 2021-05-05 15:22:02 -07:00
dfsek fd48f5f110 Merge pull request #158 from PolyhedralDev/dev/fabric-locate
Override structure location on Fabric
2021-05-04 16:45:25 -07:00
dfsek 77a4c95c4a override structure location on Fabric 2021-05-04 16:44:43 -07:00
dfsek dbc60b1d82 Merge pull request #153 from PolyhedralDev/dev/fabric-mixins
Implement Terra interfaces directly in Minecraft classes using Mixin.
2021-05-04 16:16:31 -07:00
dfsek ed942bb997 update README.md 2021-05-04 16:13:05 -07:00
dfsek 6866084872 SignBlockEntityMixin cleanup 2021-05-04 16:10:37 -07:00
dfsek 4c77419dcd fix sign getText on server 2021-05-04 15:04:53 -07:00
dfsek ecba6e0843 Merge remote-tracking branch 'origin/dev/fabric-mixins' into dev/fabric-mixins 2021-05-04 09:21:21 -07:00
dfsek 86dcb476f1 update README with modern build instructions and Forge download links 2021-05-04 09:21:12 -07:00
dfsek 13e0857882 merge FabricEnumAdapter into FabricAdapter 2021-05-03 22:49:21 -07:00
dfsek bf93a9239c bump version 2021-05-03 22:28:01 -07:00
dfsek 2d18aab709 fix funky yaml formatting 2021-05-03 22:13:42 -07:00
dfsek a1359da374 terrascript trig functions 2021-05-03 22:13:32 -07:00
dfsek f7bda835f9 fix itemmeta application 2021-05-03 20:52:03 -07:00
dfsek 7595896831 fix refmap issues 2021-05-03 20:40:56 -07:00
dfsek 6614d19845 suppress warnings 2021-05-03 20:14:06 -07:00
dfsek 6209b86560 mixin maintenance 2021-05-03 20:12:48 -07:00
dfsek a30859a3d4 dont try to remap Terra interfaces 2021-05-03 20:08:13 -07:00
dfsek ddbb46289b add package-info.java to implementation mixin package. 2021-05-03 20:03:25 -07:00
dfsek 64c35a9609 refactor Fabric project 2021-05-03 20:02:08 -07:00
dfsek f21069ab2e fix cache misses 2021-05-03 19:43:52 -07:00
dfsek 457729b832 replace most access wideners with mixins 2021-05-03 19:33:57 -07:00
dfsek 756f04a0b3 implement LockableContainerBlockEntityMixin 2021-05-03 18:38:40 -07:00
dfsek 5ee32cc3ba add ConfiguredFeatureMixin 2021-05-03 17:35:43 -07:00
dfsek 955558bc21 implement BlockMixin 2021-05-03 11:37:54 -07:00
dfsek c43a872c23 finish blockstate mixins 2021-05-03 11:27:36 -07:00
dfsek de41b92d5d add SignBlockEntityMixin 2021-05-03 10:27:40 -07:00
dfsek 03091230ed refactor mixins 2021-05-03 10:09:21 -07:00
dfsek a8c88915ea override chunkregion hashcode 2021-05-02 23:39:40 -07:00
dfsek 4cd4720101 fix loot NPE 2021-05-02 23:08:54 -07:00
dfsek 3b9280b19c start work on state mixins 2021-05-02 23:00:21 -07:00
dfsek 2d27e07441 implement BiomeMixin 2021-05-02 22:49:57 -07:00
dfsek 20a5762d2e refactor mixins 2021-05-02 22:48:21 -07:00
dfsek 146f71f704 finish World mixins 2021-05-02 22:45:34 -07:00
dfsek 1d4b0bc100 cleanup 2021-05-02 22:26:23 -07:00
dfsek 138ee0a448 refactor fabric handles 2021-05-02 22:22:32 -07:00
dfsek 2c8cae9d45 create ChunkGeneratorMixin 2021-05-02 22:21:50 -07:00
dfsek 061d2b6493 implement EntityTypeMixin 2021-05-02 21:48:06 -07:00
dfsek e71df936ab EnchantmentMixin 2021-05-02 21:35:39 -07:00
dfsek f4253acb78 item mixins 2021-05-02 21:27:25 -07:00
dfsek c12518fa49 delete FabricItem.java 2021-05-02 21:11:09 -07:00
dfsek 4704b2ebf7 implement ItemMixin 2021-05-02 21:10:48 -07:00
dfsek 89fdfdfb34 suppress warnings 2021-05-02 20:42:54 -07:00
dfsek 35d85f2aa3 PlayerEntityMixin and EntityMixin 2021-05-02 20:38:25 -07:00
dfsek c0368f1c6d implement ServerCommandSourceMixin 2021-05-02 20:17:59 -07:00
dfsek abc069046c add ProtoChunkMixin and WorldChunkMixin 2021-05-02 20:13:24 -07:00
dfsek 46d0b08068 implement ChunkRegionMixin 2021-05-02 19:49:59 -07:00
dfsek a7e3a0286e add -forge and -fabric to Modrinth version numbers. 2021-05-02 17:46:55 -07:00
dfsek 6da8924868 Merge pull request #145 from PolyhedralDev/dev/forge
Forge implementation
2021-05-02 17:38:56 -07:00
dfsek d9dd6afe4b Merge pull request #143 from solonovamax/improvement/better-gradle-performance
Improve gradle performance significantly
2021-05-02 17:37:36 -07:00
dfsek dfec26f789 fix forge modrinth task 2021-05-02 17:21:05 -07:00
dfsek d13be5e159 add Forge modrinth publish task 2021-05-02 17:16:03 -07:00
dfsek 51c5f70d64 forge jarfile nightmare "solution" 2021-05-02 17:08:59 -07:00
dfsek 05b1902c06 Merge pull request #151 from DJtheRedstoner/patch-mixin-ap
Fix mixin annotation processor issues
2021-05-02 15:47:32 -07:00
dfsek f4ae2cac68 add MixinGeneratorOptions 2021-05-02 15:46:03 -07:00
dfsek ea3995afce fix refmap name 2021-05-02 15:44:30 -07:00
dfsek c41d60c38f remove manual refmap 2021-05-02 15:43:12 -07:00
DJtheRedstoner 19edcbddd5 Fix mixin annotation processor issues
These issues were caused by CompilationConfig.configureCompilation()
overwriting JavaCompile's options.compilerArgs list which removed any
previously added arguments, including those added by fabric-loom and
mixingradle.
2021-05-02 18:17:18 -04:00
dfsek 4f65555e82 remove mixins until annotation processor gets fixed. 2021-05-02 14:37:34 -07:00
dfsek 9956cab507 dont use vanilla registries 2021-05-01 22:21:27 -07:00
dfsek fddf0c51b7 cleanup PopulationManager 2021-05-01 20:27:59 -07:00
dfsek e2a52afb67 remove methods only used for cursed Bukkit stuff from common World interface. 2021-05-01 19:07:19 -07:00
dfsek c8c3a33912 fix loot table issue 2021-05-01 18:40:31 -07:00
dfsek b178f69e47 Merge remote-tracking branch 'origin/dev/forge' into dev/forge 2021-05-01 18:20:23 -07:00
dfsek 049a56fcb0 Mixin on Forge 2021-05-01 18:20:11 -07:00
solonovamax 2d41dd8f08 Don't use all cores processors for tests
Signed-off-by: solonovamax <solonovamax@12oclockpoint.com>
2021-05-01 16:53:45 -04:00
solonovamax aa9e33af1d Remove old and deprecated compile configuration + some minor refactoring
Signed-off-by: solonovamax <solonovamax@12oclockpoint.com>
2021-05-01 16:53:45 -04:00
dfsek 02870805c7 hoist calculations in chunk generator 2021-04-30 09:35:15 -07:00
dfsek e493825ab7 bump version 2021-04-30 09:25:56 -07:00
dfsek 762b248641 update to latest Tectonic 2021-04-29 23:42:56 -07:00
dfsek f81ccee020 cleanup 2021-04-29 23:31:03 -07:00
dfsek 3561e5f30f commands on Forge 2021-04-29 23:19:49 -07:00
dfsek c67817b9d2 fix structure issues 2021-04-29 22:00:40 -07:00
dfsek 756619edb6 Forge actually loads to worlds now 2021-04-29 21:48:32 -07:00
dfsek ee1c889d54 world screen type 2021-04-29 21:11:26 -07:00
dfsek 9f3dcf07b6 Pack loading on Forge 2021-04-29 20:30:48 -07:00
dfsek 93a2f103f7 add pack.mcmeta 2021-04-29 16:41:14 -07:00
dfsek 3ea12ceeab start implementing terraplugin 2021-04-29 12:30:17 -07:00
dfsek ce8ec51ae4 forge actually loads now 2021-04-29 12:24:29 -07:00
dfsek 54bb4ef109 sort of working Forge project 2021-04-29 01:55:24 -07:00
dfsek 59b655ce5d working forge buildscript 2021-04-29 01:27:28 -07:00
solonovamax 4c1e1bb7d5 Improve gradle performance significantly
Signed-off-by: solonovamax <solonovamax@12oclockpoint.com>
2021-04-28 20:43:53 -04:00
dfsek eee54f507e Merge pull request #138 from PolyhedralDev/dev/profilerimpl
Fancy stack-based profiler
2021-04-26 21:32:42 -07:00
dfsek 6f1b1611ab fix dumb gradle issue 2021-04-26 21:26:44 -07:00
dfsek 205499220d profile more things 2021-04-26 20:59:21 -07:00
dfsek a0c5631eba bump version 2021-04-26 20:46:36 -07:00
dfsek 9323abc788 document Profiler 2021-04-26 19:04:20 -07:00
dfsek 632f898dc8 implement Profiler#reset 2021-04-26 19:01:53 -07:00
dfsek 8737b0d984 resolve merge conflict 2021-04-25 17:12:42 -07:00
dfsek bcb68853d5 @SuppressWarnings go brrr 2021-04-25 17:11:50 -07:00
dfsek 8823d6d65e fix stack size assumption at profiler start 2021-04-25 17:11:50 -07:00
dfsek 5d3a2b6e84 profile more things 2021-04-25 17:11:50 -07:00
dfsek 23fb7753ab fancy unicode symbols B) 2021-04-25 17:11:50 -07:00
dfsek f8e7e343cb fix % parent issue 2021-04-25 17:11:50 -07:00
dfsek e5f4c5dc8d implement terrascript profiling 2021-04-25 17:11:49 -07:00
dfsek 8a10867e5f implement new profiler 2021-04-25 17:11:04 -07:00
dfsek da366a75e8 add autocloseable option 2021-04-25 17:09:08 -07:00
dfsek eb4bf74cc6 implement TerraPlugin#getProfier 2021-04-25 17:09:08 -07:00
dfsek 168c0ced13 improve performance in deep operations 2021-04-25 17:09:07 -07:00
dfsek 5d4bdb431b improve data output 2021-04-25 17:09:07 -07:00
dfsek 40188c671f basic profiler implementation 2021-04-25 17:09:07 -07:00
dfsek f396e0e5eb Merge pull request #133 from PolyhedralDev/ver/5.1.4
Fix minor Fabric issues
2021-04-16 09:01:29 -07:00
dfsek 942a8c9c8b Merge pull request #131 from xieve/fix-disable-config
Fixed populator disable config (#130)
2021-04-15 10:29:20 -07:00
dfsek 414dcdae3e use vanilla delegate spawn rules 2021-04-15 10:20:14 -07:00
dfsek 1195a6676f implement getHeight on Fabric 2021-04-15 10:00:22 -07:00
dfsek 5501f53056 implement TerraFabricPlugin#getWorld(long) 2021-04-15 09:59:13 -07:00
xieve 41d6e1c648 Fixed populator disable config (#130) 2021-04-15 18:18:27 +02:00
dfsek 5ac7257517 Merge pull request #119 from PolyhedralDev/ver/5.1.3
Fix Fabric Physics, add "dead" entry checking to registries.
2021-04-11 18:56:25 -07:00
dfsek 9f4f9702a6 bump version 2021-04-11 17:54:07 -07:00
dfsek 01d169256e properly relocate dependencies 2021-04-11 17:49:39 -07:00
dfsek 7a703ad091 add publication config to Fabric 2021-04-11 14:15:13 -07:00
dfsek ce9273c7e8 proper fluid updating on Fabric 2021-04-11 00:45:53 -07:00
dfsek 653a414ac1 should(tm) fix fabric physics 2021-04-10 23:49:02 -07:00
dfsek 2080db21ca warn about dead registry entries when debug mode is enabled 2021-04-10 19:22:41 -07:00
dfsek 8a933609ee Merge pull request #107 from PolyhedralDev/ver/5.1.2
5.1.2 LootPopulateEvent patch
2021-04-01 15:14:14 -07:00
dfsek ba4a50e234 add getStructureScript method to LootPopulateEvent 2021-04-01 15:03:48 -07:00
dfsek f8e8ce8bc2 Bump version 2021-03-31 20:50:17 -07:00
dfsek 0013d4e682 Merge pull request #106 from PolyhedralDev/ver/5.1.2
Add LootPopulateEvent and EntitySpawnEvent
2021-03-31 20:49:44 -07:00
dfsek 9a97f1178d release modrinth to beta channel 2021-03-31 08:34:17 -07:00
dfsek e6a551d84d add getter/setter for loot table in LootPopulateEvent 2021-03-30 09:23:21 -07:00
dfsek 92921430d8 add EntitySpawnEvent 2021-03-30 00:27:16 -07:00
dfsek 20c905aae4 add LootPopulateEvent 2021-03-30 00:19:32 -07:00
dfsek ec0730ef73 test commit so version is different B) 2021-03-29 21:14:34 -07:00
dfsek e4576b3405 Add modrinth publish task 2021-03-29 21:09:36 -07:00
dfsek c5800970a8 i totally didnt forget to bump version 2021-03-29 16:39:10 -07:00
dfsek 8f88b1c156 Merge pull request #101 from PolyhedralDev/ver/5.1.1
API to add populators, Buildscript improvements
2021-03-29 16:26:59 -07:00
dfsek 1360994a67 Add options to disable default populators 2021-03-29 11:56:16 -07:00
dfsek e00b28d27e remove sysout 2021-03-29 10:07:25 -07:00
dfsek c5ff5c101d custom biome color configuration 2021-03-29 09:52:54 -07:00
dfsek b1a1001c49 clean up buildscripts 2021-03-26 08:50:58 -07:00
dfsek 77d5162e73 add API for addons to register populators 2021-03-23 11:40:22 -07:00
dfsek 2e8cd54ac2 Merge pull request #96 from PolyhedralDev/ver/5.1.0
Address several issues
2021-03-17 10:53:32 -07:00
dfsek 28222c074e bump version 2021-03-16 22:29:13 -07:00
dfsek 1b70766a17 remove legacy fractal trees (will be available in an addon) 2021-03-16 21:49:08 -07:00
dfsek cda2d4688c implement DynamicBlockFunction 2021-03-16 21:20:02 -07:00
dfsek 5028582198 add smart waterlog 2021-03-16 21:04:56 -07:00
dfsek 5458564cfa fix CommandTest issues 2021-03-16 11:06:58 -07:00
dfsek 7f11373f75 Merge pull request #80 from PolyhedralDev/ver/5.0.0
Addon loader, fleshed out addon API, and Fabric finalization
2021-03-16 10:45:42 -07:00
dfsek f9ca8d139b fix ExpressionFunction issue 2021-03-16 09:09:30 -07:00
dfsek 6f287a1894 use LinkedHashMap for loading functions to preserve order 2021-03-15 23:24:49 -07:00
dfsek 6f4251796e optimize ores and sampling 2021-03-15 21:15:53 -07:00
dfsek 5ad349e350 document event API 2021-03-14 23:54:10 -07:00
dfsek ec4e0694a4 add structure locate command 2021-03-14 16:45:32 -07:00
dfsek 6ab8cd5b5b fix mushrooms on fabric 2021-03-14 16:30:27 -07:00
dfsek b3868bd750 allow paralithic expressions to be defined in expression samplers 2021-03-13 15:18:25 -07:00
dfsek ff8181bbfa fix function loading issues 2021-03-13 14:48:02 -07:00
dfsek 074ad44bca add Gabor noise 2021-03-11 19:03:57 -07:00
dfsek 9a6c34a2d5 fix fabric teleportation issues 2021-03-10 16:33:55 -07:00
dfsek ce3c0f105e more specific duplicate ID message 2021-03-10 16:26:15 -07:00
dfsek 58162027a9 specify problematic addon class 2021-03-10 16:21:04 -07:00
dfsek 39400cce0a Merge pull request #89 from PolyhedralDev/dev/commands
Platform-agnostic command API
2021-03-10 16:16:40 -07:00
dfsek 819d795c23 Fabric cleanup 2021-03-10 16:16:11 -07:00
dfsek d6b5f60b18 implement Bukkit-specific commands 2021-03-10 16:08:25 -07:00
dfsek 1f16a82a8d implement GeometryCommands 2021-03-10 16:02:25 -07:00
dfsek 51fa58b481 add command main-menu messages 2021-03-10 15:52:20 -07:00
dfsek bed8c561a4 add "usage" to all commands 2021-03-10 15:33:38 -07:00
dfsek fdb2441b1a export command 2021-03-10 15:28:50 -07:00
dfsek bf5be91868 clean up WorldHandle, add generic utility classes 2021-03-10 15:20:17 -07:00
dfsek 06956a7a2d fix command NPEs on invalid IDs 2021-03-10 03:20:34 -07:00
dfsek a584ac2401 Bukkit command cleanup/fixes 2021-03-10 03:03:04 -07:00
dfsek 513c6a647f Bukkit command cleanup 2021-03-10 02:54:03 -07:00
dfsek 5a85aced45 BiomeInfoCommand 2021-03-10 02:50:40 -07:00
dfsek 41933b84a0 implement entity teleportation and biome locate 2021-03-10 02:46:00 -07:00
dfsek 67aae87754 work on fabric commands 2021-03-10 01:57:01 -07:00
Daniil Z 13346daa6a Russian translation (#88)
* Russian translation

* Move file to commons
2021-03-09 23:30:14 -07:00
dfsek 5e9b841cac trigger block re-render on Fabric 2021-03-09 23:20:47 -07:00
dfsek 5820fe1db3 fix imageloader issue 2021-03-09 20:05:52 -07:00
dfsek f5c0174473 more command stuff 2021-03-09 20:05:42 -07:00
dfsek 31b583910e fabric command fixes 2021-03-09 03:48:26 -07:00
dfsek fac4cb43a8 cleanup fabric commands 2021-03-09 03:15:43 -07:00
dfsek 0f39d64d72 sort of working Fabric commands 2021-03-09 03:07:02 -07:00
dfsek 5fc012f7ba injection for argumentparser 2021-03-08 23:56:58 -07:00
dfsek f773ca2322 ArgumentTarget and SwitchTarget 2021-03-08 23:56:06 -07:00
dfsek 52c56af02c tab completion 2021-03-08 03:17:22 -07:00
dfsek 22d6fdf293 add tab completion 2021-03-08 02:27:35 -07:00
dfsek b1256427a2 basic bukkit implementation 2021-03-08 01:40:12 -07:00
dfsek 026a6066d3 implement switches 2021-03-08 00:52:57 -07:00
dfsek 0cd5898107 fix child command issues 2021-03-08 00:38:52 -07:00
dfsek a5d101ff61 fix child command issues 2021-03-08 00:38:52 -07:00
dfsek c1b04d1772 argument parsing 2021-03-08 00:14:19 -07:00
dfsek 0d58201e3f mock command API 2021-03-07 23:44:19 -07:00
dfsek 939121dea6 Merge pull request #87 from PolyhedralDev/dev/fabric
Fabric stuff
2021-03-07 23:42:01 -07:00
dfsek c44d26cc18 loom is pain 2021-03-07 21:41:13 -07:00
dfsek c047209b86 manually create terra-refmap.json because loom is dumb 2021-03-06 22:26:13 -07:00
dfsek f989e4dc89 fix fabric mixin stuff 2021-03-06 11:37:28 -07:00
dfsek c8c3ab312b separate generator and GeneratorWrapper 2021-03-06 10:05:11 -07:00
dfsek dc52dd635b update README.md 2021-03-03 15:59:26 -07:00
dfsek 4e97ba6da9 Merge remote-tracking branch 'origin/ver/5.0.0' into ver/5.0.0 2021-03-03 13:42:22 -07:00
dfsek c0d26256af fix typo 2021-03-03 13:42:15 -07:00
dfsek 7828de74aa work on loom stuff 2021-03-02 00:30:26 -07:00
dfsek 01396e739c Merge pull request #84 from PolyhedralDev/dev/world-maps
World specific config pack instances
2021-03-01 18:41:30 -07:00
dfsek e4c1a056ae fix server issues 2021-03-01 17:20:19 -07:00
dfsek 613e9c0c54 fabric ID fix 2021-03-01 16:54:56 -07:00
dfsek c5a174ba7a remove sys.out 2021-03-01 16:49:32 -07:00
dfsek 72296cf960 cleanup 2021-03-01 16:45:27 -07:00
dfsek 265449c5a7 fix ProbabilityCollection map issues 2021-03-01 16:43:14 -07:00
dfsek 1125b498ec remove unneeded generic from BiomeBuilder 2021-03-01 15:39:10 -07:00
dfsek a28f3fa660 working WorldConfig impl 2021-03-01 15:20:09 -07:00
dfsek 5a6b7ac4c1 begin work on WorldConfig stuff
This doesn't compile right now. A lot of work needs to be done.
2021-03-01 09:58:18 -07:00
dfsek 7f988dcf26 implement FabricDirectional 2021-02-25 01:43:01 -07:00
dfsek 7c177d568b implement FabricRotatable 2021-02-25 01:33:10 -07:00
dfsek 12af19edba fabric entities 2021-02-25 01:21:28 -07:00
dfsek 924bc6e469 Fabric Enchantments 2021-02-25 00:57:36 -07:00
dfsek 4569a9ed13 working damage 2021-02-25 00:45:25 -07:00
dfsek 4ca2f0c08d fabric inventory stuff 2021-02-24 23:46:43 -07:00
dfsek 4a47815be7 remove assumption that world starts at Y=0 2021-02-24 19:49:39 -07:00
dfsek dd446b3034 add HPROF to ignored files 2021-02-24 14:12:30 -07:00
dfsek 0fcc0f798c fabric cleanup, move lang and config to common 2021-02-24 13:59:25 -07:00
dfsek 1637644bdd accesswideners B) 2021-02-24 13:14:21 -07:00
dfsek 2b114f225e bump version 2021-02-24 09:54:20 -07:00
dfsek 32746e8dd9 target 1.16.5 for Fabric 2021-02-24 09:40:56 -07:00
dfsek d29c1e572e basic Fabric pack selection 2021-02-24 09:17:44 -07:00
dfsek bb6dcb3880 fix structure buffer weirdness 2021-02-24 08:51:42 -07:00
dfsek 3c56813d6b cursed #equals 2021-02-24 02:00:47 -07:00
dfsek 5c0482e972 more fabric stuff 2021-02-24 01:33:19 -07:00
dfsek 83f981111a remove debug stuff 2021-02-23 22:36:29 -07:00
dfsek 4171768cc9 semi working fabric inventories 2021-02-23 22:33:08 -07:00
dfsek df2acfaa40 FabricMobSpawner 2021-02-23 20:44:40 -07:00
dfsek 533380107b working FabricSign 2021-02-23 17:29:39 -07:00
dfsek 9c2b844290 preparations for Sponge API8 2021-02-23 15:57:27 -07:00
dfsek 98c1fea7fd more fabric stuff 2021-02-22 22:23:57 -07:00
dfsek 415df211ed fabric cleanup 2021-02-22 21:41:15 -07:00
dfsek ac09e059fc caves on fabric actually work now 2021-02-22 21:02:59 -07:00
dfsek 268cc7c48b fabric stuff 2021-02-22 20:47:06 -07:00
dfsek 358bd350b5 MaterialData is gone 2021-02-22 19:47:15 -07:00
dfsek a328ff2f2a refactor API 2021-02-22 16:13:53 -07:00
dfsek 46a08e49f5 registry stuff 2021-02-22 10:32:38 -07:00
dfsek 05cd0b625c notnull annotations 2021-02-21 23:08:51 -07:00
dfsek 5e940187d9 documentation 2021-02-21 22:25:00 -07:00
dfsek 8b196716a4 CheckedRegistry API 2021-02-21 22:04:29 -07:00
dfsek 6025e0f557 cleanup 2021-02-21 17:49:07 -07:00
dfsek e00209c99c rework chunkgenerator API to allow easier extension 2021-02-21 14:19:42 -07:00
dfsek 03e9f6b882 more cleanup 2021-02-20 23:59:17 -07:00
dfsek fab8c90e92 clean up ChunkGenerator API 2021-02-20 23:49:47 -07:00
dfsek 3b719d0880 add dependency injection API 2021-02-20 23:23:17 -07:00
dfsek 45dbe45fb4 set up dependency stuff 2021-02-20 22:31:13 -07:00
dfsek 76f2a3fbc4 fleshed out addon loading 2021-02-19 22:49:11 -07:00
dfsek 5e761c3e29 basic addon loading 2021-02-19 20:36:35 -07:00
dfsek 6d51da3118 Merge pull request #77 from PolyhedralDev/ver/4.3.0
Noise Changes
2021-02-18 20:39:49 -07:00
577 changed files with 13351 additions and 6155 deletions
+1 -1
View File
@@ -342,6 +342,6 @@ ij_json_wrap_long_lines = false
indent_size = 2
ij_yaml_keep_indents_on_empty_lines = true
ij_yaml_keep_line_breaks = true
ij_yaml_space_before_colon = true
ij_yaml_space_before_colon = false
ij_yaml_spaces_within_braces = true
ij_yaml_spaces_within_brackets = true
+1
View File
@@ -52,6 +52,7 @@ gradle-app.setting
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
*.hprof
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
+35 -14
View File
@@ -1,21 +1,42 @@
# Terra
Terra is a data-driven world generator based on [Gaea](https://github.com/PolyhedralDev/Gaea). Find out more on our
[Spigot page](https://www.spigotmc.org/resources/85151/)!
## Building and running Terra
To build, simply run `./gradlew build` on Linux/MacOS, or `gradlew.bat build` on Windows.
This will produce a jar in `build/libs` called `Terra-[CURRENT VERSION].jar`.
You can put this right into your plugins dir, along with the correct Gaea version.
Terra is an incredibly powerful free & open-source data-driven, platform-agnostic world generator. It allows you to create a world exactly
to your specifications, with no knowledge of Java required.
If you would like to test it with a default server config, just run `./gradlew setupServer` or
`./gradlew.bat setupServer` to set up the server, then `./gradlew testWithPaper` or `gradlew.bat testWithPaper` to run
the server. If you want a clean installation of the server, re-run the `setupServer` task.
This will download a default server config from [here](https://github.com/PolyhedralDev/WorldGenTestServer)
and install the server in the `target/server` directory, along with all the needed plugins.
**Note: You will need to adjust the `NAME` variable `bukkit.yml` of the test server if you are not using the default
Terra config.**
## Downloads:
* Paper+ servers (Paper, Tuinity, Purpur, etc): [SpigotMC](https://www.spigotmc.org/resources/85151/)
* Fabric: [Modrinth](https://modrinth.com/mod/terra) / [CurseForge](https://www.curseforge.com/minecraft/mc-mods/terra-world-generator)
* Forge: [Modrinth](https://modrinth.com/mod/terra) / [CurseForge](https://www.curseforge.com/minecraft/mc-mods/terra-world-generator)
## Building and Running Terra
To build, simply run `./gradlew build` (`gradlew.bat build` on Windows). This will build all platforms, and
produce JARs in `platforms/<platform>/build/libs`
### Production JARs:
* Bukkit: `Terra-<version>-shaded.jar`
* Fabric: `Terra-<version>-shaded-mapped.jar`
* Forge: `Terra-<version>-shaded.jar`
### Building a Specific Platform
To build a specific platform, run `gradlew :platforms:<platform>:build`.
JARs are produced in `platforms/<platform>/build/libs`.
### Running Minecraft in the IDE
To run Minecraft with Terra in the IDE (for testing) use the following tasks:
* Bukkit
* `installPaper` - Install a [Paper](https://github.com/PaperMC/Paper) test server. (Only needs to be run once).
* `installPurpur` - Install a [Purpur](https://github.com/pl3xgaming/Purpur) test server. (Only needs to be run once).
* `runPaper` - Run the Paper test server with Terra (`installPaper` must have been run previously).
* `runPurpur` - Run the Purpur test server with Terra (`installPurpur` must have been run previously).
* Fabric
* `runClient` - Run a Minecraft client with Terra installed.
* `runServer` - Run a Minecraft server with Terra installed.
* Forge
* `runClient` - Run a Minecraft client with Terra installed.
* `runServer` - Run a Minecraft server with Terra installed.
## Contributing
Contributions are welcome! If you want to see a feature in Terra, please, open an issue, or implement it yourself and
submit a PR!
+19 -2
View File
@@ -1,10 +1,27 @@
import com.dfsek.terra.getGitHash
val versionObj = Version("4", "3", "0", true)
val versionObj = Version("5", "3", "1", true)
allprojects {
version = versionObj
group = "com.dfsek.terra"
tasks.withType<JavaCompile>().configureEach {
options.isFork = true
options.isIncremental = true
}
tasks.withType<Test>().configureEach {
useJUnitPlatform()
maxHeapSize = "2G"
ignoreFailures = false
failFast = true
maxParallelForks = (Runtime.getRuntime().availableProcessors() - 1).takeIf { it > 0 } ?: 1
reports.html.isEnabled = false
reports.junitXml.isEnabled = false
}
}
/**
* Version class that does version stuff.
@@ -18,4 +35,4 @@ class Version(val major: String, val minor: String, val revision: String, val pr
else //Only use git hash if it's a prerelease.
"$major.$minor.$revision-BETA+${getGitHash()}"
}
}
}
@@ -7,30 +7,15 @@ import org.gradle.kotlin.dsl.withType
import java.io.ByteArrayOutputStream
fun Project.configureCommon() {
apply(plugin = "java-library")
apply(plugin = "maven-publish")
apply(plugin = "idea")
configureDependencies()
configureCompilation()
configureDistribution()
version = rootProject.version
tasks.withType<Test>().configureEach {
useJUnitPlatform()
maxHeapSize = "2G"
ignoreFailures = false
failFast = true
maxParallelForks = 12
}
}
fun Project.getGitHash(): String {
val stdout = java.io.ByteArrayOutputStream()
val stdout = ByteArrayOutputStream()
exec {
commandLine = mutableListOf("git", "rev-parse", "--short", "HEAD")
standardOutput = stdout
@@ -3,12 +3,16 @@ package com.dfsek.terra
import org.gradle.api.JavaVersion
import org.gradle.api.Project
import org.gradle.api.plugins.JavaPluginConvention
import org.gradle.api.tasks.bundling.Jar
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.api.tasks.javadoc.Javadoc
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.withType
import org.gradle.kotlin.dsl.*
import org.gradle.language.jvm.tasks.ProcessResources
fun Project.configureCompilation() {
apply(plugin = "maven-publish")
apply(plugin = "idea")
configure<JavaPluginConvention> {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
@@ -17,11 +21,35 @@ fun Project.configureCompilation() {
tasks.withType<JavaCompile> {
options.encoding = "UTF-8"
doFirst {
options.compilerArgs = mutableListOf("-Xlint:all")
options.compilerArgs.add("-Xlint:all")
}
}
tasks.withType<ProcessResources> {
include("**/*.*")
filter<org.apache.tools.ant.filters.ReplaceTokens>(
"tokens" to mapOf(
"VERSION" to project.version.toString()
)
)
}
tasks.withType<Javadoc> {
options.encoding = "UTF-8"
}
tasks.withType<Jar> {
archiveBaseName.set("Terra-${archiveBaseName.get()}")
from("../LICENSE", "../../LICENSE")
}
tasks.register<Jar>("sourcesJar") {
archiveClassifier.set("sources")
}
tasks.register<Jar>("javadocJar") {
dependsOn("javadoc")
archiveClassifier.set("javadoc")
from(tasks.getByName<Javadoc>("javadoc").destinationDir)
}
}
@@ -1,13 +1,27 @@
package com.dfsek.terra
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.invoke
import org.gradle.kotlin.dsl.repositories
fun Project.configureDependencies() {
apply(plugin = "java")
apply(plugin = "java-library")
configurations {
val shaded = create("shaded")
val shadedApi = create("shadedApi")
shaded.extendsFrom(shadedApi)
getByName("api").extendsFrom(shadedApi)
val shadedImplementation = create("shadedImplementation")
shaded.extendsFrom(shadedImplementation)
getByName("implementation").extendsFrom(shadedImplementation)
}
repositories {
maven { url = uri("http://maven.enginehub.org/repo/") }
maven { url = uri("https://maven.enginehub.org/repo/") }
maven { url = uri("https://repo.codemc.org/repository/maven-public") }
maven { url = uri("https://papermc.io/repo/repository/maven-public/") }
maven { url = uri("https://maven.fabricmc.net/") }
@@ -18,9 +32,7 @@ fun Project.configureDependencies() {
dependencies {
"testImplementation"("org.junit.jupiter:junit-jupiter-api:5.7.0")
"testImplementation"("org.yaml:snakeyaml:1.27")
"testImplementation"("com.googlecode.json-simple:json-simple:1.1.1")
"testRuntimeOnly"("org.junit.jupiter:junit-jupiter-engine:5.7.0")
"compileOnly"("org.jetbrains:annotations:20.1.0")
"testImplementation"("org.junit.jupiter:junit-jupiter-engine:5.7.0")
"api"("org.jetbrains:annotations:20.1.0")
}
}
@@ -14,26 +14,8 @@ fun Project.configureDistribution() {
apply(plugin = "java-library")
apply(plugin = "com.github.johnrengelman.shadow")
// configurations.create("shaded")
configurations {
val shaded = create("shaded")
getByName("compile").extendsFrom(shaded)
// shaded.extendsFrom(getByName("compile"))
val shadedApi = create("shadedApi")
shaded.extendsFrom(shadedApi)
getByName("api").extendsFrom(shadedApi)
val shadedImplementation = create("shadedImplementation")
shaded.extendsFrom(shadedImplementation)
getByName("implementation").extendsFrom(shadedImplementation)
}
// tasks.withType<JavaCompile> {
// classpath +=
// }
val downloadDefaultPacks = tasks.create("downloadDefaultPacks") {
group = "terra"
doFirst {
file("${buildDir}/resources/main/packs/").deleteRecursively()
@@ -45,21 +27,6 @@ fun Project.configureDistribution() {
}
tasks["processResources"].dependsOn(downloadDefaultPacks)
tasks.withType<Jar> {
archiveBaseName.set("Terra-${archiveBaseName.get()}")
from("../LICENSE", "../../LICENSE")
}
tasks.register<Jar>("sourcesJar") {
archiveClassifier.set("sources")
}
tasks.register<Jar>("javadocJar") {
dependsOn("javadoc")
archiveClassifier.set("javadoc")
from(tasks.getByName<Javadoc>("javadoc").destinationDir)
}
tasks.named<ShadowJar>("shadowJar") {
// Tell shadow to download the packs
dependsOn(downloadDefaultPacks)
@@ -69,8 +36,14 @@ fun Project.configureDistribution() {
archiveClassifier.set("shaded")
setVersion(project.version)
relocate("org.apache.commons", "com.dfsek.terra.lib.commons")
relocate("parsii", "com.dfsek.terra.lib.parsii")
relocate("net.jafama", "com.dfsek.terra.lib.jafama")
relocate("org.objectweb.asm", "com.dfsek.terra.lib.asm")
relocate("com.google.errorprone", "com.dfsek.terra.lib.google.errorprone")
relocate("com.google.j2objc", "com.dfsek.terra.lib.google.j2objc")
relocate("org.checkerframework", "com.dfsek.terra.lib.checkerframework")
relocate("org.javax.annotation", "com.dfsek.terra.lib.javax.annotation")
relocate("org.json", "com.dfsek.terra.lib.json")
relocate("org.yaml", "com.dfsek.terra.lib.yaml")
minimize()
}
convention.getPlugin<BasePluginConvention>().archivesBaseName = project.name
+12 -5
View File
@@ -1,11 +1,14 @@
import com.dfsek.terra.configureCommon
import com.dfsek.terra.configureCompilation
import com.dfsek.terra.configureDependencies
plugins {
`java-library`
`maven-publish`
idea
}
configureCommon()
configureCompilation()
configureDependencies()
group = "com.dfsek.terra.common"
@@ -14,14 +17,18 @@ dependencies {
"shadedApi"("commons-io:commons-io:2.4")
"shadedApi"("com.dfsek:Paralithic:0.3.2")
"shadedApi"("com.dfsek:Tectonic:1.2.3")
"shadedApi"("com.dfsek:Tectonic:1.3.1")
"shadedApi"("net.jafama:jafama:2.3.2")
"shadedApi"("org.yaml:snakeyaml:1.27")
"shadedApi"("org.ow2.asm:asm:9.0")
"shadedApi"("commons-io:commons-io:2.6")
"compileOnly"("com.googlecode.json-simple:json-simple:1.1")
"shadedApi"("com.googlecode.json-simple:json-simple:1.1.1")
"shadedApi"("org.yaml:snakeyaml:1.27")
"shadedApi"("com.google.guava:guava:30.0-jre")
"compileOnly"("com.google.guava:guava:30.0-jre")
"testImplementation"("com.google.guava:guava:30.0-jre")
}
publishing {
@@ -0,0 +1,61 @@
package com.dfsek.terra.addon;
import com.dfsek.terra.api.addons.TerraAddon;
import com.dfsek.terra.api.addons.annotations.Addon;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class AddonClassLoader extends URLClassLoader {
static {
ClassLoader.registerAsParallelCapable();
}
public AddonClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
public AddonClassLoader(URL[] urls) {
super(urls);
}
@SuppressWarnings("unchecked")
public static Set<Class<? extends TerraAddon>> fetchAddonClasses(File file) throws IOException {
JarFile jarFile = new JarFile(file);
Enumeration<JarEntry> entries = jarFile.entries();
AddonClassLoader loader = new AddonClassLoader(new URL[] {file.toURI().toURL()}, AddonClassLoader.class.getClassLoader());
Set<Class<? extends TerraAddon>> set = new HashSet<>();
while(entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if(entry.isDirectory() || !entry.getName().endsWith(".class")) continue;
String className = entry.getName().substring(0, entry.getName().length() - 6).replace('/', '.');
try {
Class<?> clazz = loader.loadClass(className);
Addon addon = clazz.getAnnotation(Addon.class);
if(addon == null) continue;
if(!TerraAddon.class.isAssignableFrom(clazz))
throw new IllegalArgumentException("Addon class \"" + clazz + "\" must extend TerraAddon.");
set.add((Class<? extends TerraAddon>) clazz);
} catch(ClassNotFoundException e) {
throw new IllegalStateException(e); // this should literally never happen, if it does something is very wrong
}
}
return set;
}
}
@@ -0,0 +1,42 @@
package com.dfsek.terra.addon;
import com.dfsek.terra.addon.exception.AddonLoadException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class AddonPool {
private final Map<String, PreLoadAddon> pool = new HashMap<>();
public void add(PreLoadAddon addon) throws AddonLoadException {
if(pool.containsKey(addon.getId())) {
String message = "Duplicate addon ID: " +
addon.getId() + "; original ID from file: " +
pool.get(addon.getId()).getFile().getAbsolutePath() +
", class: " +
pool.get(addon.getId()).getAddonClass().getCanonicalName() +
"Duplicate ID from file: " +
addon.getFile().getAbsolutePath() +
", class: " +
addon.getAddonClass().getCanonicalName();
throw new AddonLoadException(message);
}
pool.put(addon.getId(), addon);
}
public PreLoadAddon get(String id) {
return pool.get(id);
}
public void buildAll() throws AddonLoadException {
for(PreLoadAddon value : pool.values()) {
value.rebuildDependencies(this, value, true);
}
}
public Set<PreLoadAddon> getAddons() {
return new HashSet<>(pool.values());
}
}
@@ -0,0 +1,58 @@
package com.dfsek.terra.addon;
import com.dfsek.terra.addon.exception.AddonLoadException;
import com.dfsek.terra.addon.exception.CircularDependencyException;
import com.dfsek.terra.addon.exception.DependencyMissingException;
import com.dfsek.terra.api.addons.TerraAddon;
import com.dfsek.terra.api.addons.annotations.Addon;
import com.dfsek.terra.api.addons.annotations.Depends;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class PreLoadAddon {
private final List<PreLoadAddon> depends = new ArrayList<>();
private final Class<? extends TerraAddon> addonClass;
private final String id;
private final String[] dependencies;
private final File file;
public PreLoadAddon(Class<? extends TerraAddon> addonClass, File file) {
this.addonClass = addonClass;
this.id = addonClass.getAnnotation(Addon.class).value();
this.file = file;
Depends depends = addonClass.getAnnotation(Depends.class);
this.dependencies = depends == null ? new String[] {} : depends.value();
}
public List<PreLoadAddon> getDepends() {
return depends;
}
public void rebuildDependencies(AddonPool pool, PreLoadAddon origin, boolean levelG1) throws AddonLoadException {
if(this.equals(origin) && !levelG1)
throw new CircularDependencyException("Detected circular dependency in addon \"" + id + "\", dependencies: " + Arrays.toString(dependencies));
for(String dependency : dependencies) {
PreLoadAddon preLoadAddon = pool.get(dependency);
if(preLoadAddon == null)
throw new DependencyMissingException("Dependency " + dependency + " was not found. Please install " + dependency + " to use " + id + ".");
depends.add(preLoadAddon);
preLoadAddon.rebuildDependencies(pool, origin, false);
}
}
public String getId() {
return id;
}
public Class<? extends TerraAddon> getAddonClass() {
return addonClass;
}
public File getFile() {
return file;
}
}
@@ -0,0 +1,13 @@
package com.dfsek.terra.addon.exception;
public class AddonLoadException extends Exception {
private static final long serialVersionUID = -4949084729296580176L;
public AddonLoadException(String message) {
super(message);
}
public AddonLoadException(String message, Throwable cause) {
super(message, cause);
}
}
@@ -0,0 +1,13 @@
package com.dfsek.terra.addon.exception;
public class CircularDependencyException extends AddonLoadException {
private static final long serialVersionUID = 7398510879124125121L;
public CircularDependencyException(String message) {
super(message);
}
public CircularDependencyException(String message, Throwable cause) {
super(message, cause);
}
}
@@ -0,0 +1,13 @@
package com.dfsek.terra.addon.exception;
public class DependencyMissingException extends AddonLoadException {
private static final long serialVersionUID = -8419489102208521583L;
public DependencyMissingException(String message) {
super(message);
}
public DependencyMissingException(String message, Throwable cause) {
super(message, cause);
}
}
@@ -0,0 +1,78 @@
package com.dfsek.terra.api;
import com.dfsek.terra.api.addons.TerraAddon;
import com.dfsek.terra.api.event.EventManager;
import com.dfsek.terra.api.platform.handle.ItemHandle;
import com.dfsek.terra.api.platform.handle.WorldHandle;
import com.dfsek.terra.api.platform.world.World;
import com.dfsek.terra.api.registry.CheckedRegistry;
import com.dfsek.terra.api.registry.LockedRegistry;
import com.dfsek.terra.api.util.JarUtil;
import com.dfsek.terra.api.util.logging.DebugLogger;
import com.dfsek.terra.api.util.logging.Logger;
import com.dfsek.terra.config.PluginConfig;
import com.dfsek.terra.config.lang.Language;
import com.dfsek.terra.config.pack.ConfigPack;
import com.dfsek.terra.profiler.Profiler;
import com.dfsek.terra.world.TerraWorld;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.jar.JarFile;
/**
* Represents a Terra mod/plugin instance.
*/
public interface TerraPlugin extends LoaderRegistrar {
WorldHandle getWorldHandle();
TerraWorld getWorld(World world);
Logger logger();
PluginConfig getTerraConfig();
File getDataFolder();
boolean isDebug();
Language getLanguage();
CheckedRegistry<ConfigPack> getConfigRegistry();
LockedRegistry<TerraAddon> getAddons();
boolean reload();
ItemHandle getItemHandle();
void saveDefaultConfig();
String platformName();
DebugLogger getDebugLogger();
EventManager getEventManager();
default String getVersion() {
return "@VERSION@";
}
/**
* Runs a task that may or may not be thread safe, depending on platform.
* <p>
* Allows platforms to define what code is safe to be run asynchronously.
*
* @param task Task to be run.
*/
default void runPossiblyUnsafeTask(Runnable task) {
task.run();
}
Profiler getProfiler();
default JarFile getModJar() throws URISyntaxException, IOException {
return JarUtil.getJarFile();
}
}
@@ -0,0 +1,49 @@
package com.dfsek.terra.api.addons;
import com.dfsek.terra.api.addons.annotations.Addon;
import com.dfsek.terra.api.addons.annotations.Author;
import com.dfsek.terra.api.addons.annotations.Version;
import org.jetbrains.annotations.NotNull;
/**
* Represents an entry point for an addon. Implementations must be annotated with {@link Addon}.
*/
public abstract class TerraAddon {
/**
* Gets the version of this addon.
*
* @return Addon version.
*/
public final @NotNull String getVersion() {
Version version = getClass().getAnnotation(Version.class);
return version == null ? "0.1.0" : version.value();
}
/**
* Gets the author of this addon.
*
* @return Addon author.
*/
public final @NotNull String getAuthor() {
Author author = getClass().getAnnotation(Author.class);
return author == null ? "Anon Y. Mous" : author.value();
}
/**
* Gets the name (ID) of this addon.
*
* @return Addon ID.
*/
public final @NotNull String getName() {
Addon addon = getClass().getAnnotation(Addon.class);
if(addon == null)
throw new IllegalStateException("Addon annotation not present"); // This should never happen; the presence of this annotation is checked by the addon loader.
return addon.value();
}
/**
* Invoked immediately after an addon is loaded.
*/
public abstract void initialize();
}
@@ -0,0 +1,20 @@
package com.dfsek.terra.api.addons.annotations;
import org.jetbrains.annotations.NotNull;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Specifies that the annotated class is an entry point for a Terra addon.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Addon {
/**
* @return The ID of the addon.
*/
@NotNull String value();
}
@@ -0,0 +1,20 @@
package com.dfsek.terra.api.addons.annotations;
import org.jetbrains.annotations.NotNull;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Optional annotation that specifies the author of an addon.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Author {
/**
* @return Name of the addon author.
*/
@NotNull String value();
}
@@ -0,0 +1,20 @@
package com.dfsek.terra.api.addons.annotations;
import org.jetbrains.annotations.NotNull;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Optional annotation that specifies dependencies of an addon.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Depends {
/**
* @return All addons this addon is dependent upon.
*/
@NotNull String[] value();
}
@@ -0,0 +1,20 @@
package com.dfsek.terra.api.addons.annotations;
import org.jetbrains.annotations.NotNull;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Optional annotation that specifies the version of an addon.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Version {
/**
* @return Version of the addon.
*/
@NotNull String value();
}
@@ -0,0 +1,17 @@
package com.dfsek.terra.api.command;
import com.dfsek.terra.api.command.exception.CommandException;
import com.dfsek.terra.api.command.exception.MalformedCommandException;
import com.dfsek.terra.api.platform.CommandSender;
import java.util.List;
public interface CommandManager {
void execute(String command, CommandSender sender, List<String> args) throws CommandException;
void register(String name, Class<? extends CommandTemplate> clazz) throws MalformedCommandException;
List<String> tabComplete(String command, CommandSender sender, List<String> args) throws CommandException;
int getMaxArgumentDepth();
}
@@ -0,0 +1,7 @@
package com.dfsek.terra.api.command;
import com.dfsek.terra.api.platform.CommandSender;
public interface CommandTemplate {
void execute(CommandSender sender);
}
@@ -0,0 +1,38 @@
package com.dfsek.terra.api.command;
import com.dfsek.terra.api.platform.CommandSender;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public final class ExecutionState {
private final Set<String> switches = new HashSet<>();
private final Map<String, String> args = new HashMap<>();
private final CommandSender sender;
protected ExecutionState(CommandSender sender) {
this.sender = sender;
}
protected void addSwitch(String flag) {
switches.add(flag);
}
protected void addArgument(String arg, String value) {
args.put(arg, value);
}
public String getArgument(String argument) {
return args.get(argument);
}
public boolean hasSwitch(String flag) {
return switches.contains(flag);
}
public CommandSender getSender() {
return sender;
}
}
@@ -0,0 +1,283 @@
package com.dfsek.terra.api.command;
import com.dfsek.terra.api.TerraPlugin;
import com.dfsek.terra.api.command.annotation.Argument;
import com.dfsek.terra.api.command.annotation.Command;
import com.dfsek.terra.api.command.annotation.Subcommand;
import com.dfsek.terra.api.command.annotation.Switch;
import com.dfsek.terra.api.command.annotation.inject.ArgumentTarget;
import com.dfsek.terra.api.command.annotation.inject.SwitchTarget;
import com.dfsek.terra.api.command.annotation.type.DebugCommand;
import com.dfsek.terra.api.command.annotation.type.PlayerCommand;
import com.dfsek.terra.api.command.annotation.type.WorldCommand;
import com.dfsek.terra.api.command.arg.ArgumentParser;
import com.dfsek.terra.api.command.exception.CommandException;
import com.dfsek.terra.api.command.exception.ExecutionException;
import com.dfsek.terra.api.command.exception.InvalidArgumentsException;
import com.dfsek.terra.api.command.exception.MalformedCommandException;
import com.dfsek.terra.api.command.exception.SwitchFormatException;
import com.dfsek.terra.api.command.tab.TabCompleter;
import com.dfsek.terra.api.injection.Injector;
import com.dfsek.terra.api.injection.exception.InjectionException;
import com.dfsek.terra.api.platform.CommandSender;
import com.dfsek.terra.api.platform.entity.Player;
import com.dfsek.terra.api.util.ReflectionUtil;
import com.dfsek.terra.world.TerraWorld;
import net.jafama.FastMath;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class TerraCommandManager implements CommandManager {
private final Map<String, CommandHolder> commands = new HashMap<>();
private final Injector<TerraPlugin> pluginInjector;
private final TerraPlugin main;
public TerraCommandManager(TerraPlugin main) {
this.main = main;
this.pluginInjector = new Injector<>(main);
pluginInjector.addExplicitTarget(TerraPlugin.class);
}
@Override
public void execute(String commandName, CommandSender sender, List<String> argsIn) throws CommandException {
if(!commands.containsKey(commandName)) throw new InvalidArgumentsException("No such command \"" + commandName + "\"");
execute(commands.get(commandName), sender, new ArrayList<>(argsIn));
}
private void execute(CommandHolder commandHolder, CommandSender sender, List<String> args) throws CommandException {
Class<? extends CommandTemplate> commandClass = commandHolder.clazz;
if(commandClass.isAnnotationPresent(DebugCommand.class) && !main.isDebug()) {
sender.sendMessage("Command must be executed with debug mode enabled.");
return;
}
if(commandClass.isAnnotationPresent(PlayerCommand.class) && !(sender instanceof Player)) {
sender.sendMessage("Command must be executed by player.");
return;
}
if(commandClass.isAnnotationPresent(WorldCommand.class) && (!(sender instanceof Player) || !(((Player) sender).getWorld()).isTerraWorld())) {
sender.sendMessage("Command must be executed in a Terra world.");
return;
}
List<String> ogArgs = new ArrayList<>(args);
ExecutionState state = new ExecutionState(sender);
if(!commandClass.isAnnotationPresent(Command.class)) {
invoke(commandClass, state, commandHolder);
return;
}
Command command = commandClass.getAnnotation(Command.class);
if(command.arguments().length == 0 && command.subcommands().length == 0) {
if(args.isEmpty()) {
invoke(commandClass, state, commandHolder);
return;
} else throw new InvalidArgumentsException("Expected 0 arguments, found " + args.size());
}
if(!args.isEmpty() && commandHolder.subcommands.containsKey(args.get(0))) {
String c = args.get(0);
args.remove(0);
execute(commandHolder.subcommands.get(c), sender, args);
return;
}
boolean req = true;
for(Argument argument : command.arguments()) {
if(!req && argument.required()) {
throw new MalformedCommandException("Required arguments must come first! Arguments: " + Arrays.toString(command.arguments()));
}
req = argument.required();
if(args.isEmpty()) {
if(req) throw new InvalidArgumentsException("Invalid arguments: " + ogArgs + ", usage: " + command.usage());
break;
}
String arg = args.get(0);
if(arg.startsWith("-")) { // switches have started.
if(req) throw new InvalidArgumentsException("Switches must come after arguments.");
break;
}
state.addArgument(argument.value(), args.remove(0));
}
while(!args.isEmpty()) {
String aSwitch = args.remove(0);
if(!aSwitch.startsWith("-")) throw new SwitchFormatException("Invalid switch \"" + aSwitch + "\"");
String val = aSwitch.substring(1); // remove dash
if(!commandHolder.switches.containsKey(val)) throw new SwitchFormatException("No such switch \"" + aSwitch + "\"");
state.addSwitch(commandHolder.switches.get(val));
}
invoke(commandClass, state, commandHolder);
}
private void invoke(Class<? extends CommandTemplate> clazz, ExecutionState state, CommandHolder holder) throws CommandException {
try {
CommandTemplate template = clazz.getConstructor().newInstance();
pluginInjector.inject(template);
for(Field field : ReflectionUtil.getFields(clazz)) {
if(field.isAnnotationPresent(ArgumentTarget.class)) {
ArgumentTarget argumentTarget = field.getAnnotation(ArgumentTarget.class);
if(!holder.argumentMap.containsKey(argumentTarget.value())) {
throw new MalformedCommandException("Argument Target specifies nonexistent argument \"" + argumentTarget.value() + "\"");
}
String argument = argumentTarget.value();
ArgumentParser<?> argumentParser = holder.argumentMap.get(argumentTarget.value()).argumentParser().getConstructor().newInstance();
pluginInjector.inject(argumentParser);
field.setAccessible(true);
String value = state.getArgument(argument);
if(value == null) value = holder.argumentMap.get(argumentTarget.value()).defaultValue();
field.set(template, argumentParser.parse(state.getSender(), value));
}
if(field.isAnnotationPresent(SwitchTarget.class)) {
SwitchTarget switchTarget = field.getAnnotation(SwitchTarget.class);
if(!holder.switches.containsValue(switchTarget.value())) {
throw new MalformedCommandException("Switch Target specifies nonexistent switch \"" + switchTarget.value() + "\"");
}
if(!(field.getType() == boolean.class)) {
throw new MalformedCommandException("Switch Target must be of type boolean.");
}
field.setAccessible(true);
field.setBoolean(template, state.hasSwitch(switchTarget.value()));
}
}
try {
template.execute(state.getSender());
} catch(Throwable e) {
throw new ExecutionException("Failed to execute command: " + e.getMessage(), e);
}
} catch(InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | InjectionException e) {
throw new MalformedCommandException("Unable to reflectively instantiate command: ", e);
}
}
@Override
public void register(String name, Class<? extends CommandTemplate> clazz) throws MalformedCommandException {
commands.put(name, new CommandHolder(clazz));
}
@Override
public List<String> tabComplete(String command, CommandSender sender, List<String> args) throws CommandException {
if(args.isEmpty()) return new ArrayList<>(commands.keySet()).stream().sorted(String::compareTo).collect(Collectors.toList());
if(!commands.containsKey(command)) return Collections.emptyList();
return tabComplete(commands.get(command), sender, new ArrayList<>(args)).stream().filter(s -> s.toLowerCase().startsWith(args.get(args.size() - 1).toLowerCase())).sorted(String::compareTo).collect(Collectors.toList());
}
@Override
public int getMaxArgumentDepth() {
int max = 0;
for(CommandHolder value : commands.values()) {
max = FastMath.max(getMaxArgumentDepth(value), max);
}
return max;
}
private int getMaxArgumentDepth(CommandHolder holder) {
int max = 0;
max = FastMath.max(holder.arguments.size() + holder.switchList.size(), max);
for(CommandHolder value : holder.subcommands.values()) {
max = FastMath.max(max, getMaxArgumentDepth(value) + 1);
}
return max;
}
private List<String> tabComplete(CommandHolder holder, CommandSender sender, List<String> args) throws CommandException {
if(args.isEmpty()) return Collections.emptyList();
List<String> completions = new ArrayList<>();
if(args.size() == 1) {
completions.addAll(holder.subcommands.keySet());
}
if(holder.subcommands.containsKey(args.get(0))) {
List<String> newArgs = new ArrayList<>(args);
newArgs.remove(0);
completions.addAll(tabComplete(holder.subcommands.get(args.get(0)), sender, newArgs));
}
try {
if(args.size() <= holder.arguments.size()) {
TabCompleter completer = holder.arguments.get(args.size() - 1).tabCompleter().getConstructor().newInstance();
pluginInjector.inject(completer);
completions.addAll(completer.complete(sender));
}
} catch(InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | InjectionException e) {
throw new MalformedCommandException("Unable to reflectively instantiate tab-completer: ", e);
}
return completions;
}
/**
* Pre-processes command metadata.
*/
private static final class CommandHolder {
private final Class<? extends CommandTemplate> clazz;
private final Map<String, CommandHolder> subcommands = new HashMap<>();
private final Map<String, String> switches = new HashMap<>();
private final List<Argument> arguments;
private final List<Switch> switchList;
private final Map<String, Argument> argumentMap = new HashMap<>();
private CommandHolder(Class<? extends CommandTemplate> clazz) throws MalformedCommandException {
this.clazz = clazz;
if(clazz.isAnnotationPresent(Command.class)) {
Command command = clazz.getAnnotation(Command.class);
for(Subcommand subcommand : command.subcommands()) {
if(subcommands.containsKey(subcommand.value()))
throw new MalformedCommandException("Duplicate subcommand: " + subcommand);
CommandHolder holder = new CommandHolder(subcommand.clazz());
subcommands.put(subcommand.value(), holder);
for(String alias : subcommand.aliases()) {
subcommands.put(alias, holder);
}
}
for(Switch aSwitch : command.switches()) {
if(switches.containsKey(aSwitch.value())) throw new MalformedCommandException("Duplicate switch: " + aSwitch);
switches.put(aSwitch.value(), aSwitch.value());
for(String alias : aSwitch.aliases()) {
switches.put(alias, aSwitch.value());
}
}
for(Argument argument : command.arguments()) {
if(argumentMap.containsKey(argument.value())) throw new MalformedCommandException("Duplicate argument: " + argument);
argumentMap.put(argument.value(), argument);
}
arguments = Arrays.asList(command.arguments());
switchList = Arrays.asList(command.switches());
} else {
arguments = Collections.emptyList();
switchList = Collections.emptyList();
}
}
}
}
@@ -0,0 +1,25 @@
package com.dfsek.terra.api.command.annotation;
import com.dfsek.terra.api.command.arg.ArgumentParser;
import com.dfsek.terra.api.command.arg.StringArgumentParser;
import com.dfsek.terra.api.command.tab.NothingCompleter;
import com.dfsek.terra.api.command.tab.TabCompleter;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Argument {
String value();
boolean required() default true;
Class<? extends TabCompleter> tabCompleter() default NothingCompleter.class;
Class<? extends ArgumentParser<?>> argumentParser() default StringArgumentParser.class;
String defaultValue() default "";
}
@@ -0,0 +1,18 @@
package com.dfsek.terra.api.command.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Command {
Argument[] arguments() default {};
Switch[] switches() default {};
Subcommand[] subcommands() default {};
String usage() default "";
}
@@ -0,0 +1,18 @@
package com.dfsek.terra.api.command.annotation;
import com.dfsek.terra.api.command.CommandTemplate;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Subcommand {
String value();
String[] aliases() default {};
Class<? extends CommandTemplate> clazz();
}
@@ -0,0 +1,14 @@
package com.dfsek.terra.api.command.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Switch {
String value();
String[] aliases() default {};
}
@@ -0,0 +1,12 @@
package com.dfsek.terra.api.command.annotation.inject;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ArgumentTarget {
String value();
}
@@ -0,0 +1,12 @@
package com.dfsek.terra.api.command.annotation.inject;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SwitchTarget {
String value();
}
@@ -0,0 +1,14 @@
package com.dfsek.terra.api.command.annotation.type;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Command may only be executed with debug mode enabled.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DebugCommand {
}
@@ -0,0 +1,14 @@
package com.dfsek.terra.api.command.annotation.type;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marks command as player-only
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface PlayerCommand {
}
@@ -0,0 +1,14 @@
package com.dfsek.terra.api.command.annotation.type;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Command may only be executed in a Terra world.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface WorldCommand {
}
@@ -0,0 +1,7 @@
package com.dfsek.terra.api.command.arg;
import com.dfsek.terra.api.platform.CommandSender;
public interface ArgumentParser<T> {
T parse(CommandSender sender, String arg);
}
@@ -0,0 +1,10 @@
package com.dfsek.terra.api.command.arg;
import com.dfsek.terra.api.platform.CommandSender;
public class DoubleArgumentParser implements ArgumentParser<Double> {
@Override
public Double parse(CommandSender sender, String arg) {
return arg == null ? null : Double.parseDouble(arg);
}
}
@@ -0,0 +1,10 @@
package com.dfsek.terra.api.command.arg;
import com.dfsek.terra.api.platform.CommandSender;
public class IntegerArgumentParser implements ArgumentParser<Integer> {
@Override
public Integer parse(CommandSender sender, String arg) {
return arg == null ? null : Integer.parseInt(arg);
}
}
@@ -0,0 +1,10 @@
package com.dfsek.terra.api.command.arg;
import com.dfsek.terra.api.platform.CommandSender;
public class StringArgumentParser implements ArgumentParser<String> {
@Override
public String parse(CommandSender sender, String arg) {
return arg;
}
}
@@ -0,0 +1,13 @@
package com.dfsek.terra.api.command.exception;
public abstract class CommandException extends Exception {
private static final long serialVersionUID = -2955328495045879822L;
public CommandException(String message) {
super(message);
}
public CommandException(String message, Throwable cause) {
super(message, cause);
}
}
@@ -0,0 +1,13 @@
package com.dfsek.terra.api.command.exception;
public class ExecutionException extends CommandException {
private static final long serialVersionUID = -6345523475880607959L;
public ExecutionException(String message) {
super(message);
}
public ExecutionException(String message, Throwable cause) {
super(message, cause);
}
}
@@ -0,0 +1,13 @@
package com.dfsek.terra.api.command.exception;
public class InvalidArgumentsException extends CommandException {
private static final long serialVersionUID = 7563619667472569824L;
public InvalidArgumentsException(String message) {
super(message);
}
public InvalidArgumentsException(String message, Throwable cause) {
super(message, cause);
}
}
@@ -0,0 +1,16 @@
package com.dfsek.terra.api.command.exception;
/**
* Thrown when command is incorrectly defined.
*/
public class MalformedCommandException extends CommandException {
private static final long serialVersionUID = -5417760860407895496L;
public MalformedCommandException(String message) {
super(message);
}
public MalformedCommandException(String message, Throwable cause) {
super(message, cause);
}
}
@@ -0,0 +1,13 @@
package com.dfsek.terra.api.command.exception;
public class SwitchFormatException extends CommandException {
private static final long serialVersionUID = -965858989317844628L;
public SwitchFormatException(String message) {
super(message);
}
public SwitchFormatException(String message, Throwable cause) {
super(message, cause);
}
}
@@ -0,0 +1,13 @@
package com.dfsek.terra.api.command.tab;
import com.dfsek.terra.api.platform.CommandSender;
import java.util.Collections;
import java.util.List;
public class NothingCompleter implements TabCompleter {
@Override
public List<String> complete(CommandSender sender) {
return Collections.emptyList();
}
}
@@ -0,0 +1,9 @@
package com.dfsek.terra.api.command.tab;
import com.dfsek.terra.api.platform.CommandSender;
import java.util.List;
public interface TabCompleter {
List<String> complete(CommandSender sender);
}
@@ -1,48 +0,0 @@
package com.dfsek.terra.api.core;
import com.dfsek.terra.api.LoaderRegistrar;
import com.dfsek.terra.api.core.event.EventManager;
import com.dfsek.terra.api.platform.handle.ItemHandle;
import com.dfsek.terra.api.platform.handle.WorldHandle;
import com.dfsek.terra.api.platform.world.World;
import com.dfsek.terra.config.PluginConfig;
import com.dfsek.terra.config.lang.Language;
import com.dfsek.terra.debug.DebugLogger;
import com.dfsek.terra.registry.ConfigRegistry;
import com.dfsek.terra.world.TerraWorld;
import java.io.File;
import java.util.logging.Logger;
public interface TerraPlugin extends LoaderRegistrar {
WorldHandle getWorldHandle();
boolean isEnabled();
TerraWorld getWorld(World world);
Logger getLogger();
PluginConfig getTerraConfig();
File getDataFolder();
boolean isDebug();
Language getLanguage();
ConfigRegistry getRegistry();
void reload();
ItemHandle getItemHandle();
void saveDefaultConfig();
String platformName();
DebugLogger getDebugLogger();
EventManager getEventManager();
}
@@ -1,4 +0,0 @@
package com.dfsek.terra.api.core.event;
public interface EventListener {
}
@@ -1,14 +0,0 @@
package com.dfsek.terra.api.core.event;
import com.dfsek.terra.api.core.event.events.Event;
public interface EventManager {
/**
* Call an event, and return the execution status.
* @param event Event to pass to all registered EventListeners.
* @return False if the event is cancellable and has been cancelled, otherwise true.
*/
boolean callEvent(Event event);
void registerListener(EventListener listener);
}
@@ -1,11 +0,0 @@
package com.dfsek.terra.api.core.event.events;
/**
* Events that implement this interface may be cancelled.
*
* Cancelling an event is assumed to stop the execution of whatever action triggered the event.
*/
public interface Cancellable extends Event {
boolean isCancelled();
void setCancelled();
}
@@ -1,4 +0,0 @@
package com.dfsek.terra.api.core.event.events;
public interface Event {
}
@@ -1,16 +0,0 @@
package com.dfsek.terra.api.core.event.events.config;
import com.dfsek.terra.api.core.event.events.Event;
import com.dfsek.terra.config.pack.ConfigPack;
public abstract class ConfigPackLoadEvent implements Event {
private final ConfigPack pack;
public ConfigPackLoadEvent(ConfigPack pack) {
this.pack = pack;
}
public ConfigPack getPack() {
return pack;
}
}
@@ -1,19 +0,0 @@
package com.dfsek.terra.api.core.event.events.world;
import com.dfsek.terra.api.core.event.events.Event;
import com.dfsek.terra.world.TerraWorld;
/**
* Called upon initialization of a TerraWorld.
*/
public class TerraWorldLoadEvent implements Event {
private final TerraWorld world;
public TerraWorldLoadEvent(TerraWorld world) {
this.world = world;
}
public TerraWorld getWorld() {
return world;
}
}
@@ -0,0 +1,12 @@
package com.dfsek.terra.api.event;
import com.dfsek.terra.api.event.events.Event;
/**
* Marker interface for a class that contains event listener methods.
*
* @see Event
* @see EventManager
*/
public interface EventListener {
}
@@ -0,0 +1,25 @@
package com.dfsek.terra.api.event;
import com.dfsek.terra.api.addons.TerraAddon;
import com.dfsek.terra.api.event.events.Event;
/**
* Manages event registration and triggering.
*/
public interface EventManager {
/**
* Call an event, and return the execution status.
*
* @param event Event to pass to all registered EventListeners.
* @return False if the event is cancellable and has been cancelled, otherwise true.
*/
boolean callEvent(Event event);
/**
* Register an {@link EventListener} under an {@link TerraAddon}.
*
* @param addon Addon to register listener for.
* @param listener Listener to register.
*/
void registerListener(TerraAddon addon, EventListener listener);
}
@@ -1,9 +1,12 @@
package com.dfsek.terra.api.core.event;
package com.dfsek.terra.api.event;
import com.dfsek.terra.api.core.TerraPlugin;
import com.dfsek.terra.api.core.event.annotations.Priority;
import com.dfsek.terra.api.core.event.events.Cancellable;
import com.dfsek.terra.api.core.event.events.Event;
import com.dfsek.terra.api.TerraPlugin;
import com.dfsek.terra.api.addons.TerraAddon;
import com.dfsek.terra.api.event.annotations.Global;
import com.dfsek.terra.api.event.annotations.Priority;
import com.dfsek.terra.api.event.events.Cancellable;
import com.dfsek.terra.api.event.events.Event;
import com.dfsek.terra.api.event.events.PackEvent;
import com.dfsek.terra.api.util.ReflectionUtil;
import java.io.PrintWriter;
@@ -29,19 +32,30 @@ public class TerraEventManager implements EventManager {
public boolean callEvent(Event event) {
listeners.getOrDefault(event.getClass(), Collections.emptyList()).forEach(listenerHolder -> {
try {
listenerHolder.method.invoke(listenerHolder.listener, event);
if(event instanceof PackEvent && !listenerHolder.global) {
PackEvent packEvent = (PackEvent) event;
if(packEvent
.getPack()
.getTemplate()
.getAddons()
.contains(listenerHolder.addon)) {
listenerHolder.method.invoke(listenerHolder.listener, event);
}
} else {
listenerHolder.method.invoke(listenerHolder.listener, event);
}
} catch(InvocationTargetException e) {
StringWriter writer = new StringWriter();
e.getTargetException().printStackTrace(new PrintWriter(writer));
main.getLogger().warning("Exception occurred during event handling:");
main.getLogger().warning(writer.toString());
main.getLogger().warning("Report this to the maintainers of " + listenerHolder.method.getName());
main.logger().warning("Exception occurred during event handling:");
main.logger().warning(writer.toString());
main.logger().warning("Report this to the maintainers of " + listenerHolder.method.getName());
} catch(Exception e) {
StringWriter writer = new StringWriter();
e.printStackTrace(new PrintWriter(writer));
main.getLogger().warning("Exception occurred during event handling:");
main.getLogger().warning(writer.toString());
main.getLogger().warning("Report this to the maintainers of " + listenerHolder.method.getName());
main.logger().warning("Exception occurred during event handling:");
main.logger().warning(writer.toString());
main.logger().warning("Report this to the maintainers of " + listenerHolder.method.getName());
}
}
);
@@ -51,7 +65,7 @@ public class TerraEventManager implements EventManager {
@SuppressWarnings("unchecked")
@Override
public void registerListener(EventListener listener) {
public void registerListener(TerraAddon addon, EventListener listener) {
Class<? extends EventListener> listenerClass = listener.getClass();
Method[] methods = ReflectionUtil.getMethods(listenerClass);
@@ -68,7 +82,7 @@ public class TerraEventManager implements EventManager {
List<ListenerHolder> holders = listeners.computeIfAbsent((Class<? extends Event>) eventParam, e -> new ArrayList<>());
holders.add(new ListenerHolder(method, listener, priority));
holders.add(new ListenerHolder(method, listener, priority, addon, method.getAnnotation(Global.class) != null));
holders.sort(Comparator.comparingInt(ListenerHolder::getPriority)); // Sort priorities.
}
@@ -78,11 +92,15 @@ public class TerraEventManager implements EventManager {
private final Method method;
private final EventListener listener;
private final int priority;
private final TerraAddon addon;
private final boolean global;
private ListenerHolder(Method method, EventListener listener, int priority) {
private ListenerHolder(Method method, EventListener listener, int priority, TerraAddon addon, boolean global) {
this.method = method;
this.listener = listener;
this.priority = priority;
this.addon = addon;
this.global = global;
}
public int getPriority() {
@@ -0,0 +1,17 @@
package com.dfsek.terra.api.event.annotations;
import com.dfsek.terra.api.event.events.PackEvent;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Specifies that an event handler is to handle all {@link PackEvent}s, regardless of whether the pack
* depends on the addon's listener.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Global {
}
@@ -1,4 +1,4 @@
package com.dfsek.terra.api.core.event.annotations;
package com.dfsek.terra.api.event.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -12,11 +12,11 @@ import java.lang.annotation.Target;
@Target(ElementType.METHOD)
public @interface Priority {
/**
* Highest possible priority. Listeners with this priority will always be invoked first.
* Highest possible priority. Listeners with this priority will always be invoked last.
*/
int HIGHEST = Integer.MAX_VALUE;
/**
* Lowest possible priority. Listeners with this priority will always be invoked last.
* Lowest possible priority. Listeners with this priority will always be invoked first.
*/
int LOWEST = Integer.MIN_VALUE;
/**
@@ -0,0 +1,20 @@
package com.dfsek.terra.api.event.events;
import com.dfsek.terra.api.util.mutable.MutableBoolean;
/**
* Abstract class containing basic {@link Cancellable} implementation.
*/
public abstract class AbstractCancellable implements Cancellable {
private final MutableBoolean cancelled = new MutableBoolean(false);
@Override
public boolean isCancelled() {
return cancelled.get();
}
@Override
public void setCancelled(boolean cancelled) {
this.cancelled.set(cancelled);
}
}
@@ -0,0 +1,22 @@
package com.dfsek.terra.api.event.events;
/**
* Events that implement this interface may be cancelled.
* <p>
* Cancelling an event is assumed to stop the execution of whatever action triggered the event.
*/
public interface Cancellable extends Event {
/**
* Get the cancellation status of the event.
*
* @return Whether event is cancelled.
*/
boolean isCancelled();
/**
* Set the cancellation status of the event.
*
* @param cancelled Whether event is cancelled.
*/
void setCancelled(boolean cancelled);
}
@@ -0,0 +1,7 @@
package com.dfsek.terra.api.event.events;
/**
* An event that addons may listen to.
*/
public interface Event {
}
@@ -0,0 +1,20 @@
package com.dfsek.terra.api.event.events;
import com.dfsek.terra.api.event.annotations.Global;
import com.dfsek.terra.config.pack.ConfigPack;
/**
* An event with functionality directly linked to a {@link ConfigPack}.
* <p>
* PackEvents are only invoked when the pack specifies the addon in its
* {@code addon} key (or when the listener is annotated {@link Global}).
*/
@SuppressWarnings("InterfaceMayBeAnnotatedFunctional")
public interface PackEvent extends Event {
/**
* Get the {@link ConfigPack} associated with this event.
*
* @return ConfigPack associated with the event.
*/
ConfigPack getPack();
}
@@ -0,0 +1,20 @@
package com.dfsek.terra.api.event.events.config;
import com.dfsek.terra.api.event.events.PackEvent;
import com.dfsek.terra.config.pack.ConfigPack;
/**
* An event related to the loading process of config packs.
*/
public abstract class ConfigPackLoadEvent implements PackEvent {
private final ConfigPack pack;
public ConfigPackLoadEvent(ConfigPack pack) {
this.pack = pack;
}
@Override
public ConfigPack getPack() {
return pack;
}
}
@@ -1,4 +1,4 @@
package com.dfsek.terra.api.core.event.events.config;
package com.dfsek.terra.api.event.events.config;
import com.dfsek.terra.config.pack.ConfigPack;
@@ -1,4 +1,4 @@
package com.dfsek.terra.api.core.event.events.config;
package com.dfsek.terra.api.event.events.config;
import com.dfsek.terra.config.pack.ConfigPack;
@@ -0,0 +1,31 @@
package com.dfsek.terra.api.event.events.world;
import com.dfsek.terra.api.event.events.PackEvent;
import com.dfsek.terra.config.pack.ConfigPack;
import com.dfsek.terra.config.pack.WorldConfig;
import com.dfsek.terra.world.TerraWorld;
/**
* Called upon initialization of a TerraWorld.
*/
public class TerraWorldLoadEvent implements PackEvent {
private final TerraWorld world;
private final ConfigPack pack;
public TerraWorldLoadEvent(TerraWorld world, ConfigPack pack) {
this.world = world;
this.pack = pack;
}
public TerraWorld getWorld() {
return world;
}
public ConfigPack getPack() {
return pack;
}
public WorldConfig getWorldConfig() {
return world.getConfig();
}
}
@@ -0,0 +1,45 @@
package com.dfsek.terra.api.event.events.world.generation;
import com.dfsek.terra.api.event.events.PackEvent;
import com.dfsek.terra.api.math.vector.Location;
import com.dfsek.terra.api.platform.entity.Entity;
import com.dfsek.terra.api.structures.structure.buffer.items.BufferedEntity;
import com.dfsek.terra.config.pack.ConfigPack;
/**
* Called when an entity is spawned via {@link BufferedEntity}.
*/
public class EntitySpawnEvent implements PackEvent {
private final ConfigPack pack;
private final Entity entity;
private final Location location;
public EntitySpawnEvent(ConfigPack pack, Entity entity, Location location) {
this.pack = pack;
this.entity = entity;
this.location = location;
}
@Override
public ConfigPack getPack() {
return pack;
}
/**
* Get the entity that triggered the event.
*
* @return The entity.
*/
public Entity getEntity() {
return entity;
}
/**
* Get the location of the entity.
*
* @return Location of the entity.
*/
public Location getLocation() {
return location;
}
}
@@ -0,0 +1,80 @@
package com.dfsek.terra.api.event.events.world.generation;
import com.dfsek.terra.api.event.events.AbstractCancellable;
import com.dfsek.terra.api.event.events.Cancellable;
import com.dfsek.terra.api.event.events.PackEvent;
import com.dfsek.terra.api.platform.block.Block;
import com.dfsek.terra.api.platform.block.state.Container;
import com.dfsek.terra.api.structures.loot.LootTable;
import com.dfsek.terra.api.structures.script.StructureScript;
import com.dfsek.terra.api.structures.structure.buffer.items.BufferedLootApplication;
import com.dfsek.terra.config.pack.ConfigPack;
import org.jetbrains.annotations.NotNull;
/**
* Called when loot is populated via {@link BufferedLootApplication}.
*/
public class LootPopulateEvent extends AbstractCancellable implements PackEvent, Cancellable {
private final Block block;
private final Container container;
private LootTable table;
private final ConfigPack pack;
private final StructureScript script;
public LootPopulateEvent(Block block, Container container, LootTable table, ConfigPack pack, StructureScript script) {
this.block = block;
this.container = container;
this.table = table;
this.pack = pack;
this.script = script;
}
@Override
public ConfigPack getPack() {
return pack;
}
/**
* Get the block containing the tile entity loot is applied to.
*
* @return Block at which loot is applied.
*/
public Block getBlock() {
return block;
}
/**
* Get the {@link Container} representing the inventory.
*
* @return Inventory recieving loot.
*/
public Container getContainer() {
return container;
}
/**
* Get the loot table to be populated.
* @return Loot table.
*/
public LootTable getTable() {
return table;
}
/**
* Set the loot table to be populated.
*
* @param table New loot table.
*/
public void setTable(@NotNull LootTable table) {
this.table = table;
}
/**
* Get the script used to generate the structure.
*
* @return Structure script.
*/
public StructureScript getStructureScript() {
return script;
}
}
@@ -0,0 +1,78 @@
package com.dfsek.terra.api.injection;
import com.dfsek.terra.api.injection.annotations.Inject;
import com.dfsek.terra.api.injection.exception.InjectionException;
import com.dfsek.terra.api.util.ReflectionUtil;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.Set;
/**
* Dynamic dependency injector.
* <p>
* Stores an object to inject, and injects it into objects passed to {@link #inject(Object)}.
*
* @param <T> Type of object to inject.
*/
public class Injector<T> {
private final T value;
private final Set<Class<? extends T>> targets = new HashSet<>();
/**
* Instantiate an Injector with a value to inject
*
* @param value Value to inject
*/
public Injector(T value) {
this.value = value;
}
/**
* Add an explicit class as a target. Useful for applications where subclasses may cause issues with DI.
*
* @param target Target class type.
*/
public void addExplicitTarget(Class<? extends T> target) {
targets.add(target);
}
/**
* Inject the stored object into an object.
* <p>
* Injects the stored object into any non-static, non-final fields
* annotated with {@link Inject},
* with type matching the stored object or any explicit targets
* ({@link #addExplicitTarget(Class)}.
*
* @param object Object to inject into
* @throws InjectionException If:
* <ul>
* <li>Matching field annotated with {@link Inject} is final</li>
* <li>Matching field annotated with {@link Inject} is static</li>
* <li>A reflective access exception occurs</li>
* </ul>
*/
public void inject(Object object) throws InjectionException {
for(Field field : ReflectionUtil.getFields(object.getClass())) {
Inject inject = field.getAnnotation(Inject.class);
if(inject == null) continue;
if(value.getClass().equals(field.getType()) || targets.contains(field.getType())) {
int mod = field.getModifiers();
if(Modifier.isFinal(mod)) {
throw new InjectionException("Attempted to inject final field: " + field);
}
if(Modifier.isStatic(mod)) {
throw new InjectionException("Attempted to inject static field: " + field);
}
field.setAccessible(true);
try {
field.set(object, value);
} catch(IllegalAccessException e) {
throw new InjectionException("Failed to inject field: " + field, e);
}
}
}
}
}
@@ -0,0 +1,14 @@
package com.dfsek.terra.api.injection.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Specifies that a field is a target for dependency injection.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Inject {
}
@@ -0,0 +1,18 @@
package com.dfsek.terra.api.injection.exception;
import com.dfsek.terra.api.injection.Injector;
/**
* Thrown when dynamic dependency injection cannot be completed by an {@link Injector}.
*/
public class InjectionException extends Exception {
private static final long serialVersionUID = -6929631447064215387L;
public InjectionException(String message) {
super(message);
}
public InjectionException(String message, Throwable cause) {
super(message, cause);
}
}
@@ -1,7 +1,7 @@
package com.dfsek.terra.api.math;
import com.dfsek.terra.api.util.FastRandom;
import com.dfsek.terra.world.generation.math.Sampler;
import com.dfsek.terra.world.generation.math.samplers.Sampler;
import net.jafama.FastMath;
import java.util.List;
@@ -1,96 +0,0 @@
package com.dfsek.terra.api.math;
import com.dfsek.terra.api.math.noise.NoiseSampler;
import java.util.Collections;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
@SuppressWarnings("unchecked")
public class ProbabilityCollection<E> {
private final Set<E> cont = new HashSet<>();
private Object[] array = new Object[0];
private int size;
public com.dfsek.terra.api.math.ProbabilityCollection<E> add(E item, int probability) {
if(!cont.contains(item)) size++;
cont.add(item);
int oldLength = array.length;
Object[] newArray = new Object[array.length + probability];
System.arraycopy(array, 0, newArray, 0, array.length); // Expand array.
array = newArray;
for(int i = oldLength; i < array.length; i++) array[i] = item;
return this;
}
public E get(Random r) {
if(array.length == 0) return null;
return (E) array[r.nextInt(array.length)];
}
public E get(NoiseSampler n, double x, double y, double z) {
if(array.length == 0) return null;
return (E) array[MathUtil.normalizeIndex(n.getNoise(x, y, z), array.length)];
}
public E get(NoiseSampler n, double x, double z) {
if(array.length == 0) return null;
return (E) array[MathUtil.normalizeIndex(n.getNoise(x, z), array.length)];
}
public int getTotalProbability() {
return array.length;
}
public int size() {
return size;
}
public Set<E> getContents() {
return new HashSet<>(cont);
}
public static final class Singleton<T> extends ProbabilityCollection<T> {
private final T single;
public Singleton(T single) {
this.single = single;
}
@Override
public ProbabilityCollection<T> add(T item, int probability) {
throw new UnsupportedOperationException();
}
@Override
public T get(Random r) {
return single;
}
@Override
public T get(NoiseSampler n, double x, double y, double z) {
return single;
}
@Override
public T get(NoiseSampler n, double x, double z) {
return single;
}
@Override
public int getTotalProbability() {
return 1;
}
@Override
public int size() {
return 1;
}
@Override
public Set<T> getContents() {
return Collections.singleton(single);
}
}
}
@@ -24,7 +24,7 @@ public class Range implements Iterable<Integer> {
return max;
}
public com.dfsek.terra.api.math.Range setMax(int max) {
public Range setMax(int max) {
this.max = max;
return this;
}
@@ -33,7 +33,7 @@ public class Range implements Iterable<Integer> {
return min;
}
public com.dfsek.terra.api.math.Range setMin(int min) {
public Range setMin(int min) {
this.min = min;
return this;
}
@@ -42,35 +42,35 @@ public class Range implements Iterable<Integer> {
return max - min;
}
public com.dfsek.terra.api.math.Range multiply(int mult) {
public Range multiply(int mult) {
min *= mult;
max *= mult;
return this;
}
public com.dfsek.terra.api.math.Range reflect(int pt) {
return new com.dfsek.terra.api.math.Range(2 * pt - this.getMax(), 2 * pt - this.getMin());
public Range reflect(int pt) {
return new Range(2 * pt - this.getMax(), 2 * pt - this.getMin());
}
public int get(Random r) {
return r.nextInt((max - min) + 1) + min;
}
public com.dfsek.terra.api.math.Range intersects(com.dfsek.terra.api.math.Range other) {
public Range intersects(com.dfsek.terra.api.math.Range other) {
try {
return new com.dfsek.terra.api.math.Range(FastMath.max(this.getMin(), other.getMin()), FastMath.min(this.getMax(), other.getMax()));
return new Range(FastMath.max(this.getMin(), other.getMin()), FastMath.min(this.getMax(), other.getMax()));
} catch(IllegalArgumentException e) {
return null;
}
}
public com.dfsek.terra.api.math.Range add(int add) {
public Range add(int add) {
this.min += add;
this.max += add;
return this;
}
public com.dfsek.terra.api.math.Range sub(int sub) {
public Range sub(int sub) {
this.min -= sub;
this.max -= sub;
return this;
@@ -89,7 +89,7 @@ public class Range implements Iterable<Integer> {
@Override
public boolean equals(Object obj) {
if(!(obj instanceof com.dfsek.terra.api.math.Range)) return false;
com.dfsek.terra.api.math.Range other = (com.dfsek.terra.api.math.Range) obj;
Range other = (com.dfsek.terra.api.math.Range) obj;
return other.getMin() == this.getMin() && other.getMax() == this.getMax();
}
@@ -100,10 +100,10 @@ public class Range implements Iterable<Integer> {
}
private static class RangeIterator implements Iterator<Integer> {
private final com.dfsek.terra.api.math.Range m;
private final Range m;
private Integer current;
public RangeIterator(com.dfsek.terra.api.math.Range m) {
public RangeIterator(Range m) {
this.m = m;
current = m.getMin();
}
@@ -14,7 +14,7 @@ import com.dfsek.terra.config.loaders.config.function.FunctionTemplate;
import java.util.Map;
/**
* Sampler implementation using Paralithic expression
* Sampler3D implementation using Paralithic expression
*/
public class ExpressionSampler implements NoiseSampler {
private final Expression expression;
@@ -1,7 +1,7 @@
package com.dfsek.terra.api.math.noise.samplers.noise;
/**
* Sampler implementation that returns a constant.
* Sampler3D implementation that returns a constant.
*/
public class ConstantSampler extends NoiseFunction {
private final double constant;
@@ -0,0 +1,105 @@
package com.dfsek.terra.api.math.noise.samplers.noise;
import com.dfsek.terra.api.math.noise.samplers.noise.random.WhiteNoiseSampler;
import net.jafama.FastMath;
public class GaborNoiseSampler extends NoiseFunction {
private final WhiteNoiseSampler rand;
private double k = 1.0;
private double a = 0.1;
private double f0 = 0.625;
private double kernelRadius = (FastMath.sqrt(-FastMath.log(0.05) / Math.PI) / a);
private double omega0 = Math.PI * 0.25;
private boolean isotropic = true;
private double impulsesPerKernel = 64d;
private double impulseDensity = (impulsesPerKernel / (Math.PI * kernelRadius * kernelRadius));
private double impulsesPerCell = impulseDensity * kernelRadius * kernelRadius;
private double g = FastMath.exp(-impulsesPerCell);
public GaborNoiseSampler(int seed) {
super(seed);
rand = new WhiteNoiseSampler(seed);
}
public void setIsotropic(boolean isotropic) {
this.isotropic = isotropic;
}
public void setImpulsesPerKernel(double impulsesPerKernel) {
this.impulsesPerKernel = impulsesPerKernel;
recalculateRadiusAndDensity();
}
public void setA(double a) {
this.a = a;
recalculateRadiusAndDensity();
}
public void setFrequency0(double f0) {
this.f0 = f0;
}
public void setRotation(double omega0) {
this.omega0 = Math.PI * omega0;
}
public void setDeviation(double k) {
this.k = k;
}
private void recalculateRadiusAndDensity() {
kernelRadius = (FastMath.sqrt(-FastMath.log(0.05) / Math.PI) / this.a);
impulseDensity = (impulsesPerKernel / (Math.PI * kernelRadius * kernelRadius));
impulsesPerCell = impulseDensity * kernelRadius * kernelRadius;
g = FastMath.exp(-impulsesPerCell);
}
@Override
public double getNoiseRaw(int seed, double x, double z) {
return gaborNoise(seed, x, z);
}
@Override
public double getNoiseRaw(int seed, double x, double y, double z) {
return gaborNoise(seed, x, z);
}
private double gaborNoise(int seed, double x, double y) {
x /= kernelRadius;
y /= kernelRadius;
int xi = fastFloor(x);
int yi = fastFloor(y);
double xf = x - xi;
double yf = y - yi;
double noise = 0;
for(int dx = -1; dx <= 1; dx++) {
for(int dz = -1; dz <= 1; dz++) {
noise += calculateCell(seed, xi + dx, yi + dz, xf - dx, yf - dz);
}
}
return noise;
}
private double calculateCell(int seed, int xi, int yi, double x, double y) {
long mashedSeed = murmur64(31L * xi + yi) + seed;
double gaussianSource = (rand.getNoiseRaw(mashedSeed++) + 1) / 2;
int impulses = 0;
while(gaussianSource > g) {
impulses++;
gaussianSource *= (rand.getNoiseRaw(mashedSeed++) + 1) / 2;
}
double noise = 0;
for(int i = 0; i < impulses; i++) {
noise += rand.getNoiseRaw(mashedSeed++) * gabor(isotropic ? (rand.getNoiseRaw(mashedSeed++) + 1) * Math.PI : omega0, x * kernelRadius, y * kernelRadius);
}
return noise;
}
private double gabor(double omega_0, double x, double y) {
return k * (FastMath.exp(-Math.PI * (a * a) * (x * x + y * y)) * fastCos(2 * Math.PI * f0 * (x * fastCos(omega_0) + y * fastSin(omega_0))));
}
}
@@ -22,6 +22,8 @@ public abstract class NoiseFunction implements NoiseSampler {
return f >= 0 ? (int) f : (int) f - 1;
}
static final int precision = 100;
protected static int hash(int seed, int xPrimed, int yPrimed, int zPrimed) {
int hash = seed ^ xPrimed ^ yPrimed ^ zPrimed;
@@ -36,6 +38,8 @@ public abstract class NoiseFunction implements NoiseSampler {
return hash;
}
static final int modulus = 360 * precision;
protected static int fastRound(double f) {
return f >= 0 ? (int) (f + 0.5f) : (int) (f - 0.5);
}
@@ -73,6 +77,48 @@ public abstract class NoiseFunction implements NoiseSampler {
return FastMath.sqrt(f);
}
static final double[] sin = new double[360 * 100]; // lookup table
static {
for(int i = 0; i < sin.length; i++) {
sin[i] = (float) Math.sin((double) (i) / (precision));
}
}
protected static int fastCeil(double f) {
int i = (int) f;
if(i < f) i++;
return i;
}
/**
* Murmur64 hashing function
*
* @param h Input value
* @return Hashed value
*/
protected static long murmur64(long h) {
h ^= h >>> 33;
h *= 0xff51afd7ed558ccdL;
h ^= h >>> 33;
h *= 0xc4ceb9fe1a85ec53L;
h ^= h >>> 33;
return h;
}
private static double sinLookup(int a) {
return a >= 0 ? sin[a % (modulus)] : -sin[-a % (modulus)];
}
protected static double fastSin(double a) {
return sinLookup((int) (a * precision + 0.5f));
}
protected static double fastCos(double a) {
return sinLookup((int) ((a + Math.PI / 2) * precision + 0.5f));
}
public void setSeed(int seed) {
this.seed = seed;
}
@@ -12,38 +12,41 @@ public class WhiteNoiseSampler extends NoiseFunction {
super(seed);
}
public double getNoiseRaw(long seed) {
return (Double.longBitsToDouble((murmur64(seed) & 0x000fffffffffffffL) | POSITIVE_POW1) - 1.5) * 2;
}
@Override
public double getNoiseRaw(int seed, double x, double y) {
long hashX = Double.doubleToRawLongBits(x) ^ seed;
long hashZ = Double.doubleToRawLongBits(y) ^ seed;
long hash = ((hashX ^ (hashX >>> 32)) + ((hashZ ^ (hashZ >>> 32)) << 32)) ^ seed;
long base = (murmur64(hash) & 0x000fffffffffffffL)
| POSITIVE_POW1; // Sign and exponent
return (Double.longBitsToDouble(base) - 1.5) * 2;
return (getNoiseUnmapped(seed, x, y) - 1.5) * 2;
}
@Override
public double getNoiseRaw(int seed, double x, double y, double z) {
return (getNoiseUnmapped(seed, x, y, z) - 1.5) * 2;
}
public double getNoiseUnmapped(int seed, double x, double y, double z) {
long base = ((randomBits(seed, x, y, z)) & 0x000fffffffffffffL) | POSITIVE_POW1; // Sign and exponent
return Double.longBitsToDouble(base);
}
public double getNoiseUnmapped(int seed, double x, double y) {
long base = (randomBits(seed, x, y) & 0x000fffffffffffffL) | POSITIVE_POW1; // Sign and exponent
return Double.longBitsToDouble(base);
}
public long randomBits(int seed, double x, double y, double z) {
long hashX = Double.doubleToRawLongBits(x) ^ seed;
long hashZ = Double.doubleToRawLongBits(y) ^ seed;
long hash = (((hashX ^ (hashX >>> 32)) + ((hashZ ^ (hashZ >>> 32)) << 32)) ^ seed) + Double.doubleToRawLongBits(z);
long base = ((murmur64(hash)) & 0x000fffffffffffffL)
| POSITIVE_POW1; // Sign and exponent
return (Double.longBitsToDouble(base) - 1.5) * 2;
return murmur64(hash);
}
/**
* Murmur64 hashing function
*
* @param h Input value
* @return Hashed value
*/
private static long murmur64(long h) {
h ^= h >>> 33;
h *= 0xff51afd7ed558ccdL;
h ^= h >>> 33;
h *= 0xc4ceb9fe1a85ec53L;
h ^= h >>> 33;
return h;
public long randomBits(int seed, double x, double y) {
long hashX = Double.doubleToRawLongBits(x) ^ seed;
long hashZ = Double.doubleToRawLongBits(y) ^ seed;
long hash = ((hashX ^ (hashX >>> 32)) + ((hashZ ^ (hashZ >>> 32)) << 32)) ^ seed;
return murmur64(hash);
}
}
@@ -0,0 +1,243 @@
package com.dfsek.terra.api.math.noise.samplers.noise.simplex;
public class SimplexSampler extends SimplexStyleSampler {
private static final Double2[] GRAD_2D = {
new Double2(-1, -1), new Double2(1, -1), new Double2(-1, 1), new Double2(1, 1),
new Double2(0, -1), new Double2(-1, 0), new Double2(0, 1), new Double2(1, 0),
};
private static final Double3[] GRAD_3D = {
new Double3(1, 1, 0), new Double3(-1, 1, 0), new Double3(1, -1, 0), new Double3(-1, -1, 0),
new Double3(1, 0, 1), new Double3(-1, 0, 1), new Double3(1, 0, -1), new Double3(-1, 0, -1),
new Double3(0, 1, 1), new Double3(0, -1, 1), new Double3(0, 1, -1), new Double3(0, -1, -1),
new Double3(1, 1, 0), new Double3(0, -1, 1), new Double3(-1, 1, 0), new Double3(0, -1, -1),
};
private static final double F2 = 1.0 / 2.0;
private static final double F3 = (1.0 / 3.0);
private static final double G2 = 1.0 / 4.0;
private static final double G3 = (1.0 / 6.0);
private static final double G33 = G3 * 3 - 1;
private static final int X_PRIME = 1619;
private static final int Y_PRIME = 31337;
private static final int Z_PRIME = 6971;
public SimplexSampler(int seed) {
super(seed);
}
private static double gradCoord3D(int seed, int x, int y, int z, double xd, double yd, double zd) {
int hash = seed;
hash ^= X_PRIME * x;
hash ^= Y_PRIME * y;
hash ^= Z_PRIME * z;
hash = hash * hash * hash * 60493;
hash = (hash >> 13) ^ hash;
Double3 g = GRAD_3D[hash & 15];
return xd * g.x + yd * g.y + zd * g.z;
}
private static double gradCoord2D(int seed, int x, int y, double xd, double yd) {
int hash = seed;
hash ^= X_PRIME * x;
hash ^= Y_PRIME * y;
hash = hash * hash * hash * 60493;
hash = (hash >> 13) ^ hash;
Double2 g = GRAD_2D[hash & 7];
return xd * g.x + yd * g.y;
}
@Override
public double getNoiseRaw(int seed, double x, double y) {
double t = (x + y) * F2;
int i = fastFloor(x + t);
int j = fastFloor(y + t);
t = (i + j) * G2;
double X0 = i - t;
double Y0 = j - t;
double x0 = x - X0;
double y0 = y - Y0;
int i1, j1;
if(x0 > y0) {
i1 = 1;
j1 = 0;
} else {
i1 = 0;
j1 = 1;
}
double x1 = x0 - i1 + G2;
double y1 = y0 - j1 + G2;
double x2 = x0 - 1 + F2;
double y2 = y0 - 1 + F2;
double n0, n1, n2;
t = 0.5 - x0 * x0 - y0 * y0;
if(t < 0) {
n0 = 0;
} else {
t *= t;
n0 = t * t * gradCoord2D(seed, i, j, x0, y0);
}
t = 0.5 - x1 * x1 - y1 * y1;
if(t < 0) {
n1 = 0;
} else {
t *= t;
n1 = t * t * gradCoord2D(seed, i + i1, j + j1, x1, y1);
}
t = 0.5 - x2 * x2 - y2 * y2;
if(t < 0) {
n2 = 0;
} else {
t *= t;
n2 = t * t * gradCoord2D(seed, i + 1, j + 1, x2, y2);
}
return 50 * (n0 + n1 + n2);
}
@Override
public double getNoiseRaw(int seed, double x, double y, double z) {
double t = (x + y + z) * F3;
int i = fastFloor(x + t);
int j = fastFloor(y + t);
int k = fastFloor(z + t);
t = (i + j + k) * G3;
double x0 = x - (i - t);
double y0 = y - (j - t);
double z0 = z - (k - t);
int i1, j1, k1;
int i2, j2, k2;
if(x0 >= y0) {
if(y0 >= z0) {
i1 = 1;
j1 = 0;
k1 = 0;
i2 = 1;
j2 = 1;
k2 = 0;
} else if(x0 >= z0) {
i1 = 1;
j1 = 0;
k1 = 0;
i2 = 1;
j2 = 0;
k2 = 1;
} else // x0 < z0
{
i1 = 0;
j1 = 0;
k1 = 1;
i2 = 1;
j2 = 0;
k2 = 1;
}
} else // x0 < y0
{
if(y0 < z0) {
i1 = 0;
j1 = 0;
k1 = 1;
i2 = 0;
j2 = 1;
k2 = 1;
} else if(x0 < z0) {
i1 = 0;
j1 = 1;
k1 = 0;
i2 = 0;
j2 = 1;
k2 = 1;
} else // x0 >= z0
{
i1 = 0;
j1 = 1;
k1 = 0;
i2 = 1;
j2 = 1;
k2 = 0;
}
}
double x1 = x0 - i1 + G3;
double y1 = y0 - j1 + G3;
double z1 = z0 - k1 + G3;
double x2 = x0 - i2 + F3;
double y2 = y0 - j2 + F3;
double z2 = z0 - k2 + F3;
double x3 = x0 + G33;
double y3 = y0 + G33;
double z3 = z0 + G33;
double n0, n1, n2, n3;
t = 0.6 - x0 * x0 - y0 * y0 - z0 * z0;
if(t < 0) n0 = 0;
else {
t *= t;
n0 = t * t * gradCoord3D(seed, i, j, k, x0, y0, z0);
}
t = 0.6 - x1 * x1 - y1 * y1 - z1 * z1;
if(t < 0) {
n1 = 0;
} else {
t *= t;
n1 = t * t * gradCoord3D(seed, i + i1, j + j1, k + k1, x1, y1, z1);
}
t = 0.6 - x2 * x2 - y2 * y2 - z2 * z2;
if(t < 0) {
n2 = 0;
} else {
t *= t;
n2 = t * t * gradCoord3D(seed, i + i2, j + j2, k + k2, x2, y2, z2);
}
t = 0.6 - x3 * x3 - y3 * y3 - z3 * z3;
if(t < 0) {
n3 = 0;
} else {
t *= t;
n3 = t * t * gradCoord3D(seed, i + 1, j + 1, k + 1, x3, y3, z3);
}
return 32 * (n0 + n1 + n2 + n3);
}
private static class Double2 {
public final double x, y;
public Double2(double x, double y) {
this.x = x;
this.y = y;
}
}
private static class Double3 {
public final double x, y, z;
public Double3(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
}
}
@@ -1,7 +1,7 @@
package com.dfsek.terra.api.math.paralithic.noise;
import com.dfsek.terra.api.math.noise.NoiseSampler;
import com.dfsek.terra.util.hash.HashMapDoubleDouble;
import com.dfsek.terra.api.util.hash.HashMapDoubleDouble;
@@ -170,4 +170,9 @@ public class Location implements Cloneable {
public String toString() {
return "[" + world + ": (" + getX() + ", " + getY() + ", " + getZ() + ")]";
}
public Location multiply(double v) {
vector.multiply(v);
return this;
}
}
@@ -1,6 +0,0 @@
package com.dfsek.terra.api.platform;
import com.dfsek.terra.api.platform.entity.Entity;
public interface Player extends Entity {
}
@@ -11,7 +11,9 @@ public interface Block extends Handle {
BlockState getState();
Block getRelative(BlockFace face);
default Block getRelative(BlockFace face) {
return getRelative(face, 1);
}
Block getRelative(BlockFace face, int len);
@@ -19,7 +21,9 @@ public interface Block extends Handle {
Location getLocation();
MaterialData getType();
default BlockType getType() {
return getBlockData().getBlockType();
}
int getX();
@@ -3,11 +3,16 @@ package com.dfsek.terra.api.platform.block;
import com.dfsek.terra.api.platform.Handle;
public interface BlockData extends Cloneable, Handle {
MaterialData getMaterial();
boolean matches(MaterialData materialData);
BlockType getBlockType();
boolean matches(BlockData other);
BlockData clone();
String getAsString();
boolean isAir();
boolean isStructureVoid();
}
@@ -0,0 +1,11 @@
package com.dfsek.terra.api.platform.block;
import com.dfsek.terra.api.platform.Handle;
public interface BlockType extends Handle {
BlockData getDefaultData();
boolean isSolid();
boolean isWater();
}
@@ -1,17 +0,0 @@
package com.dfsek.terra.api.platform.block;
import com.dfsek.terra.api.platform.Handle;
public interface MaterialData extends Handle {
boolean matches(MaterialData other);
boolean matches(BlockData other);
boolean isSolid();
boolean isAir();
double getMaxDurability();
BlockData createBlockData();
}
@@ -3,7 +3,12 @@ package com.dfsek.terra.api.platform.entity;
import com.dfsek.terra.api.math.vector.Location;
import com.dfsek.terra.api.platform.CommandSender;
import com.dfsek.terra.api.platform.Handle;
import com.dfsek.terra.api.platform.world.World;
public interface Entity extends Handle, CommandSender {
Location getLocation();
void setLocation(Location location);
World getWorld();
}
@@ -0,0 +1,4 @@
package com.dfsek.terra.api.platform.entity;
public interface Player extends Entity {
}
@@ -1,13 +1,13 @@
package com.dfsek.terra.api.platform.handle;
import com.dfsek.terra.api.platform.block.MaterialData;
import com.dfsek.terra.api.platform.inventory.ItemStack;
import com.dfsek.terra.api.platform.inventory.Item;
import com.dfsek.terra.api.platform.inventory.item.Enchantment;
import java.util.Set;
public interface ItemHandle {
ItemStack newItemStack(MaterialData material, int amount);
Item createItem(String data);
Enchantment getEnchantment(String id);
@@ -1,23 +1,26 @@
package com.dfsek.terra.api.platform.handle;
import com.dfsek.terra.api.platform.block.Block;
import com.dfsek.terra.api.math.vector.Location;
import com.dfsek.terra.api.platform.block.BlockData;
import com.dfsek.terra.api.platform.block.MaterialData;
import com.dfsek.terra.api.platform.entity.EntityType;
import com.dfsek.terra.api.platform.entity.Player;
import com.dfsek.terra.api.util.generic.pair.Pair;
/**
* Interface to be implemented for world manipulation.
*/
public interface WorldHandle {
void setBlockData(Block block, BlockData data, boolean physics);
BlockData getBlockData(Block block);
MaterialData getType(Block block);
BlockData createBlockData(String data);
MaterialData createMaterialData(String data);
EntityType getEntity(String id);
/**
* Get the locations selected by a player. (Usually via WorldEdit)
*
* @param player Player to get locations for
* @return Pair of locations.
*/
default Pair<Location, Location> getSelectedLocation(Player player) {
throw new UnsupportedOperationException("Cannot get selection on this platform.");
}
}
@@ -0,0 +1,12 @@
package com.dfsek.terra.api.platform.inventory;
import com.dfsek.terra.api.platform.Handle;
/**
* An inventory item.
*/
public interface Item extends Handle {
ItemStack newItemStack(int amount);
double getMaxDurability();
}
@@ -1,19 +1,21 @@
package com.dfsek.terra.api.platform.inventory;
import com.dfsek.terra.api.platform.Handle;
import com.dfsek.terra.api.platform.block.MaterialData;
import com.dfsek.terra.api.platform.inventory.item.Damageable;
import com.dfsek.terra.api.platform.inventory.item.ItemMeta;
public interface ItemStack extends Handle, Cloneable {
public interface ItemStack extends Handle {
int getAmount();
void setAmount(int i);
MaterialData getType();
ItemStack clone();
Item getType();
ItemMeta getItemMeta();
void setItemMeta(ItemMeta meta);
default boolean isDamageable() {
return getItemMeta() instanceof Damageable;
}
}
@@ -1,4 +1,6 @@
/**
* API for platform implementations. Mostly interfaces to be implemented by platform delegates.
* <p>
* Interfaces in this package generally should <b>not</b> be implemented by addons.
*/
package com.dfsek.terra.api.platform;
@@ -0,0 +1,13 @@
package com.dfsek.terra.api.platform.world;
import com.dfsek.terra.api.math.vector.Location;
import com.dfsek.terra.api.util.collections.MaterialSet;
import java.util.Random;
public interface Tree {
boolean plant(Location l, Random r);
MaterialSet getSpawnable();
}
@@ -6,6 +6,8 @@ import com.dfsek.terra.api.platform.block.Block;
import com.dfsek.terra.api.platform.entity.Entity;
import com.dfsek.terra.api.platform.entity.EntityType;
import com.dfsek.terra.api.platform.world.generator.ChunkGenerator;
import com.dfsek.terra.api.platform.world.generator.GeneratorWrapper;
import com.dfsek.terra.api.world.generation.TerraChunkGenerator;
import java.io.File;
import java.util.UUID;
@@ -17,19 +19,27 @@ public interface World extends Handle {
ChunkGenerator getGenerator();
String getName();
UUID getUID();
boolean isChunkGenerated(int x, int z);
Chunk getChunkAt(int x, int z);
File getWorldFolder();
default Chunk getChunkAt(Location location) {
return getChunkAt(location.getBlockX() >> 4, location.getBlockZ() >> 4);
}
Block getBlockAt(int x, int y, int z);
Block getBlockAt(Location l);
default Block getBlockAt(Location l) {
return getBlockAt(l.getBlockX(), l.getBlockY(), l.getBlockZ());
}
Entity spawnEntity(Location location, EntityType entityType);
int getMinHeight();
default boolean isTerraWorld() {
return getGenerator().getHandle() instanceof GeneratorWrapper;
}
default TerraChunkGenerator getTerraGenerator() {
return ((GeneratorWrapper) getGenerator().getHandle()).getHandle();
}
}
@@ -0,0 +1,14 @@
package com.dfsek.terra.api.platform.world.generator;
import com.dfsek.terra.api.platform.world.ChunkAccess;
public interface ChunkData extends ChunkAccess {
/**
* Get the maximum height for the chunk.
* <p>
* Setting blocks at or above this height will do nothing.
*
* @return the maximum height
*/
int getMaxHeight();
}
@@ -1,43 +1,7 @@
package com.dfsek.terra.api.platform.world.generator;
import com.dfsek.terra.api.platform.Handle;
import com.dfsek.terra.api.platform.world.BiomeGrid;
import com.dfsek.terra.api.platform.world.ChunkAccess;
import com.dfsek.terra.api.platform.world.World;
import com.dfsek.terra.api.world.generation.TerraChunkGenerator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Random;
public interface ChunkGenerator extends Handle {
boolean isParallelCapable();
boolean shouldGenerateCaves();
boolean shouldGenerateDecorations();
boolean shouldGenerateMobs();
boolean shouldGenerateStructures();
ChunkData generateChunkData(@NotNull World world, @NotNull Random random, int x, int z, @NotNull BiomeGrid biome);
List<BlockPopulator> getDefaultPopulators(World world);
@Nullable
TerraChunkGenerator getTerraGenerator();
interface ChunkData extends ChunkAccess {
/**
* Get the maximum height for the chunk.
* <p>
* Setting blocks at or above this height will do nothing.
*
* @return the maximum height
*/
int getMaxHeight();
}
}
@@ -1,6 +1,9 @@
package com.dfsek.terra.api.platform.world.generator;
import com.dfsek.terra.api.platform.Handle;
import com.dfsek.terra.api.world.generation.TerraChunkGenerator;
public interface GeneratorWrapper extends Handle {
@Override
TerraChunkGenerator getHandle();
}
@@ -0,0 +1,84 @@
package com.dfsek.terra.api.registry;
import com.dfsek.tectonic.exception.LoadException;
import com.dfsek.tectonic.loading.ConfigLoader;
import com.dfsek.terra.registry.OpenRegistry;
import com.dfsek.terra.registry.exception.DuplicateEntryException;
import java.lang.reflect.Type;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
/**
* Wrapper for a registry that ensures checked additions.
*
* @param <T> Type in registry
*/
public class CheckedRegistry<T> implements Registry<T> {
private final OpenRegistry<T> registry;
public CheckedRegistry(OpenRegistry<T> registry) {
this.registry = registry;
}
/**
* Add a value to this registry, checking whether it is present first.
*
* @param identifier Identifier to assign value.
* @param value Value to add.
* @throws DuplicateEntryException If an entry with the same identifier is already present.
*/
public void add(String identifier, T value) throws DuplicateEntryException {
registry.addChecked(identifier, value);
}
/**
* Add a value to the registry, without checking presence beforehand.
* <p>
* Use of this method is generally discouraged, as it is bad practice to overwrite registry values.
*
* @param identifier Identifier to assign value.
* @param value Value to add.
* @deprecated Use of {@link #add(String, Object)} is encouraged.
*/
@Deprecated
public void addUnchecked(String identifier, T value) {
registry.add(identifier, value);
}
@Override
public T get(String identifier) {
return registry.get(identifier);
}
@Override
public boolean contains(String identifier) {
return registry.contains(identifier);
}
@Override
public void forEach(Consumer<T> consumer) {
registry.forEach(consumer);
}
@Override
public void forEach(BiConsumer<String, T> consumer) {
registry.forEach(consumer);
}
@Override
public Set<T> entries() {
return registry.entries();
}
@Override
public Set<String> keys() {
return registry.keys();
}
@Override
public T load(Type t, Object c, ConfigLoader loader) throws LoadException {
return registry.load(t, c, loader);
}
}
@@ -0,0 +1,57 @@
package com.dfsek.terra.api.registry;
import com.dfsek.tectonic.exception.LoadException;
import com.dfsek.tectonic.loading.ConfigLoader;
import java.lang.reflect.Type;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
/**
* Wrapper for a registry that forbids all write access.
*
* @param <T> Type in registry
*/
public class LockedRegistry<T> implements Registry<T> {
private final Registry<T> registry;
public LockedRegistry(Registry<T> registry) {
this.registry = registry;
}
@Override
public T get(String identifier) {
return registry.get(identifier);
}
@Override
public boolean contains(String identifier) {
return registry.contains(identifier);
}
@Override
public void forEach(Consumer<T> consumer) {
registry.forEach(consumer);
}
@Override
public void forEach(BiConsumer<String, T> consumer) {
registry.forEach(consumer);
}
@Override
public Set<T> entries() {
return registry.entries();
}
@Override
public Set<String> keys() {
return registry.keys();
}
@Override
public T load(Type t, Object c, ConfigLoader loader) throws LoadException {
return registry.load(t, c, loader);
}
}

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