Compare commits

..

98 Commits

Author SHA1 Message Date
3cdcb411f7
Ooprs!!1 2025-04-03 11:09:13 +07:00
174a86b33c
Fix tritanium ellipsoid being biased 2025-04-01 16:06:20 +07:00
f451ea4df4
Merge branch '1.21' into worldgen-placement-providers 2025-03-31 20:58:47 +07:00
74d48a7dc3
Update references after merge 2025-03-31 13:10:12 +07:00
5b463e8adb
Merge branch '1.21' into worldgen-placement-providers
# Conflicts:
#	src/data/kotlin/ru/dbotthepony/mc/otm/datagen/WorldGen.kt
#	src/main/kotlin/ru/dbotthepony/mc/otm/OverdriveThatMatters.kt
#	src/main/kotlin/ru/dbotthepony/mc/otm/registry/data/MWorldGenFeatures.kt
#	src/main/kotlin/ru/dbotthepony/mc/otm/server/world/EnhancedPlacedFeature.kt
#	src/main/kotlin/ru/dbotthepony/mc/otm/server/world/EnhancedPlacementContext.kt
#	src/main/kotlin/ru/dbotthepony/mc/otm/server/world/Ext.kt
#	src/main/kotlin/ru/dbotthepony/mc/otm/server/world/PlacementVariable.kt
#	src/main/kotlin/ru/dbotthepony/mc/otm/server/world/PlacementVariableMap.kt
#	src/main/kotlin/ru/dbotthepony/mc/otm/server/world/feature/DebugPlacerFeature.kt
#	src/main/kotlin/ru/dbotthepony/mc/otm/server/world/feature/EnhancedFeature.kt
#	src/main/kotlin/ru/dbotthepony/mc/otm/server/world/placement/ChainPlacement.kt
#	src/main/kotlin/ru/dbotthepony/mc/otm/server/world/placement/EllipsoidPlacement.kt
#	src/main/kotlin/ru/dbotthepony/mc/otm/server/world/placement/EnhancedChainPlacement.kt
#	src/main/kotlin/ru/dbotthepony/mc/otm/server/world/placement/EnhancedCountPlacement.kt
#	src/main/kotlin/ru/dbotthepony/mc/otm/server/world/placement/EnhancedPlacement.kt
#	src/main/kotlin/ru/dbotthepony/mc/otm/server/world/placement/EnhancedSplitPlacement.kt
#	src/main/kotlin/ru/dbotthepony/mc/otm/server/world/placement/SplitPlacement.kt
#	src/main/kotlin/ru/dbotthepony/mc/otm/server/world/placement/StandardDeviationHeightProvider.kt
#	src/main/kotlin/ru/dbotthepony/mc/otm/server/world/placement/WormPlacement.kt
2025-03-31 13:02:27 +07:00
0743ea2c9a
Update method references after merge 2025-03-31 08:17:31 +07:00
347a5465cc
Move vec3i hash strategy to util.collect 2025-03-31 08:15:38 +07:00
526578f76c
Merge branch '1.21' into worldgen-placement-providers 2025-03-31 08:14:57 +07:00
ac1c9a1ad4
Add ellipsoid placement rotation 2025-03-30 15:13:56 +07:00
9cfbab6c3c
Mark BooleanProvider impls as data classes so they can be compared 2025-03-29 22:06:15 +07:00
07e6369454
Merge branch '1.21' into worldgen-placement-providers 2025-03-29 22:05:15 +07:00
ddab73bad1
Merge branch '1.21' into worldgen-placement-providers 2025-03-29 18:20:37 +07:00
a03c2d5eb4
Remove BlockPosSet since it is not used, twice as slow as regular tree sets and practically dont give much memory savings 2025-03-26 16:00:41 +07:00
7ca0ba1ce5
TreeSet is faster than ObjectRBTreeSet for some reason 2025-03-26 15:58:47 +07:00
7a5a979169
Improve enhanced placed feature performance 2025-03-26 13:15:26 +07:00
e2369b3a24
Unify placement modifier and feature under single "placement" interface, greatly simplifying and empowering things at the same time 2025-03-26 11:03:48 +07:00
2e104dcbce
Create new variable map for each evaluation so leaks wont have lasting effects 2025-03-25 20:38:11 +07:00
159125fb4b
Per-block variable storage in enhanced placement 2025-03-25 18:59:53 +07:00
727111cf4a
Fix blockposset subset forgetting blocks at negative y values 2025-03-25 14:13:19 +07:00
d08a928e04
a 2025-03-25 13:22:29 +07:00
398bd532f4
Remove "inefficinet" comment since placements are now efficiently filtered 2025-03-25 12:26:43 +07:00
7e570747c2
Bring back bigger caches since memory usage now is normal again 2025-03-25 12:25:53 +07:00
d683ea1e38
More efficient ellipsoid placement 2025-03-25 12:16:25 +07:00
f74dbbd84a
Fix level being wrong when placing features in-world 2025-03-25 12:10:28 +07:00
2993ae61ca
man. 2025-03-25 11:52:59 +07:00
9577e205e7
Set last returned index to -1 when removing 2025-03-25 11:52:01 +07:00
ba83b89476
Ooprs!!1 2025-03-25 11:11:23 +07:00
bf6fec7753
Create shared state in EnhancedPlacementContext for memory efficiency 2025-03-25 11:09:47 +07:00
0af5fb7301
Use LinkedList in generated chunk for memory efficiency 2025-03-25 11:09:22 +07:00
a4e40bd464
Provide BlockPosSet for vastly improved memory efficiency 2025-03-25 11:03:35 +07:00
07b295ce45
Add enhanced variants for placement modifiers 2025-03-25 00:07:55 +07:00
ba492e0cee
Merge branch '1.21' into worldgen-placement-providers 2025-03-24 23:34:18 +07:00
dca02893a4
Make generation cache expire much quicker 2025-03-24 21:11:59 +07:00
ab6e3ad87f
Use sets only for final placements, use lists for intermediate 2025-03-24 20:16:57 +07:00
78c0a5d717
Fix platform declaration clash 2025-03-24 19:32:18 +07:00
e150b57b65
Add passthrough placement modifier, and add argument-less placed feature builder 2025-03-24 19:30:17 +07:00
1e6e38ea7d
Remove enormous placement 2025-03-24 19:27:36 +07:00
1acd105925
Move tritanium worms to enhanced placed feature 2025-03-24 19:27:03 +07:00
5d05fe3bb4
Use tree set 2025-03-24 19:22:35 +07:00
bc81103e38
Use sets for block positions instead of lists
it makes no sense to give same position twice
2025-03-24 19:07:25 +07:00
18f9bc2654
Enhanced placements prototyping 2025-03-24 18:42:52 +07:00
03a1ab9197
Merge branch '1.21' into worldgen-placement-providers 2025-03-24 18:15:59 +07:00
055ca7ec43
Merge branch '1.21' into worldgen-placement-providers 2025-03-24 18:15:05 +07:00
ca67e796da
Update boolean provider to use MRegistries 2025-03-24 14:01:32 +07:00
9ee561d0fd
Merge branch '1.21' into worldgen-placement-providers 2025-03-24 13:58:58 +07:00
e399fa8b68
Mark Boolean Provider register as internal 2025-03-23 12:34:05 +07:00
d55fbbe62f
Merge branch '1.21' into worldgen-placement-providers 2025-03-23 10:23:05 +07:00
b6b3cbed81
Merge branch '1.21' into worldgen-placement-providers 2025-03-22 00:38:45 +07:00
97c4922765
a 2025-03-11 18:26:50 +07:00
d2e7971726
Merge branch '1.21' into worldgen-placement-providers 2025-03-11 18:01:55 +07:00
e7f2e0551a
Merge branch '1.21' into worldgen-placement-providers 2025-03-09 22:01:00 +07:00
84ca0c2a24
Use GJRAND64 for enormous placement 2025-03-09 21:47:17 +07:00
326490b73c
Merge branch '1.21' into worldgen-placement-providers 2025-03-09 21:29:52 +07:00
ed04a8c4d2
Drastically reduce tritanium clusters generated 2025-03-09 15:39:20 +07:00
611f4055e9
Ellipsoid heart with worms tritanium placement 2025-03-09 15:38:20 +07:00
00ff8fa91e
damn 2025-03-09 15:01:23 +07:00
3ee539bc9d
Back to debug placement 2025-03-09 13:57:31 +07:00
44b0bcd776
Use nested caches for enormous placements instead of weak hash map
Should recycle memory better
2025-03-09 13:34:47 +07:00
cc456958e1
Remove duplicates from positions provided by enormous placement 2025-03-09 13:31:42 +07:00
5f76aa1661
AlwaysTrue and AlwaysFalse boolean providers 2025-03-09 13:06:46 +07:00
bd8c5ed97b
Clarify docs 2025-03-09 13:04:31 +07:00
674c875249
Make enormous placement a wrapper around regular placement modifiers, simplifying code 2025-03-09 12:59:36 +07:00
7736762c86
Some code for allowing multiple worms 2025-03-09 12:49:53 +07:00
274b5c059a
Fix AbstractEnormousPlacement doesnt separate caches for different dimensions 2025-03-09 12:42:28 +07:00
45d7b30b8a
As usual 2025-03-09 00:07:28 +07:00
abeeda8139
Reduce maximum amount of dilithium in vein, make tritanium worm vein rarer 2025-03-08 23:55:26 +07:00
e179756995
Use proper features to place enormous ores 2025-03-08 23:47:49 +07:00
df949a6861
More or less actual tritanium worm placement configuration 2025-03-08 23:32:59 +07:00
88c3fe24b0
Post placement modifiers for enormous placements 2025-03-08 22:51:43 +07:00
25a312b56f
Separate turn rate for each axis, smooth turning with specifable turn speed 2025-03-08 21:59:29 +07:00
be97560f01
Merge branch '1.21' into worldgen-placement-providers 2025-03-08 19:24:19 +07:00
d06c32aec2
Use PCG as random source in Enormous Placement 2025-03-08 17:40:35 +07:00
3ea9a7f386
Merge branch '1.21' into worldgen-placement-providers 2025-03-08 17:35:01 +07:00
8b0edc5d5f
Cope-posty 2025-03-08 14:28:51 +07:00
19e405d4f4
Use RandomGenerator methods when available 2025-03-08 14:18:11 +07:00
e1d3b36dab
Merge branch '1.21' into worldgen-placement-providers 2025-03-08 14:12:44 +07:00
157d2c5498
Merge branch '1.21' into worldgen-placement-providers 2025-03-08 14:12:12 +07:00
01054a05d2
Add BooleanProvider.BiasedLinear 2025-03-08 11:06:35 +07:00
a89371007f
Declare BooleanProvider as interface 2025-03-07 23:36:56 +07:00
70ac0f2c7f
No longer require seedMix to be non-zero 2025-03-07 21:39:47 +07:00
0ef34a00cc
Merge branch '1.21' into worldgen-placement-providers 2025-03-07 21:31:07 +07:00
cbc95e8e5b
Merge branch '1.21' into worldgen-placement-providers 2025-03-05 10:52:04 +07:00
0c15c93dcd
Disallow to specify seed mix as zero to avoid EXTREMELY unlikely case where world's seed is zero, and we are generating chunk at 0, 0, in which Xoshiro256 will generate an exception 2025-03-05 10:25:17 +07:00
92846cbad0
Merge branch '1.21' into worldgen-placement-providers 2025-03-05 09:46:36 +07:00
ab42d1c1fb
Use Xoshiro256SSRandom instead of CMWC in enormous placements 2025-03-05 09:45:22 +07:00
b8468f7828
Merge branch 'prng' into worldgen-placement-providers 2025-03-05 09:44:23 +07:00
01a6ca27bf
Merge branch '1.21' into worldgen-placement-providers 2025-03-05 00:45:13 +07:00
db3f22f3fc
Merge branch '1.21' into worldgen-placement-providers 2025-03-04 18:51:27 +07:00
4da5ef81f7
Merge branch '1.21' into worldgen-placement-providers 2025-03-03 14:44:28 +07:00
536f69930c
Use CMWC random for enormous placements 2025-03-03 12:12:15 +07:00
988f078d24
Merge branch '1.21' into worldgen-placement-providers 2025-03-03 12:11:23 +07:00
f6abe0f038
Initial worm placement code along with debug feature 2025-03-03 06:40:29 +07:00
97d30d3a19
Add seed mix to enormous placements 2025-03-02 11:36:35 +07:00
cc9ebef018
Remove chunk cache tweaking from datapack 2025-03-02 11:29:08 +07:00
f9c8130e26
Merge branch '1.21' into worldgen-placement-providers 2025-03-02 11:25:40 +07:00
c0812687cb
OneOf number provider, improve dilithium ore generation further 2025-03-02 11:21:30 +07:00
cd38e1a154
Adjust dilithium placement parameters 2025-03-02 09:59:20 +07:00
a903b9cff8
Enormous placement modifier, greatly enhance dilithium ore placement 2025-03-02 09:56:58 +07:00
119 changed files with 1790 additions and 1430 deletions

View File

@ -209,9 +209,6 @@ dependencies {
// runtimeOnly("curse.maven:integrated-tunnels-251389:4344632")
implementation("mekanism:Mekanism:${mc_version}-${mekanism_version}")
implementation("curse.maven:iron-chests-228756:5491156")
implementation("curse.maven:iron-shulker-boxes-314911:5491246")
}
}
@ -271,8 +268,6 @@ minecraft {
mixin {
config("$mod_id.mixins.json")
config("$mod_id.ironchest.mixins.json")
config("$mod_id.ironshulkerbox.mixins.json")
// config("$mod_id.ad_astra.mixins.json")
}

View File

@ -57,6 +57,7 @@ import ru.dbotthepony.mc.otm.datagen.tags.addStructureTags
import ru.dbotthepony.mc.otm.datagen.tags.addSuspiciousTags
import ru.dbotthepony.mc.otm.datagen.tags.addTags
import ru.dbotthepony.mc.otm.matter.MatterDataProvider
import ru.dbotthepony.mc.otm.registry.MRegistries
import ru.dbotthepony.mc.otm.registry.game.MBlocks
import ru.dbotthepony.mc.otm.registry.objects.ColoredDecorativeBlock
import ru.dbotthepony.mc.otm.registry.objects.DecorativeBlock
@ -560,6 +561,7 @@ object DataGen {
val registrySetBuilder = RegistrySetBuilder()
.add(Registries.DAMAGE_TYPE, ::registerDamageTypes)
.add(Registries.CONFIGURED_FEATURE, ::registerConfiguredFeatures)
.add(MRegistries.CONFIGURED_FEATURE, ::registerEnhancedConfiguredFeatures)
.add(Registries.PLACED_FEATURE, ::registerPlacedFeatures)
.add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, ::registerBiomeModifiers)

View File

@ -7,14 +7,15 @@ import net.minecraft.resources.ResourceKey
import net.minecraft.tags.BiomeTags
import net.minecraft.tags.BlockTags
import net.minecraft.util.valueproviders.ClampedNormalFloat
import net.minecraft.util.valueproviders.ClampedNormalInt
import net.minecraft.util.valueproviders.ConstantFloat
import net.minecraft.util.valueproviders.UniformFloat
import net.minecraft.util.valueproviders.UniformInt
import net.minecraft.world.level.levelgen.GenerationStep
import net.minecraft.world.level.levelgen.VerticalAnchor
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature
import net.minecraft.world.level.levelgen.feature.Feature
import net.minecraft.world.level.levelgen.feature.configurations.OreConfiguration
import net.minecraft.world.level.levelgen.feature.configurations.ReplaceBlockConfiguration
import net.minecraft.world.level.levelgen.heightproviders.VeryBiasedToBottomHeight
import net.minecraft.world.level.levelgen.placement.CountPlacement
import net.minecraft.world.level.levelgen.placement.HeightRangePlacement
@ -25,20 +26,40 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.TagMatchTest
import net.neoforged.neoforge.common.world.BiomeModifier
import net.neoforged.neoforge.registries.NeoForgeRegistries
import ru.dbotthepony.mc.otm.util.math.Decimal
import ru.dbotthepony.mc.otm.data.world.EllipsoidPlacement
import ru.dbotthepony.mc.otm.data.world.StandardDeviationHeightProvider
import ru.dbotthepony.mc.otm.data.world.BooleanProvider
import ru.dbotthepony.mc.otm.data.world.OneOfFloatProvider
import ru.dbotthepony.mc.otm.registry.MRegistries
import ru.dbotthepony.mc.otm.server.world.placement.StandardDeviationHeightProvider
import ru.dbotthepony.mc.otm.registry.game.MBlocks
import ru.dbotthepony.mc.otm.registry.data.MWorldGenFeatures
import ru.dbotthepony.mc.otm.server.world.EnhancedPlacedFeature
import ru.dbotthepony.mc.otm.server.world.feature.BlackHolePlacerFeature
import ru.dbotthepony.mc.otm.server.world.feature.EnhancedFeature
import ru.dbotthepony.mc.otm.server.world.placement
import ru.dbotthepony.mc.otm.server.world.placement.EllipsoidPlacement
import ru.dbotthepony.mc.otm.server.world.placement.EnhancedChainPlacement
import ru.dbotthepony.mc.otm.server.world.placement.EnhancedCountPlacement
import ru.dbotthepony.mc.otm.server.world.placement.EnhancedPlacement
import ru.dbotthepony.mc.otm.server.world.placement.EnhancedSplitPlacement
import ru.dbotthepony.mc.otm.server.world.placement.WormPlacement
import ru.dbotthepony.mc.otm.server.world.wrap
private object ConfiguredFeatures {
val TRITANIUM_ORE = key("tritanium_ore")
val TRITANIUM_ORE_SMALL = key("tritanium_ore_small")
val DILITHIUM = key("dilithium")
val BLACK_HOLE = key("black_hole")
private fun key(name: String): ResourceKey<ConfiguredFeature<*, *>> {
return ResourceKey.create(Registries.CONFIGURED_FEATURE, modLocation(name))
}
private fun ekey(name: String): ResourceKey<EnhancedFeature.Configured<*, *>> {
return ResourceKey.create(MRegistries.CONFIGURED_FEATURE, modLocation(name))
}
}
fun registerEnhancedConfiguredFeatures(context: BootstrapContext<EnhancedFeature.Configured<*, *>>) {
}
fun registerConfiguredFeatures(context: BootstrapContext<ConfiguredFeature<*, *>>) {
@ -52,6 +73,8 @@ fun registerConfiguredFeatures(context: BootstrapContext<ConfiguredFeature<*, *>
)
context.register(ConfiguredFeatures.TRITANIUM_ORE, ConfiguredFeature(Feature.ORE, OreConfiguration(target, 9)))
context.register(ConfiguredFeatures.TRITANIUM_ORE_SMALL, ConfiguredFeature(Feature.REPLACE_SINGLE_BLOCK, ReplaceBlockConfiguration(target)))
//context.register(ConfiguredFeatures.TRITANIUM_ORE_SMALL, ConfiguredFeature(MWorldGenFeatures.DEBUG_PLACEMENT, DebugPlacerFeature.Config(MBlocks.TRITANIUM_ORE.defaultBlockState())))
}
run {
@ -60,7 +83,7 @@ fun registerConfiguredFeatures(context: BootstrapContext<ConfiguredFeature<*, *>
OreConfiguration.target(deepslate, MBlocks.DEEPSLATE_DILITHIUM_ORE.defaultBlockState()),
)
context.register(ConfiguredFeatures.DILITHIUM, ConfiguredFeature(Feature.ORE, OreConfiguration(target, 3)))
context.register(ConfiguredFeatures.DILITHIUM, ConfiguredFeature(Feature.REPLACE_SINGLE_BLOCK, ReplaceBlockConfiguration(target)))
}
context.register(ConfiguredFeatures.BLACK_HOLE, ConfiguredFeature(
@ -71,7 +94,7 @@ fun registerConfiguredFeatures(context: BootstrapContext<ConfiguredFeature<*, *>
private object PlacedFeatures {
val NORMAL_TRITANIUM = key("normal_tritanium")
val DEEP_TRITANIUM = key("deep_tritanium")
val CLOUD_TITANIUM = key("cloud_tritanium")
val WORM_TRITANIUM = key("worm_tritanium")
val DILITHIUM = key("dilithium")
val BLACK_HOLE = key("black_hole")
@ -82,6 +105,12 @@ private object PlacedFeatures {
fun registerPlacedFeatures(context: BootstrapContext<PlacedFeature>) {
val configured = context.lookup(Registries.CONFIGURED_FEATURE)
val econfigured = context.lookup(MRegistries.CONFIGURED_FEATURE)
val ringularity = OneOfFloatProvider.of(
ClampedNormalFloat.of(0.4f, 0.2f, -2f, 2f),
ClampedNormalFloat.of(-0.4f, 0.2f, -2f, 2f),
)
run {
val ore = configured.getOrThrow(ConfiguredFeatures.TRITANIUM_ORE)
@ -89,7 +118,7 @@ fun registerPlacedFeatures(context: BootstrapContext<PlacedFeature>) {
context.register(PlacedFeatures.NORMAL_TRITANIUM, PlacedFeature(
ore,
listOf(
CountPlacement.of(UniformInt.of(2, 6)),
CountPlacement.of(UniformInt.of(1, 3)),
InSquarePlacement.spread(),
HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(10), 15.0))
)
@ -98,53 +127,93 @@ fun registerPlacedFeatures(context: BootstrapContext<PlacedFeature>) {
context.register(PlacedFeatures.DEEP_TRITANIUM, PlacedFeature(
ore,
listOf(
CountPlacement.of(UniformInt.of(4, 8)),
CountPlacement.of(UniformInt.of(3, 5)),
InSquarePlacement.spread(),
HeightRangePlacement.of(VeryBiasedToBottomHeight.of(VerticalAnchor.aboveBottom(4), VerticalAnchor.absolute(0), 16))
)
))
context.register(PlacedFeatures.CLOUD_TITANIUM, PlacedFeature(
ore,
listOf(
RarityFilter.onAverageOnceEvery(16),
InSquarePlacement.spread(),
HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(10), 15.0)),
EllipsoidPlacement(
x = ClampedNormalInt.of(0f, 6f, Int.MIN_VALUE, Int.MAX_VALUE),
y = ClampedNormalInt.of(0f, 12f, Int.MIN_VALUE, Int.MAX_VALUE),
z = ClampedNormalInt.of(0f, 6f, Int.MIN_VALUE, Int.MAX_VALUE),
count = ClampedNormalInt.of(60f, 60f, 40, 160),
xLength = ClampedNormalFloat.of(11f, 4f, 6f, 14f),
yLength = ClampedNormalFloat.of(11f, 4f, 6f, 14f),
zLength = ClampedNormalFloat.of(11f, 4f, 6f, 14f),
context.register(
PlacedFeatures.WORM_TRITANIUM,
EnhancedPlacedFeature.configure(
chunkScanRange = 24,
seedMix = 9284343575495L,
root = EnhancedChainPlacement(
RarityFilter.onAverageOnceEvery(140).wrap(),
InSquarePlacement.spread().wrap(),
HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(-40), 15.0)).wrap(),
EnhancedSplitPlacement(
EnhancedSplitPlacement.Mode.COMBINE,
// "heart"
EllipsoidPlacement(
count = UniformInt.of(600, 900),
xLength = UniformFloat.of(9f, 12f),
yLength = UniformFloat.of(9f, 12f),
zLength = UniformFloat.of(9f, 12f),
x = ringularity,
y = ringularity,
z = ringularity,
),
// "branches"
EnhancedChainPlacement(
EnhancedCountPlacement(UniformInt.of(2, 7)),
WormPlacement(
length = UniformInt.of(60, 120),
turnRateXY = WormPlacement.normalDistributedTurnRate(2f, 3f),
initialAngleXY = WormPlacement.normalDistributedTurnRate(3f, 4f),
turnRateXZ = WormPlacement.normalDistributedTurnRate(30f),
turnSpeedXZ = WormPlacement.constantTurnRate(10f),
turnSpeedXY = WormPlacement.constantTurnRate(4f),
turnChanceXY = BooleanProvider.Unbiased(6),
turnChanceXZ = BooleanProvider.BiasedLinear(0.6f, 5),
maxTravelUp = 90,
maxTravelDown = 10,
),
EllipsoidPlacement(
count = UniformInt.of(3, 6),
xLength = ConstantFloat.of(4f),
yLength = ConstantFloat.of(4f),
zLength = ConstantFloat.of(4f),
x = ringularity,
y = ringularity,
z = ringularity,
)
)
),
configured.getOrThrow(ConfiguredFeatures.TRITANIUM_ORE_SMALL).placement()
)
)
))
)
}
run {
val ore = configured.getOrThrow(ConfiguredFeatures.DILITHIUM)
context.register(PlacedFeatures.DILITHIUM, PlacedFeature(
ore,
listOf(
RarityFilter.onAverageOnceEvery(12),
InSquarePlacement.spread(),
HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(0), 15.0)),
EllipsoidPlacement(
x = ClampedNormalInt.of(0f, 8f, Int.MIN_VALUE, Int.MAX_VALUE),
y = ClampedNormalInt.of(0f, 20f, Int.MIN_VALUE, Int.MAX_VALUE),
z = ClampedNormalInt.of(0f, 8f, Int.MIN_VALUE, Int.MAX_VALUE),
count = ClampedNormalInt.of(200f, 200f, 200, 600),
xLength = ClampedNormalFloat.of(11f, 4f, 8f, 14f),
// allow crystals to generate as far as standard deviation allows
// to increase chance for player to discover crystal vein
yLength = ConstantFloat.of(60f),
zLength = ClampedNormalFloat.of(11f, 4f, 8f, 14f),
context.register(
PlacedFeatures.DILITHIUM,
EnhancedPlacedFeature.configure(
chunkScanRange = 6,
seedMix = 237483209523709234L,
root = EnhancedChainPlacement(
RarityFilter.onAverageOnceEvery(120).wrap(),
InSquarePlacement.spread().wrap(),
HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(0), 15.0)).wrap(),
EllipsoidPlacement(
x = ringularity,
y = ringularity,
z = ringularity,
count = UniformInt.of(8000, 16000),
xLength = UniformFloat.of(30f, 70f),
yLength = UniformFloat.of(40f, 90f),
zLength = UniformFloat.of(30f, 70f),
rotateXZ = BooleanProvider.AlwaysTrue,
),
ore.placement()
)
)
))
)
}
val blackHole = configured.getOrThrow(ConfiguredFeatures.BLACK_HOLE)
@ -179,7 +248,7 @@ fun registerBiomeModifiers(context: BootstrapContext<BiomeModifier>) {
HolderSet.direct(
placed.getOrThrow(PlacedFeatures.NORMAL_TRITANIUM),
placed.getOrThrow(PlacedFeatures.DEEP_TRITANIUM),
placed.getOrThrow(PlacedFeatures.CLOUD_TITANIUM),
placed.getOrThrow(PlacedFeatures.WORM_TRITANIUM),
placed.getOrThrow(PlacedFeatures.DILITHIUM),
),
GenerationStep.Decoration.UNDERGROUND_ORES

View File

@ -128,17 +128,13 @@ fun addItemModels(provider: MatteryItemModelProvider) {
provider.generated(MItems.BREAD_MONSTER_SPAWN_EGG, modLocation("item/egg/bread_monster"))
provider.generated(MItems.LOADER_SPAWN_EGG, modLocation("item/egg/loader"))
provider.capacitorWithGauge(MItems.BATTERY_CRUDE, 10, "battery_gauge_", modLocation("item/battery_tier0"))
provider.capacitorWithGauge(MItems.BATTERY_BASIC, 10, "battery_gauge_", modLocation("item/battery_tier1"))
provider.capacitorWithGauge(MItems.BATTERY_NORMAL, 10, "battery_gauge_", modLocation("item/battery_tier2"))
provider.capacitorWithGauge(MItems.BATTERY_DENSE, 10, "battery_gauge_", modLocation("item/battery_tier3"))
provider.capacitorWithGauge(MItems.BATTERY_CAPACITOR, 10, "battery_gauge_", modLocation("item/battery_tier4"))
provider.generatedTiered(MItems.BATTERIES, "battery_tier")
provider.generated(MItems.BATTERY_CREATIVE)
provider.capacitorWithGauge(MItems.PROCEDURAL_BATTERY, 9, "battery_procedural_gauge_", modLocation("item/battery_procedural"))
provider.generated(MItems.PROCEDURAL_BATTERY, modLocation("item/battery_procedural"))
provider.capacitorWithGauge(MItems.MATTER_CAPACITOR_BASIC, 8, "matter_capacitor_gauge_", modLocation("item/matter_capacitor_tier1"))
provider.capacitorWithGauge(MItems.MATTER_CAPACITOR_NORMAL, 8, "matter_capacitor_gauge_", modLocation("item/matter_capacitor_tier2"))
provider.capacitorWithGauge(MItems.MATTER_CAPACITOR_DENSE, 8, "matter_capacitor_gauge_", modLocation("item/matter_capacitor_tier3"))
provider.generated(MItems.MATTER_CAPACITOR_BASIC, modLocation("item/matter_capacitor_tier1"))
provider.generated(MItems.MATTER_CAPACITOR_NORMAL, modLocation("item/matter_capacitor_tier2"))
provider.generated(MItems.MATTER_CAPACITOR_DENSE, modLocation("item/matter_capacitor_tier3"))
provider.generated(MItems.MATTER_CAPACITOR_CREATIVE)
provider.generated(MItems.MachineUpgrades.Basic.BLANK, modLocation("item/machine_upgrade_tier1"))
@ -186,8 +182,8 @@ fun addItemModels(provider: MatteryItemModelProvider) {
provider.upgrade(MItems.MachineUpgrades.Creative.MATTER_STORAGE_FLAT_SMALL, "matter", "creative")
provider.capacitorWithGauge(MItems.QUANTUM_BATTERY, 10, "battery_gauge_")
provider.capacitorWithGauge(MItems.QUANTUM_CAPACITOR, 10, "battery_gauge_")
provider.generated(MItems.QUANTUM_BATTERY)
provider.generated(MItems.QUANTUM_CAPACITOR)
provider.generated(MItems.QUANTUM_BATTERY_CREATIVE)
provider.generated(MItems.PATTERN_DRIVE_NORMAL, modLocation("item/pattern_drive_tier1"))

View File

@ -143,33 +143,6 @@ class MatteryItemModelProvider(event: GatherDataEvent) : ItemModelProvider(event
.texture("layer1", modLocation("item/machine_upgrade_icon_$upgradeType"))
}
fun capacitorWithGauge(item: Item, fillTextures: Int, gaugePrefix: String, baseTexture: ResourceLocation? = null) = exec {
val path = item.registryName!!.path
val texture = baseTexture ?: modLocation("item/$path")
val empty = withExistingParent("${path}_empty", GENERATED)
.texture("layer0", texture)
val basic = withExistingParent(path, GENERATED)
.texture("layer0", texture)
.texture("layer1", modLocation("item/${gaugePrefix}$fillTextures"))
.override()
.predicate(modLocation("capacitor_gauge"), 0f)
.model(empty)
.end()
for (i in 1 .. fillTextures) {
val model = withExistingParent("${path}_fill_$i", GENERATED)
.texture("layer0", texture)
.texture("layer1", modLocation("item/${gaugePrefix}$i"))
basic.override()
.predicate(modLocation("capacitor_gauge"), i.toFloat() / fillTextures.toFloat())
.model(model)
.end()
}
}
companion object {
val ARMOR_TRIM_MATERIALS = listOf("quartz", "iron", "netherite", "redstone", "copper", "gold", "emerald", "diamond", "lapis", "amethyst")
val GENERATED = ResourceLocation("minecraft", "item/generated")

View File

@ -1,16 +0,0 @@
package ru.dbotthepony.mc.otm.mixin.ironchest;
import com.progwml6.ironchest.common.block.regular.entity.CopperChestBlockEntity;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import ru.dbotthepony.mc.otm.compat.ironchest.MatteryIronChestMenu;
@Mixin(CopperChestBlockEntity.class)
public abstract class CopperChestBlockEntityMixin {
@Overwrite(remap = false)
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
return MatteryIronChestMenu.copper(containerId, inventory, ((CopperChestBlockEntity)(Object) this));
}
}

View File

@ -1,16 +0,0 @@
package ru.dbotthepony.mc.otm.mixin.ironchest;
import com.progwml6.ironchest.common.block.regular.entity.CrystalChestBlockEntity;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import ru.dbotthepony.mc.otm.compat.ironchest.MatteryIronChestMenu;
@Mixin(CrystalChestBlockEntity.class)
public abstract class CrystalChestBlockEntityMixin {
@Overwrite(remap = false)
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
return MatteryIronChestMenu.crystal(containerId, inventory, ((CrystalChestBlockEntity)(Object) this));
}
}

View File

@ -1,16 +0,0 @@
package ru.dbotthepony.mc.otm.mixin.ironchest;
import com.progwml6.ironchest.common.block.regular.entity.DiamondChestBlockEntity;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import ru.dbotthepony.mc.otm.compat.ironchest.MatteryIronChestMenu;
@Mixin(DiamondChestBlockEntity.class)
public abstract class DiamondChestBlockEntityMixin {
@Overwrite(remap = false)
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
return MatteryIronChestMenu.diamond(containerId, inventory, ((DiamondChestBlockEntity)(Object) this));
}
}

View File

@ -1,16 +0,0 @@
package ru.dbotthepony.mc.otm.mixin.ironchest;
import com.progwml6.ironchest.common.block.regular.entity.DirtChestBlockEntity;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import ru.dbotthepony.mc.otm.compat.ironchest.MatteryIronChestMenu;
@Mixin(DirtChestBlockEntity.class)
public abstract class DirtChestBlockEntityMixin {
@Overwrite(remap = false)
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
return MatteryIronChestMenu.dirt(containerId, inventory, ((DirtChestBlockEntity)(Object) this));
}
}

View File

@ -1,16 +0,0 @@
package ru.dbotthepony.mc.otm.mixin.ironchest;
import com.progwml6.ironchest.common.block.regular.entity.GoldChestBlockEntity;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import ru.dbotthepony.mc.otm.compat.ironchest.MatteryIronChestMenu;
@Mixin(GoldChestBlockEntity.class)
public abstract class GoldChestBlockEntityMixin {
@Overwrite(remap = false)
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
return MatteryIronChestMenu.gold(containerId, inventory, ((GoldChestBlockEntity)(Object) this));
}
}

View File

@ -1,16 +0,0 @@
package ru.dbotthepony.mc.otm.mixin.ironchest;
import com.progwml6.ironchest.common.block.regular.entity.IronChestBlockEntity;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import ru.dbotthepony.mc.otm.compat.ironchest.MatteryIronChestMenu;
@Mixin(IronChestBlockEntity.class)
public abstract class IronChestBlockEntityMixin {
@Overwrite(remap = false)
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
return MatteryIronChestMenu.iron(containerId, inventory, ((IronChestBlockEntity)(Object) this));
}
}

View File

@ -1,16 +0,0 @@
package ru.dbotthepony.mc.otm.mixin.ironchest;
import com.progwml6.ironchest.common.block.regular.entity.ObsidianChestBlockEntity;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import ru.dbotthepony.mc.otm.compat.ironchest.MatteryIronChestMenu;
@Mixin(ObsidianChestBlockEntity.class)
public abstract class ObsidianChestBlockEntityMixin {
@Overwrite(remap = false)
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
return MatteryIronChestMenu.obsidian(containerId, inventory, ((ObsidianChestBlockEntity)(Object) this));
}
}

View File

@ -1,16 +0,0 @@
package ru.dbotthepony.mc.otm.mixin.ironchest;
import com.progwml6.ironchest.common.block.trapped.entity.TrappedCopperChestBlockEntity;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import ru.dbotthepony.mc.otm.compat.ironchest.MatteryIronChestMenu;
@Mixin(TrappedCopperChestBlockEntity.class)
public abstract class TrappedCopperChestBlockEntityMixin {
@Overwrite(remap = false)
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
return MatteryIronChestMenu.copper(containerId, inventory, ((TrappedCopperChestBlockEntity)(Object) this));
}
}

View File

@ -1,16 +0,0 @@
package ru.dbotthepony.mc.otm.mixin.ironchest;
import com.progwml6.ironchest.common.block.trapped.entity.TrappedCrystalChestBlockEntity;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import ru.dbotthepony.mc.otm.compat.ironchest.MatteryIronChestMenu;
@Mixin(TrappedCrystalChestBlockEntity.class)
public abstract class TrappedCrystalChestBlockEntityMixin {
@Overwrite(remap = false)
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
return MatteryIronChestMenu.crystal(containerId, inventory, ((TrappedCrystalChestBlockEntity)(Object) this));
}
}

View File

@ -1,16 +0,0 @@
package ru.dbotthepony.mc.otm.mixin.ironchest;
import com.progwml6.ironchest.common.block.trapped.entity.TrappedDiamondChestBlockEntity;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import ru.dbotthepony.mc.otm.compat.ironchest.MatteryIronChestMenu;
@Mixin(TrappedDiamondChestBlockEntity.class)
public abstract class TrappedDiamondChestBlockEntityMixin {
@Overwrite(remap = false)
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
return MatteryIronChestMenu.diamond(containerId, inventory, ((TrappedDiamondChestBlockEntity)(Object) this));
}
}

View File

@ -1,16 +0,0 @@
package ru.dbotthepony.mc.otm.mixin.ironchest;
import com.progwml6.ironchest.common.block.trapped.entity.TrappedDirtChestBlockEntity;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import ru.dbotthepony.mc.otm.compat.ironchest.MatteryIronChestMenu;
@Mixin(TrappedDirtChestBlockEntity.class)
public abstract class TrappedDirtChestBlockEntityMixin {
@Overwrite(remap = false)
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
return MatteryIronChestMenu.dirt(containerId, inventory, ((TrappedDirtChestBlockEntity)(Object) this));
}
}

View File

@ -1,16 +0,0 @@
package ru.dbotthepony.mc.otm.mixin.ironchest;
import com.progwml6.ironchest.common.block.trapped.entity.TrappedGoldChestBlockEntity;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import ru.dbotthepony.mc.otm.compat.ironchest.MatteryIronChestMenu;
@Mixin(TrappedGoldChestBlockEntity.class)
public abstract class TrappedGoldChestBlockEntityMixin {
@Overwrite(remap = false)
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
return MatteryIronChestMenu.gold(containerId, inventory, ((TrappedGoldChestBlockEntity)(Object) this));
}
}

View File

@ -1,16 +0,0 @@
package ru.dbotthepony.mc.otm.mixin.ironchest;
import com.progwml6.ironchest.common.block.trapped.entity.TrappedIronChestBlockEntity;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import ru.dbotthepony.mc.otm.compat.ironchest.MatteryIronChestMenu;
@Mixin(TrappedIronChestBlockEntity.class)
public abstract class TrappedIronChestBlockEntityMixin {
@Overwrite(remap = false)
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
return MatteryIronChestMenu.iron(containerId, inventory, ((TrappedIronChestBlockEntity)(Object) this));
}
}

View File

@ -1,16 +0,0 @@
package ru.dbotthepony.mc.otm.mixin.ironchest;
import com.progwml6.ironchest.common.block.trapped.entity.TrappedObsidianChestBlockEntity;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import ru.dbotthepony.mc.otm.compat.ironchest.MatteryIronChestMenu;
@Mixin(TrappedObsidianChestBlockEntity.class)
public abstract class TrappedObsidianChestBlockEntityMixin {
@Overwrite(remap = false)
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
return MatteryIronChestMenu.obsidian(containerId, inventory, ((TrappedObsidianChestBlockEntity)(Object) this));
}
}

View File

@ -1,16 +0,0 @@
package ru.dbotthepony.mc.otm.mixin.ironshulkerbox;
import com.progwml6.ironshulkerbox.common.block.entity.CopperShulkerBoxBlockEntity;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import ru.dbotthepony.mc.otm.compat.ironshulkerbox.MatteryIronShulkerBoxMenu;
@Mixin(CopperShulkerBoxBlockEntity.class)
public abstract class CopperShulkerBoxBlockEntityMixin {
@Overwrite(remap = false)
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
return MatteryIronShulkerBoxMenu.copper(containerId, inventory, ((CopperShulkerBoxBlockEntity)(Object) this));
}
}

View File

@ -1,16 +0,0 @@
package ru.dbotthepony.mc.otm.mixin.ironshulkerbox;
import com.progwml6.ironshulkerbox.common.block.entity.CrystalShulkerBoxBlockEntity;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import ru.dbotthepony.mc.otm.compat.ironshulkerbox.MatteryIronShulkerBoxMenu;
@Mixin(CrystalShulkerBoxBlockEntity.class)
public abstract class CrystalShulkerBoxBlockEntityMixin {
@Overwrite(remap = false)
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
return MatteryIronShulkerBoxMenu.crystal(containerId, inventory, ((CrystalShulkerBoxBlockEntity)(Object) this));
}
}

View File

@ -1,16 +0,0 @@
package ru.dbotthepony.mc.otm.mixin.ironshulkerbox;
import com.progwml6.ironshulkerbox.common.block.entity.DiamondShulkerBoxBlockEntity;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import ru.dbotthepony.mc.otm.compat.ironshulkerbox.MatteryIronShulkerBoxMenu;
@Mixin(DiamondShulkerBoxBlockEntity.class)
public abstract class DiamondShulkerBoxBlockEntityMixin {
@Overwrite(remap = false)
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
return MatteryIronShulkerBoxMenu.diamond(containerId, inventory, ((DiamondShulkerBoxBlockEntity)(Object) this));
}
}

View File

@ -1,16 +0,0 @@
package ru.dbotthepony.mc.otm.mixin.ironshulkerbox;
import com.progwml6.ironshulkerbox.common.block.entity.GoldShulkerBoxBlockEntity;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import ru.dbotthepony.mc.otm.compat.ironshulkerbox.MatteryIronShulkerBoxMenu;
@Mixin(GoldShulkerBoxBlockEntity.class)
public abstract class GoldShulkerBoxBlockEntityMixin {
@Overwrite(remap = false)
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
return MatteryIronShulkerBoxMenu.gold(containerId, inventory, ((GoldShulkerBoxBlockEntity)(Object) this));
}
}

View File

@ -1,16 +0,0 @@
package ru.dbotthepony.mc.otm.mixin.ironshulkerbox;
import com.progwml6.ironshulkerbox.common.block.entity.IronShulkerBoxBlockEntity;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import ru.dbotthepony.mc.otm.compat.ironshulkerbox.MatteryIronShulkerBoxMenu;
@Mixin(IronShulkerBoxBlockEntity.class)
public abstract class IronShulkerBoxBlockEntityMixin {
@Overwrite(remap = false)
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
return MatteryIronShulkerBoxMenu.iron(containerId, inventory, ((IronShulkerBoxBlockEntity)(Object) this));
}
}

View File

@ -1,16 +0,0 @@
package ru.dbotthepony.mc.otm.mixin.ironshulkerbox;
import com.progwml6.ironshulkerbox.common.block.entity.ObsidianShulkerBoxBlockEntity;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import ru.dbotthepony.mc.otm.compat.ironshulkerbox.MatteryIronShulkerBoxMenu;
@Mixin(ObsidianShulkerBoxBlockEntity.class)
public abstract class ObsidianShulkerBoxBlockEntityMixin {
@Overwrite(remap = false)
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
return MatteryIronShulkerBoxMenu.obsidian(containerId, inventory, ((ObsidianShulkerBoxBlockEntity)(Object) this));
}
}

View File

@ -6,10 +6,8 @@ import net.neoforged.bus.api.EventPriority
import net.neoforged.fml.common.Mod
import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent
import ru.dbotthepony.mc.otm.player.android.AndroidResearchDescription
import ru.dbotthepony.mc.otm.player.android.AndroidResearchDescriptions
import ru.dbotthepony.mc.otm.player.android.AndroidResearchManager
import ru.dbotthepony.mc.otm.player.android.AndroidResearchResult
import ru.dbotthepony.mc.otm.player.android.AndroidResearchResults
import ru.dbotthepony.mc.otm.player.android.feature.EnderTeleporterFeature
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity
@ -44,10 +42,6 @@ import ru.dbotthepony.mc.otm.client.render.blockentity.BatteryBankRenderer
import ru.dbotthepony.mc.otm.client.render.blockentity.MatterBatteryBankRenderer
import ru.dbotthepony.mc.otm.compat.curios.isCuriosLoaded
import ru.dbotthepony.mc.otm.compat.curios.onCuriosSlotModifiersUpdated
import ru.dbotthepony.mc.otm.compat.ironchest.IronChestMenuTypes
import ru.dbotthepony.mc.otm.compat.ironchest.isIronChestLoaded
import ru.dbotthepony.mc.otm.compat.ironshulkerbox.IronShulkersMenuTypes
import ru.dbotthepony.mc.otm.compat.ironshulkerbox.isIronShulkersLoaded
import ru.dbotthepony.mc.otm.compat.vanilla.MatteryChestMenu
import ru.dbotthepony.mc.otm.compat.vanilla.VanillaMenuTypes
import ru.dbotthepony.mc.otm.config.PlayerConfig
@ -60,6 +54,7 @@ import ru.dbotthepony.mc.otm.config.ServerConfig
import ru.dbotthepony.mc.otm.config.ToolsConfig
import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.mc.otm.data.FlywheelMaterials
import ru.dbotthepony.mc.otm.data.world.BooleanProvider
import ru.dbotthepony.mc.otm.data.world.DecimalProvider
import ru.dbotthepony.mc.otm.entity.WitheredSkeletonSpawnHandler
import ru.dbotthepony.mc.otm.item.ChestUpgraderItem
@ -93,11 +88,14 @@ import ru.dbotthepony.mc.otm.registry.data.MHeightProviders
import ru.dbotthepony.mc.otm.registry.data.MItemFunctionTypes
import ru.dbotthepony.mc.otm.registry.data.MLootItemConditions
import ru.dbotthepony.mc.otm.registry.data.MLootNumberProviders
import ru.dbotthepony.mc.otm.registry.data.MNumberProviders
import ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers
import ru.dbotthepony.mc.otm.registry.data.MWorldGenFeatures
import ru.dbotthepony.mc.otm.server.MCommands
import ru.dbotthepony.mc.otm.storage.StorageStack
import ru.dbotthepony.mc.otm.server.triggers.KillAsAndroidTrigger
import ru.dbotthepony.mc.otm.server.world.feature.EnhancedFeature
import ru.dbotthepony.mc.otm.server.world.placement.EnhancedPlacement
import thedarkcolour.kotlinforforge.neoforge.forge.DIST
import thedarkcolour.kotlinforforge.neoforge.forge.FORGE_BUS
import thedarkcolour.kotlinforforge.neoforge.forge.LOADING_CONTEXT
@ -133,24 +131,20 @@ object OverdriveThatMatters {
CommandArgumentTypes.register(MOD_BUS)
MHeightProviders.register(MOD_BUS)
MPlacementModifiers.register(MOD_BUS)
MNumberProviders.register(MOD_BUS)
MLootNumberProviders.register(MOD_BUS)
StorageStack.register(MOD_BUS)
VanillaMenuTypes.register(MOD_BUS)
if (isIronChestLoaded) {
IronChestMenuTypes.register(MOD_BUS)
}
if (isIronShulkersLoaded) {
IronShulkersMenuTypes.register(MOD_BUS)
}
MCreativeTabs.initialize(MOD_BUS)
MOD_BUS.addListener(::registerNetworkPackets)
DecimalProvider.register(MOD_BUS)
BooleanProvider.register(MOD_BUS)
EnhancedFeature.register(MOD_BUS)
EnhancedPlacement.register(MOD_BUS)
AndroidResearchDescriptions.register(MOD_BUS)
AndroidResearchResults.register(MOD_BUS)

View File

@ -348,17 +348,6 @@ abstract class MatteryBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state
private var cache: BlockCapabilityCache<T, in Direction?>? = null
private val listeners = Listenable.Impl<T?>()
private val lookupProvider = Supplier<T?> {
if (isRemoved)
return@Supplier null
val get = cache?.capability
provider = Supplier { get }
return@Supplier get
}
private var provider: Supplier<T?> = lookupProvider
override fun addListener(listener: Consumer<T?>): Listenable.L {
return listeners.addListener(listener)
}
@ -369,21 +358,23 @@ abstract class MatteryBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state
}
override fun get(): T? {
return provider.get()
if (isRemoved)
return null
return cache?.capability
}
val isPresent: Boolean
get() = get() != null
get() = cache?.capability != null
val isEmpty: Boolean
get() = get() == null
get() = cache?.capability == null
fun rebuildCache() {
if (!SERVER_IS_LIVE) return
val level = level as? ServerLevel
val creationVersion = ++currentVersion
provider = lookupProvider
if (level == null) {
cache = null
@ -397,21 +388,12 @@ abstract class MatteryBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state
{ !isRemoved || creationVersion != currentVersion },
// IllegalStateException("Do not call getCapability on an invalid cache or from the invalidation listener!")
// what a shame.
{
provider = lookupProvider
if (SERVER_IS_LIVE) onceServer { if (!isRemoved && creationVersion == currentVersion) listeners.accept(get()) }
})
{ if (SERVER_IS_LIVE) onceServer { if (!isRemoved && creationVersion == currentVersion) listeners.accept(cache?.capability) } })
onceServer {
if (!isRemoved && creationVersion == currentVersion) listeners.accept(get())
if (!isRemoved && creationVersion == currentVersion) listeners.accept(cache?.capability)
}
}
fun removeCache() {
cache = null
currentVersion++
provider = Supplier { null }
}
}
val syncher = SynchableGroup()
@ -442,7 +424,6 @@ abstract class MatteryBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state
override fun setRemoved() {
super.setRemoved()
capabilityCaches.forEach { it.removeCache() }
unsubscribe()
}

View File

@ -115,7 +115,7 @@ abstract class EnergyCableBlockEntity(type: BlockEntityType<*>, blockPos: BlockP
val sides get() = energySides
val currentlyTransferringTo = ObjectArraySet<Pair<Node, CableSide>>()
val currentlyTransferringTo = ObjectArraySet<Pair<Node, RelativeSide>>()
override fun onNeighbour(link: Link) {
if (link is DirectionLink) {

View File

@ -74,8 +74,8 @@ private class LinkedPriorityQueue<T : Comparable<T>> {
}
class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableGraph>() {
private val livelyNodes = HashSet<Pair<EnergyCableBlockEntity.Node, EnergyCableBlockEntity.CableSide>>()
private val livelyNodesList = ArrayList<Pair<EnergyCableBlockEntity.Node, EnergyCableBlockEntity.CableSide>>()
private val livelyNodes = HashSet<Pair<EnergyCableBlockEntity.Node, RelativeSide>>()
private val livelyNodesList = ArrayList<Pair<EnergyCableBlockEntity.Node, RelativeSide>>()
fun addLivelyNode(node: EnergyCableBlockEntity.Node) {
when (contains(node)) {
@ -83,7 +83,7 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
ContainsStatus.DOES_NOT_BELONG -> throw IllegalArgumentException("$node does not belong to $this")
ContainsStatus.CONTAINS -> {
for (dir in RelativeSide.entries) {
val pair = node to node.sides[dir]!!
val pair = node to dir
if (livelyNodes.add(pair)) {
livelyNodesList.add(pair)
@ -589,7 +589,7 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
override fun onNodeRemoved(node: EnergyCableBlockEntity.Node) {
for (dir in RelativeSide.entries) {
val pair = node to node.sides[dir]!!
val pair = node to dir
if (livelyNodes.remove(pair)) {
check(livelyNodesList.remove(pair))
@ -617,7 +617,7 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
override fun onNodeAdded(node: EnergyCableBlockEntity.Node) {
for (dir in RelativeSide.entries) {
val pair = node to node.sides[dir]!!
val pair = node to dir
check(livelyNodes.add(pair))
livelyNodesList.add(pair)
}
@ -649,7 +649,8 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
continue
try {
val (node, side) = pair
val (node, relSide) = pair
val side = node.sides[relSide]!!
if (!side.isEnabled) {
indicesToRemove.add(i)

View File

@ -29,7 +29,6 @@ import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import ru.dbotthepony.mc.otm.util.math.Decimal
import ru.dbotthepony.mc.otm.util.countingLazy
import ru.dbotthepony.mc.otm.graph.matter.SimpleMatterNode
import ru.dbotthepony.mc.otm.util.math.toDecimal
import java.util.function.BooleanSupplier
class MatterBottlerBlockEntity(blockPos: BlockPos, blockState: BlockState) :
@ -198,25 +197,16 @@ class MatterBottlerBlockEntity(blockPos: BlockPos, blockState: BlockState) :
if (matter.storedMatter.isPositive) {
val energyRatio = if (energyRate <= Decimal.ZERO) Decimal.ONE else energy.extractEnergy(energyRate, true) / energyRate
val matterTransferred = matter.extractMatter(capability.receiveMatter(rate.coerceAtMost(matter.storedMatter), true), true)
val matterRatio = matterTransferred / rate
val matterRatio = matter.extractMatter(capability.receiveMatter(rate.coerceAtMost(matter.storedMatter), true), true) / rate
if (matterRatio == Decimal.ZERO && matterTransferred > Decimal.ZERO) {
// transferring very little matter, transfer this time for free
val minRatio = minOf(matterRatio, energyRatio)
if (minRatio > Decimal.ZERO) {
isWorking = true
matter.extractMatter(capability.receiveMatter(matter.storedMatter, false), false)
energy.extractEnergy(energyRate * minRatio, false)
matter.extractMatter(capability.receiveMatter(rate * minRatio, false), false)
workProgress = ((capability.storedMatter - initialCapacity!!) / capability.maxStoredMatter).toFloat()
slot.setChanged()
} else {
val minRatio = minOf(matterRatio, energyRatio)
if (minRatio > Decimal.ZERO) {
isWorking = true
energy.extractEnergy(energyRate * minRatio, false)
matter.extractMatter(capability.receiveMatter(rate * minRatio, false), false)
workProgress = ((capability.storedMatter - initialCapacity!!) / capability.maxStoredMatter).toFloat()
slot.setChanged()
}
}
} else {
if (spitItemsWhenCantWork) {
@ -272,28 +262,20 @@ class MatterBottlerBlockEntity(blockPos: BlockPos, blockState: BlockState) :
initialCapacity = initialCapacity ?: it.storedMatter
hasCapacitors = true
val rate = MachinesConfig.MatterBottler.RATE * (Decimal.ONE + upgrades.speedBonus.toDecimal())
val energyRate = MachinesConfig.MatterBottler.VALUES.energyConsumption * (Decimal.ONE + upgrades.speedBonus.toDecimal())
val rate = MachinesConfig.MatterBottler.RATE * (1.0 + upgrades.speedBonus)
val energyRate = MachinesConfig.MatterBottler.VALUES.energyConsumption * (1.0 + upgrades.speedBonus)
val energyRatio = if (energyRate <= Decimal.ZERO) Decimal.ONE else energy.extractEnergy(energyRate, true) / energyRate
val matterExtracted = matter.receiveMatter(it.extractMatterChecked(rate, true), true)
val matterRatio = matterExtracted / rate
val matterRatio = matter.receiveMatter(it.extractMatterChecked(rate, true), true) / rate
if (matterRatio == Decimal.ZERO && matterExtracted > Decimal.ZERO) {
// very little matter extracted, extract this one for free
val minRatio = minOf(energyRatio, matterRatio)
if (minRatio > Decimal.ZERO) {
any = true
matter.receiveMatter(it.extractMatterChecked(matterExtracted, false), false)
energy.extractEnergy(energyRate * energyRatio, false)
matter.receiveMatter(it.extractMatterChecked(rate * minRatio, false), false)
workProgress = 1f - (it.storedMatter / initialCapacity!!).toFloat()
} else {
val minRatio = minOf(energyRatio, matterRatio)
if (minRatio > Decimal.ZERO) {
any = true
energy.extractEnergy(energyRate * energyRatio, false)
matter.receiveMatter(it.extractMatterChecked(rate * minRatio, false), false)
workProgress = 1f - (it.storedMatter / initialCapacity!!).toFloat()
}
}
break

View File

@ -31,7 +31,7 @@ import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
import java.util.Collections.emptyIterator
import java.util.stream.Stream
internal val isCosmeticArmorLoaded by lazy {
val isCosmeticArmorLoaded by lazy {
ModList.get().isLoaded("cosmeticarmorreworked")
}

View File

@ -35,7 +35,7 @@ import java.util.Collections.emptyIterator
import java.util.stream.Stream
import kotlin.collections.ArrayList
internal val isCuriosLoaded by lazy {
val isCuriosLoaded by lazy {
ModList.get().isLoaded(CuriosApi.MODID)
}

View File

@ -1,66 +0,0 @@
package ru.dbotthepony.mc.otm.compat.ironchest
import com.progwml6.ironchest.IronChests
import com.progwml6.ironchest.common.block.IronChestsTypes
import net.minecraft.core.BlockPos
import net.minecraft.core.registries.Registries
import net.minecraft.world.Container
import net.minecraft.world.flag.FeatureFlags
import net.minecraft.world.inventory.MenuType
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.neoforged.bus.api.IEventBus
import net.neoforged.fml.ModList
import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent
import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.capability.IQuickStackContainer
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.compat.vanilla.VanillaChestScreen
import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
import ru.dbotthepony.mc.otm.menu.makeSlots
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
internal val isIronChestLoaded by lazy {
ModList.get().isLoaded(IronChests.MODID)
}
object IronChestMenuTypes {
private val registrar = MDeferredRegister(Registries.MENU, OverdriveThatMatters.MOD_ID)
val IRON by registrar.register("ironchest_iron") { MenuType(MatteryIronChestMenu::iron, FeatureFlags.VANILLA_SET) }
val GOLD by registrar.register("ironchest_gold") { MenuType(MatteryIronChestMenu::gold, FeatureFlags.VANILLA_SET) }
val DIAMOND by registrar.register("ironchest_diamond") { MenuType(MatteryIronChestMenu::diamond, FeatureFlags.VANILLA_SET) }
val COPPER by registrar.register("ironchest_copper") { MenuType(MatteryIronChestMenu::copper, FeatureFlags.VANILLA_SET) }
val CRYSTAL by registrar.register("ironchest_crystal") { MenuType(MatteryIronChestMenu::crystal, FeatureFlags.VANILLA_SET) }
val OBSIDIAN by registrar.register("ironchest_obsidian") { MenuType(MatteryIronChestMenu::obsidian, FeatureFlags.VANILLA_SET) }
val DIRT by registrar.register("ironchest_dirt") { MenuType(MatteryIronChestMenu::dirt, FeatureFlags.VANILLA_SET) }
private fun provider(level: Level, pos: BlockPos, state: BlockState, blockEntity: BlockEntity?, context: Void?): IQuickStackContainer? {
val container = blockEntity as? Container ?: return null
return IQuickStackContainer.Simple(makeSlots(container, ::MatteryMenuSlot))
}
fun registerCapabilities(event: RegisterCapabilitiesEvent) {
IronChestsTypes.entries.map { IronChestsTypes.get(it) }.forEach {
event.registerBlock(MatteryCapability.QUICK_STACK_CONTAINER, ::provider, *it.toTypedArray())
}
}
internal fun register(bus: IEventBus) {
registrar.register(bus)
bus.addListener(this::registerScreens)
bus.addListener(this::registerCapabilities)
}
private fun registerScreens(event: RegisterMenuScreensEvent) {
event.register(IRON, ::VanillaChestScreen)
event.register(GOLD, ::VanillaChestScreen)
event.register(DIAMOND, ::VanillaChestScreen)
event.register(COPPER, ::VanillaChestScreen)
event.register(CRYSTAL, ::VanillaChestScreen)
event.register(OBSIDIAN, ::VanillaChestScreen)
event.register(DIRT, ::VanillaChestScreen)
}
}

View File

@ -1,82 +0,0 @@
package ru.dbotthepony.mc.otm.compat.ironchest
import com.progwml6.ironchest.common.block.IronChestsTypes
import net.minecraft.world.Container
import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.inventory.MenuType
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import ru.dbotthepony.mc.otm.compat.vanilla.AbstractVanillaChestMenu
import ru.dbotthepony.mc.otm.container.EnhancedContainer
import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
import ru.dbotthepony.mc.otm.menu.QuickMoveInput
import ru.dbotthepony.mc.otm.menu.makeSlots
class MatteryIronChestMenu(
type: MenuType<*>, containerId: Int,
inventory: Inventory, chestType: IronChestsTypes,
container: Container = EnhancedContainer.Simple(chestType.size),
) : AbstractVanillaChestMenu(type, containerId, inventory, container) {
override val columns = chestType.rowLength
override val rows = chestType.size / chestType.rowLength
override val containerSlots = if (chestType == IronChestsTypes.DIRT) makeSlots(container, ::MDirtChestSlot) else makeSlots(container, ::MatteryMenuSlot)
init {
container.startOpen(player)
addStorageSlot(containerSlots)
addInventorySlots()
}
override val quickMoveToStorage = QuickMoveInput.create(this, playerCombinedInventorySlots, containerSlots)
override val quickMoveFromStorage = QuickMoveInput.create(this, containerSlots, playerInventorySlots, false)
class MDirtChestSlot(container: Container, slot: Int) : MatteryMenuSlot(container, slot) {
override fun mayPlace(stack: ItemStack): Boolean = stack.isEmpty || stack.`is`(Items.DIRT)
}
companion object {
@JvmStatic
@JvmOverloads
fun iron(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(IronChestsTypes.IRON.size), chestType: IronChestsTypes = IronChestsTypes.IRON): MatteryIronChestMenu {
return MatteryIronChestMenu(IronChestMenuTypes.IRON, containerId, inventory, chestType, container)
}
@JvmStatic
@JvmOverloads
fun gold(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(IronChestsTypes.GOLD.size), chestType: IronChestsTypes = IronChestsTypes.GOLD): MatteryIronChestMenu {
return MatteryIronChestMenu(IronChestMenuTypes.GOLD, containerId, inventory, chestType, container)
}
@JvmStatic
@JvmOverloads
fun diamond(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(IronChestsTypes.DIAMOND.size), chestType: IronChestsTypes = IronChestsTypes.DIAMOND): MatteryIronChestMenu {
return MatteryIronChestMenu(IronChestMenuTypes.DIAMOND, containerId, inventory, chestType, container)
}
@JvmStatic
@JvmOverloads
fun copper(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(IronChestsTypes.COPPER.size), chestType: IronChestsTypes = IronChestsTypes.COPPER): MatteryIronChestMenu {
return MatteryIronChestMenu(IronChestMenuTypes.COPPER, containerId, inventory, chestType, container)
}
@JvmStatic
@JvmOverloads
fun crystal(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(IronChestsTypes.CRYSTAL.size), chestType: IronChestsTypes = IronChestsTypes.CRYSTAL): MatteryIronChestMenu {
return MatteryIronChestMenu(IronChestMenuTypes.CRYSTAL, containerId, inventory, chestType, container)
}
@JvmStatic
@JvmOverloads
fun obsidian(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(IronChestsTypes.OBSIDIAN.size), chestType: IronChestsTypes = IronChestsTypes.OBSIDIAN): MatteryIronChestMenu {
return MatteryIronChestMenu(IronChestMenuTypes.OBSIDIAN, containerId, inventory, chestType, container)
}
@JvmStatic
@JvmOverloads
fun dirt(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(IronChestsTypes.DIRT.size), chestType: IronChestsTypes = IronChestsTypes.DIRT): MatteryIronChestMenu {
return MatteryIronChestMenu(IronChestMenuTypes.DIRT, containerId, inventory, chestType, container)
}
}
}

View File

@ -1,68 +0,0 @@
package ru.dbotthepony.mc.otm.compat.ironshulkerbox
import com.progwml6.ironshulkerbox.IronShulkerBoxes
import com.progwml6.ironshulkerbox.common.block.IronShulkerBoxesTypes
import net.minecraft.core.BlockPos
import net.minecraft.core.registries.Registries
import net.minecraft.world.Container
import net.minecraft.world.flag.FeatureFlags
import net.minecraft.world.inventory.MenuType
import net.minecraft.world.item.DyeColor
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.neoforged.bus.api.IEventBus
import net.neoforged.fml.ModList
import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent
import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.capability.IQuickStackContainer
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.compat.ironshulkerbox.MatteryIronShulkerBoxMenu.Slot
import ru.dbotthepony.mc.otm.compat.vanilla.VanillaChestScreen
import ru.dbotthepony.mc.otm.menu.makeSlots
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
import kotlin.collections.toTypedArray
internal val isIronShulkersLoaded by lazy {
ModList.get().isLoaded(IronShulkerBoxes.MODID)
}
object IronShulkersMenuTypes {
private val registrar = MDeferredRegister(Registries.MENU, OverdriveThatMatters.MOD_ID)
val IRON by registrar.register("ironshulkerbox_iron") { MenuType(MatteryIronShulkerBoxMenu::iron, FeatureFlags.VANILLA_SET) }
val GOLD by registrar.register("ironshulkerbox_gold") { MenuType(MatteryIronShulkerBoxMenu::gold, FeatureFlags.VANILLA_SET) }
val DIAMOND by registrar.register("ironshulkerbox_diamond") { MenuType(MatteryIronShulkerBoxMenu::diamond, FeatureFlags.VANILLA_SET) }
val COPPER by registrar.register("ironshulkerbox_copper") { MenuType(MatteryIronShulkerBoxMenu::copper, FeatureFlags.VANILLA_SET) }
val CRYSTAL by registrar.register("ironshulkerbox_crystal") { MenuType(MatteryIronShulkerBoxMenu::crystal, FeatureFlags.VANILLA_SET) }
val OBSIDIAN by registrar.register("ironshulkerbox_obsidian") { MenuType(MatteryIronShulkerBoxMenu::obsidian, FeatureFlags.VANILLA_SET) }
private fun provider(level: Level, pos: BlockPos, state: BlockState, blockEntity: BlockEntity?, context: Void?): IQuickStackContainer? {
val container = blockEntity as? Container ?: return null
return IQuickStackContainer.Simple(makeSlots(container, ::Slot))
}
fun registerCapabilities(event: RegisterCapabilitiesEvent) {
val colors = DyeColor.entries.toMutableList<DyeColor?>().also { it.add(0, null) }
IronShulkerBoxesTypes.entries.forEach { type ->
event.registerBlock(MatteryCapability.QUICK_STACK_CONTAINER, ::provider, *colors.map { IronShulkerBoxesTypes.get(type, it) }.toTypedArray())
}
}
internal fun register(bus: IEventBus) {
registrar.register(bus)
bus.addListener(this::registerScreens)
bus.addListener(this::registerCapabilities)
}
private fun registerScreens(event: RegisterMenuScreensEvent) {
event.register(IRON, ::VanillaChestScreen)
event.register(GOLD, ::VanillaChestScreen)
event.register(DIAMOND, ::VanillaChestScreen)
event.register(COPPER, ::VanillaChestScreen)
event.register(CRYSTAL, ::VanillaChestScreen)
event.register(OBSIDIAN, ::VanillaChestScreen)
}
}

View File

@ -1,77 +0,0 @@
package ru.dbotthepony.mc.otm.compat.ironshulkerbox
import com.progwml6.ironshulkerbox.common.block.IronShulkerBoxesTypes
import net.minecraft.world.Container
import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.inventory.MenuType
import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.compat.vanilla.AbstractVanillaChestMenu
import ru.dbotthepony.mc.otm.container.EnhancedContainer
import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
import ru.dbotthepony.mc.otm.menu.QuickMoveInput
import ru.dbotthepony.mc.otm.menu.makeSlots
class MatteryIronShulkerBoxMenu(
type: MenuType<*>, containerId: Int,
inventory: Inventory, boxType: IronShulkerBoxesTypes,
container: Container = EnhancedContainer.Simple(boxType.size)
) : AbstractVanillaChestMenu(type, containerId, inventory, container) {
override val rows: Int = boxType.size / boxType.rowLength
override val columns: Int = boxType.rowLength
override val containerSlots = makeSlots(container, ::Slot)
init {
container.startOpen(player)
addStorageSlot(containerSlots)
addInventorySlots()
}
override val quickMoveToStorage = QuickMoveInput.create(this, playerCombinedInventorySlots, containerSlots)
override val quickMoveFromStorage = QuickMoveInput.create(this, containerSlots, playerInventorySlots, false)
class Slot(container: Container, slot: Int) : MatteryMenuSlot(container, slot) {
override fun mayPlace(stack: ItemStack): Boolean {
return super.mayPlace(stack) && stack.item.canFitInsideContainerItems()
}
}
companion object {
@JvmStatic
@JvmOverloads
fun iron(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(IronShulkerBoxesTypes.IRON.size), boxType: IronShulkerBoxesTypes = IronShulkerBoxesTypes.IRON): MatteryIronShulkerBoxMenu {
return MatteryIronShulkerBoxMenu(IronShulkersMenuTypes.IRON, containerId, inventory, boxType, container)
}
@JvmStatic
@JvmOverloads
fun gold(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(IronShulkerBoxesTypes.GOLD.size), boxType: IronShulkerBoxesTypes = IronShulkerBoxesTypes.GOLD): MatteryIronShulkerBoxMenu {
return MatteryIronShulkerBoxMenu(IronShulkersMenuTypes.GOLD, containerId, inventory, boxType, container)
}
@JvmStatic
@JvmOverloads
fun diamond(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(IronShulkerBoxesTypes.DIAMOND.size), boxType: IronShulkerBoxesTypes = IronShulkerBoxesTypes.DIAMOND): MatteryIronShulkerBoxMenu {
return MatteryIronShulkerBoxMenu(IronShulkersMenuTypes.DIAMOND, containerId, inventory, boxType, container)
}
@JvmStatic
@JvmOverloads
fun copper(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(IronShulkerBoxesTypes.COPPER.size), boxType: IronShulkerBoxesTypes = IronShulkerBoxesTypes.COPPER): MatteryIronShulkerBoxMenu {
return MatteryIronShulkerBoxMenu(IronShulkersMenuTypes.COPPER, containerId, inventory, boxType, container)
}
@JvmStatic
@JvmOverloads
fun crystal(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(IronShulkerBoxesTypes.CRYSTAL.size), boxType: IronShulkerBoxesTypes = IronShulkerBoxesTypes.CRYSTAL): MatteryIronShulkerBoxMenu {
return MatteryIronShulkerBoxMenu(IronShulkersMenuTypes.CRYSTAL, containerId, inventory, boxType, container)
}
@JvmStatic
@JvmOverloads
fun obsidian(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(IronShulkerBoxesTypes.OBSIDIAN.size), boxType: IronShulkerBoxesTypes = IronShulkerBoxesTypes.OBSIDIAN): MatteryIronShulkerBoxMenu {
return MatteryIronShulkerBoxMenu(IronShulkersMenuTypes.OBSIDIAN, containerId, inventory, boxType, container)
}
}
}

View File

@ -5,7 +5,7 @@ import net.minecraft.world.inventory.Slot
import net.neoforged.fml.ModList
import ru.dbotthepony.mc.otm.client.render.MGUIGraphics
internal val isItemBordersLoaded by lazy {
val isItemBordersLoaded by lazy {
ModList.get().isLoaded("itemborders")
}

View File

@ -53,3 +53,5 @@ fun <S : Comparable<S>> Codec<S>.maxRange(max: S, exclusive: Boolean = false) =
ComparableCodec(this, max = max, maxExclusive = exclusive)
fun <S : Comparable<S>> Codec<S>.inRange(min: S, minExclusive: Boolean = false, max: S, maxExclusive: Boolean = false) =
ComparableCodec(this, min, max, minExclusive, maxExclusive)
fun <S : Comparable<S>> Codec<S>.inRange(min: S, max: S) =
ComparableCodec(this, min, max, false, false)

View File

@ -0,0 +1,142 @@
package ru.dbotthepony.mc.otm.data.world
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.util.RandomSource
import net.neoforged.bus.api.IEventBus
import ru.dbotthepony.mc.otm.data.codec.inRange
import ru.dbotthepony.mc.otm.registry.MBuiltInRegistries
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
import ru.dbotthepony.mc.otm.registry.MRegistries
interface BooleanProvider {
interface Type<T : BooleanProvider> {
val codec: MapCodec<T>
}
fun instance(): Instance
val type: Type<*>
fun interface Instance {
fun sample(random: RandomSource): Boolean
}
/**
* Each time boolean is sampled, there is a fixed chance for it to test true
*/
data class Unbiased(val chance: Float) : BooleanProvider, Instance {
constructor(chance: Int) : this(1f / chance)
override fun instance(): Instance {
return this
}
override val type: Type<*>
get() = Companion
override fun sample(random: RandomSource): Boolean {
return random.nextFloat() <= chance
}
companion object : Type<Unbiased> {
override val codec: MapCodec<Unbiased> = RecordCodecBuilder.mapCodec {
it.group(Codec.FLOAT.inRange(0f, 1f).fieldOf("chance").forGetter(Unbiased::chance)).apply(it, ::Unbiased)
}
}
}
/**
* Successful roll chance is specified as follows:
* ```
* base_chance * failures
* ```
*
* `failures` starts at 1, and increases each time roll failed, eventually reaching 100%
* if no successful rolls have been made.
*
* Once roll is successful, `failures` is reset back to 1.
*/
data class BiasedLinear(val baseChance: Float) : BooleanProvider {
constructor(middle: Float, at: Int) : this(middle / at)
private class I(private val baseChance: Float) : Instance {
var lastSuccess = 1f
override fun sample(random: RandomSource): Boolean {
val success = random.nextFloat() <= lastSuccess * baseChance
if (success)
lastSuccess = 1f
else
lastSuccess += 1f
return success
}
}
override fun instance(): Instance {
return I(baseChance)
}
override val type: Type<*>
get() = Companion
companion object : Type<BiasedLinear> {
override val codec: MapCodec<BiasedLinear> = RecordCodecBuilder.mapCodec {
it.group(
Codec.FLOAT.inRange(0f, 1f).fieldOf("base_chance").forGetter(BiasedLinear::baseChance),
).apply(it, ::BiasedLinear)
}
}
}
object AlwaysTrue : BooleanProvider, Instance, Type<AlwaysTrue> {
override fun instance(): Instance {
return this
}
override fun sample(random: RandomSource): Boolean {
return true
}
override val type: Type<*>
get() = this
override val codec: MapCodec<AlwaysTrue> = MapCodec.unit(this)
}
object AlwaysFalse : BooleanProvider, Instance, Type<AlwaysFalse> {
override fun instance(): Instance {
return this
}
override fun sample(random: RandomSource): Boolean {
return false
}
override val type: Type<*>
get() = this
override val codec: MapCodec<AlwaysFalse> = MapCodec.unit(this)
}
companion object {
private val registrar = MDeferredRegister(MRegistries.BOOLEAN_PROVIDER)
init {
registrar.register("unbiased") { Unbiased.Companion }
registrar.register("linear_bias") { BiasedLinear.Companion }
registrar.register("true") { AlwaysTrue }
registrar.register("false") { AlwaysFalse }
}
val CODEC: Codec<BooleanProvider> by lazy {
MBuiltInRegistries.BOOLEAN_PROVIDER.byNameCodec().dispatch({ it.type }, { it.codec })
}
internal fun register(bus: IEventBus) {
registrar.register(bus)
}
}
}

View File

@ -1,92 +0,0 @@
package ru.dbotthepony.mc.otm.data.world
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.core.BlockPos
import net.minecraft.util.RandomSource
import net.minecraft.util.valueproviders.FloatProvider
import net.minecraft.util.valueproviders.IntProvider
import net.minecraft.world.level.levelgen.placement.PlacementContext
import net.minecraft.world.level.levelgen.placement.PlacementModifier
import net.minecraft.world.level.levelgen.placement.PlacementModifierType
import ru.dbotthepony.mc.otm.util.math.component1
import ru.dbotthepony.mc.otm.util.math.component2
import ru.dbotthepony.mc.otm.util.math.component3
import ru.dbotthepony.mc.otm.util.math.minus
import ru.dbotthepony.mc.otm.util.math.plus
import ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers
import java.util.stream.Stream
import kotlin.math.PI
import kotlin.math.roundToInt
// aka "cloud placement"
data class EllipsoidPlacement(
val x: IntProvider,
val z: IntProvider,
val y: IntProvider,
val count: IntProvider,
val xLength: FloatProvider,
val zLength: FloatProvider,
val yLength: FloatProvider,
) : PlacementModifier() {
init {
require(xLength.minValue >= 1f) { "Bad ellipsoid x minimal size: $xLength" }
require(zLength.minValue >= 1f) { "Bad ellipsoid z minimal size: $zLength" }
require(yLength.minValue >= 1f) { "Bad ellipsoid y minimal size: $yLength" }
}
override fun getPositions(
context: PlacementContext,
random: RandomSource,
position: BlockPos
): Stream<BlockPos> {
var count = count.sample(random)
if (count <= 0) {
return Stream.empty()
}
val xLength = xLength.sample(random)
val zLength = zLength.sample(random)
val yLength = yLength.sample(random)
val xPow = xLength * xLength
val zPow = zLength * zLength
val yPow = yLength * yLength
count = minOf(count, (xLength * zLength * yLength * PI * (4.0 / 3.0)).roundToInt())
count = 600
return Stream.generate { position + BlockPos(this.x.sample(random), this.y.sample(random), this.z.sample(random)) }
.limit(count * 10L) // failsafe
.filter {
val (ellipsoidX, ellipsoidY, ellipsoidZ) = it - position
(ellipsoidX * ellipsoidX) / xPow +
(ellipsoidY * ellipsoidY) / yPow +
(ellipsoidZ * ellipsoidZ) / zPow <= 1.0f
}
.distinct()
.limit(count.toLong())
}
override fun type(): PlacementModifierType<*> {
return MPlacementModifiers.ELLIPSOID_PLACEMENT
}
companion object {
val CODEC: MapCodec<EllipsoidPlacement> by lazy {
RecordCodecBuilder.mapCodec {
it.group(
IntProvider.CODEC.fieldOf("x").forGetter(EllipsoidPlacement::x),
IntProvider.CODEC.fieldOf("y").forGetter(EllipsoidPlacement::y),
IntProvider.CODEC.fieldOf("z").forGetter(EllipsoidPlacement::z),
IntProvider.CODEC.fieldOf("count").forGetter(EllipsoidPlacement::count),
FloatProvider.codec(1f, Float.MAX_VALUE).fieldOf("x_size").forGetter(EllipsoidPlacement::xLength),
FloatProvider.codec(1f, Float.MAX_VALUE).fieldOf("z_size").forGetter(EllipsoidPlacement::zLength),
FloatProvider.codec(1f, Float.MAX_VALUE).fieldOf("y_size").forGetter(EllipsoidPlacement::yLength),
).apply(it, ::EllipsoidPlacement)
}
}
}
}

View File

@ -0,0 +1,38 @@
package ru.dbotthepony.mc.otm.data.world
import net.minecraft.util.RandomSource
import net.minecraft.util.valueproviders.FloatProvider
import net.minecraft.util.valueproviders.FloatProviderType
import ru.dbotthepony.mc.otm.registry.data.MNumberProviders
class OneOfFloatProvider(private val provider: OneOfProvider<FloatProvider>) : FloatProvider() {
override fun sample(random: RandomSource): Float {
return provider.select(random).sample(random)
}
override fun getMinValue(): Float {
return provider.children.minOf { it.minValue }
}
override fun getMaxValue(): Float {
return provider.children.minOf { it.maxValue }
}
override fun getType(): FloatProviderType<*> {
return MNumberProviders.ONE_OF_FLOAT
}
companion object {
@JvmStatic
fun of(vararg providers: FloatProvider): OneOfFloatProvider {
return OneOfFloatProvider(OneOfProvider.of(*providers))
}
@JvmStatic
fun of(providers: Collection<FloatProvider>): OneOfFloatProvider {
return OneOfFloatProvider(OneOfProvider.of(providers))
}
val CODEC = OneOfProvider.createCodec(FloatProvider.CODEC, ::OneOfFloatProvider, OneOfFloatProvider::provider)
}
}

View File

@ -0,0 +1,38 @@
package ru.dbotthepony.mc.otm.data.world
import net.minecraft.util.RandomSource
import net.minecraft.util.valueproviders.IntProvider
import net.minecraft.util.valueproviders.IntProviderType
import ru.dbotthepony.mc.otm.registry.data.MNumberProviders
class OneOfIntProvider(private val provider: OneOfProvider<IntProvider>) : IntProvider() {
override fun sample(random: RandomSource): Int {
return provider.select(random).sample(random)
}
override fun getMinValue(): Int {
return provider.children.minOf { it.minValue }
}
override fun getMaxValue(): Int {
return provider.children.minOf { it.maxValue }
}
override fun getType(): IntProviderType<*> {
return MNumberProviders.ONE_OF_INT
}
companion object {
@JvmStatic
fun of(vararg providers: IntProvider): OneOfIntProvider {
return OneOfIntProvider(OneOfProvider.of(*providers))
}
@JvmStatic
fun of(providers: Collection<IntProvider>): OneOfIntProvider {
return OneOfIntProvider(OneOfProvider.of(providers))
}
val CODEC = OneOfProvider.createCodec(IntProvider.CODEC, ::OneOfIntProvider, OneOfIntProvider::provider)
}
}

View File

@ -0,0 +1,85 @@
package ru.dbotthepony.mc.otm.data.world
import com.google.common.collect.ImmutableList
import com.mojang.serialization.Codec
import com.mojang.serialization.DataResult
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.util.RandomSource
import net.minecraft.util.valueproviders.IntProvider
import ru.dbotthepony.mc.otm.util.random
import java.util.Optional
class OneOfProvider<T> private constructor(val children: List<T>, val selector: Optional<IntProvider> = Optional.empty()) {
fun select(random: RandomSource): T {
if (selector.isEmpty) {
return children.random(random)
}
val index = selector.get().sample(random)
if (index !in children.indices) {
throw RuntimeException("Provided selector ($selector) provided index out of bounds: $index (bounds: ${children.indices})")
}
return children[index]
}
class Builder<T> {
private var selector: IntProvider? = null
private val inputs = ArrayList<T>()
fun indexSelector(selector: IntProvider): Builder<T> {
this.selector = selector
return this
}
fun add(vararg provider: T): Builder<T> {
inputs.addAll(provider)
return this
}
fun copy(): Builder<T> {
val new = Builder<T>()
new.selector = selector
new.inputs.addAll(inputs)
return new
}
fun build(): OneOfProvider<T> {
return OneOfProvider(ImmutableList.copyOf(inputs), Optional.ofNullable(selector))
}
}
companion object {
@JvmStatic
fun <T> of(vararg providers: T): OneOfProvider<T> {
return OneOfProvider(ImmutableList.copyOf(providers))
}
@JvmStatic
fun <T> of(providers: Collection<T>): OneOfProvider<T> {
return OneOfProvider(ImmutableList.copyOf(providers))
}
@JvmStatic
fun <T> createCodec(childrenCodec: Codec<T>): MapCodec<OneOfProvider<T>> {
return RecordCodecBuilder.mapCodec {
it.group(
Codec.list(childrenCodec, 1, Int.MAX_VALUE).fieldOf("children").forGetter<OneOfProvider<T>> { it.children },
IntProvider.POSITIVE_CODEC.optionalFieldOf("selector").forGetter<OneOfProvider<T>> { it.selector },
).apply(it, ::OneOfProvider)
}.validate {
if (it.selector.isEmpty || it.selector.get().minValue in it.children.indices && it.selector.get().maxValue in it.children.indices) {
return@validate DataResult.success(it)
} else {
return@validate DataResult.error { "Provided number sampler ${it.selector.get()} can produce values outside of children list index range (list size: ${it.children.size})" }
}
}
}
fun <S, T> createCodec(childrenCodec: Codec<T>, to: (OneOfProvider<T>) -> S, from: (S) -> OneOfProvider<T>): MapCodec<S> {
return createCodec(childrenCodec).xmap(to, from)
}
}
}

View File

@ -163,13 +163,6 @@ class MatterDustItem : Item(Properties().stacksTo(64)), IMatterItem {
return matter(stack) >= ItemsConfig.MATTER_DUST_CAPACITY
}
fun makeStack(matterValue: Decimal = Decimal.ONE, count: Int = 1): ItemStack {
val stack = ItemStack(this, count)
matter(stack, matterValue)
return stack
}
override fun overrideStackedOnOther(
pStack: ItemStack,
pSlot: Slot,

View File

@ -1,7 +1,6 @@
package ru.dbotthepony.mc.otm.network
import io.netty.buffer.ByteBuf
import net.minecraft.core.Direction
import net.minecraft.core.UUIDUtil
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.codec.ByteBufCodecs
@ -11,7 +10,6 @@ import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.state.BlockState
import ru.dbotthepony.kommons.math.RGBAColor
import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.mc.otm.util.math.RelativeSide
import ru.dbotthepony.mc.otm.util.readDecimal
import ru.dbotthepony.mc.otm.util.writeDecimal
import ru.dbotthepony.mc.otm.util.readBlockType
@ -37,10 +35,6 @@ object StreamCodecs {
val RESOURCE_LOCATION = ResourceLocation.STREAM_CODEC.wrap()
val BLOCK_STATE: MatteryStreamCodec<ByteBuf, BlockState> = ByteBufCodecs.idMapper(Block.BLOCK_STATE_REGISTRY).wrap()
val BLOCK_TYPE = MatteryStreamCodec.Of(FriendlyByteBuf::writeBlockType, FriendlyByteBuf::readBlockType)
val RELATIVE_SIDE = MatteryStreamCodec.Enum<FriendlyByteBuf, RelativeSide>(RelativeSide::class.java)
val DIRECTION = MatteryStreamCodec.Enum<FriendlyByteBuf, Direction>(Direction::class.java)
val RELATIVE_SIDE_NULLABLE = RELATIVE_SIDE.nullable()
val DIRECTION_NULLABLE = DIRECTION.nullable()
val RGBA: MatteryStreamCodec<ByteBuf, RGBAColor> = StreamCodec.of<ByteBuf, RGBAColor>(
{ s, v -> s.writeFloat(v.red); s.writeFloat(v.green); s.writeFloat(v.blue); s.writeFloat(v.alpha) },

View File

@ -1,19 +1,8 @@
package ru.dbotthepony.mc.otm.network.syncher
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
import it.unimi.dsi.fastutil.ints.Int2ReferenceLinkedOpenHashMap
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap
import it.unimi.dsi.fastutil.ints.IntArraySet
import it.unimi.dsi.fastutil.ints.IntCollection
import it.unimi.dsi.fastutil.ints.IntIterator
import it.unimi.dsi.fastutil.ints.IntSet
import it.unimi.dsi.fastutil.objects.ObjectIterator
import it.unimi.dsi.fastutil.objects.ObjectSet
import it.unimi.dsi.fastutil.objects.Reference2ObjectLinkedOpenHashMap
import it.unimi.dsi.fastutil.objects.ReferenceCollection
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet
import it.unimi.dsi.fastutil.objects.ReferenceSet
import net.minecraft.network.RegistryFriendlyByteBuf
import ru.dbotthepony.kommons.util.KOptional
import ru.dbotthepony.mc.otm.util.IDAllocator
@ -21,39 +10,29 @@ import java.io.Closeable
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.atomic.AtomicBoolean
/**
* Constructs a new [DynamicSynchableGroup] where [T] is itself an [ISynchable]
*/
fun <T : ISynchable> DynamicSynchableGroup(reader: RegistryFriendlyByteBuf.() -> T, writer: T.(RegistryFriendlyByteBuf) -> Unit): DynamicSynchableGroup<T> {
return DynamicSynchableGroup(reader, { this }, writer)
}
/**
* Syncher group/set, which deals with synchables of only one type, and which are created and removed
* on remote (e.g. server adding and removing synchables at will), which makes it distinct from
* [SynchableGroup], in which attached synchables are created/removed manually on both sides.
*/
class DynamicSynchableGroup<T : Any>(
class DynamicSynchableGroup<T : ISynchable>(
/**
* Constructs new [T] instance locally, when remote created one.
* Data written by [writer] must be read here, if there is any.
*/
private val reader: RegistryFriendlyByteBuf.() -> T,
/**
* Gets an [ISynchable] out of value [T] for synching state
*/
private val synchable: T.() -> ISynchable,
/**
* Allows to write additional data to network stream during
* first-time networking of [T] to remote
*/
private val writer: T.(RegistryFriendlyByteBuf) -> Unit = {},
) : ISynchable, ReferenceSet<T> {
private val writer: T.(RegistryFriendlyByteBuf) -> Unit = {}
) : ISynchable, MutableSet<T> {
constructor(factory: () -> T) : this({ factory() }, {})
private inner class RemoteState(val listener: Runnable) : IRemoteState {
private inner class RemoteSlot(val slot: Slot<T>, fromConstructor: Boolean) : Runnable, Closeable {
val remoteState = synchable(slot.value).createRemoteState(this)
val remoteState = slot.synchable.createRemoteState(this)
val isDirty = AtomicBoolean(true)
var isRemoved = false
private set
@ -112,7 +91,6 @@ class DynamicSynchableGroup<T : Any>(
checkNotNull(removed)
check(removed.slot == element)
removed.close()
removals.add(element.id)
listener.run()
}
@ -136,7 +114,7 @@ class DynamicSynchableGroup<T : Any>(
firstTime.forEach {
stream.writeByte(ADD_ENTRY)
stream.writeVarInt(it.slot.id)
writer(it.slot.value, stream)
writer(it.slot.synchable, stream)
}
firstTime.clear()
@ -199,12 +177,11 @@ class DynamicSynchableGroup<T : Any>(
}
}
private class Slot<T : Any>(val value: T, val id: Int)
private data class Slot<T : Any>(val synchable: T, val id: Int)
private var defaultReturnValue: T? = null
private val remoteStates = ArrayList<RemoteState>()
private val value2slot = Reference2ObjectLinkedOpenHashMap<T, Slot<T>>()
private val id2slot = Int2ReferenceLinkedOpenHashMap<Slot<T>>()
private val value2slot = HashMap<T, Slot<T>>()
private val id2slot = Int2ObjectOpenHashMap<Slot<T>>()
private val idAllocator = IDAllocator()
override val hasRemotes: Boolean
@ -222,7 +199,7 @@ class DynamicSynchableGroup<T : Any>(
REMOVE_ENTRY -> {
val id = stream.readVarInt()
val slot = checkNotNull(id2slot.remove(id)) { "No such slot with ID: $id" }
check(value2slot.remove(slot.value) == slot)
check(value2slot.remove(slot.synchable) == value2slot)
remoteStates.forEach { it.remove(slot) }
}
@ -235,7 +212,7 @@ class DynamicSynchableGroup<T : Any>(
if (id2slot.containsKey(id)) {
val slot = id2slot.remove(id)!!
check(value2slot.remove(slot.value) == slot)
check(value2slot.remove(slot.synchable) == value2slot)
remoteStates.forEach { it.remove(slot) }
}
@ -248,7 +225,7 @@ class DynamicSynchableGroup<T : Any>(
SYNC_ENTRY -> {
val id = stream.readVarInt()
val slot = checkNotNull(id2slot.get(id)) { "No such slot with ID: $id" }
synchable(slot.value).read(stream)
slot.synchable.read(stream)
}
CLEAR -> {
@ -310,8 +287,8 @@ class DynamicSynchableGroup<T : Any>(
return value2slot.isEmpty()
}
override fun iterator(): ObjectIterator<T> {
return object : ObjectIterator<T> {
override fun iterator(): MutableIterator<T> {
return object : MutableIterator<T> {
private val parent = value2slot.values.iterator()
private var last: KOptional<Slot<T>> = KOptional()
@ -322,7 +299,7 @@ class DynamicSynchableGroup<T : Any>(
override fun next(): T {
val slot = parent.next()
last = KOptional(slot)
return slot.value
return slot.synchable
}
override fun remove() {
@ -335,15 +312,15 @@ class DynamicSynchableGroup<T : Any>(
}
}
private fun removeBySlot(slot: Slot<T>) {
value2slot.remove(slot.value)
id2slot.remove(slot.id)
override fun remove(element: T): Boolean {
if (element !in value2slot) {
return false
}
val slot = value2slot.remove(element)!!
checkNotNull(id2slot.remove(slot.id))
remoteStates.forEach { it.remove(slot) }
idAllocator.release(slot.id)
}
override fun remove(element: T): Boolean {
removeBySlot(value2slot.remove(element) ?: return false)
return true
}
@ -367,341 +344,6 @@ class DynamicSynchableGroup<T : Any>(
return any
}
/**
* Representation of this synchable group as an [Int2ReferenceMap]. Changes made to [DynamicSynchableGroup] are reflected in this map
* and vice versa. Entries can be put at arbitrary keys, but negative keys should be avoided, since they are not efficiently
* networked, and hence will create a lot of network traffic if synchables with negative keys get frequently updated.
*/
val asMap: Int2ReferenceMap<T> by lazy {
object : Int2ReferenceMap<T> {
override fun isEmpty(): Boolean {
return this@DynamicSynchableGroup.isEmpty()
}
override fun remove(key: Int): T? {
val slot = id2slot.remove(key) ?: return null
removeBySlot(slot)
return slot.value
}
override fun getOrDefault(key: Int, defaultValue: T): T {
return super.getOrDefault(key, defaultValue)
}
override val size: Int
get() = this@DynamicSynchableGroup.size
override fun put(key: Int, value: T): T? {
val slot = Slot(value, key)
val oldSlot = id2slot.put(key, slot)
if (oldSlot?.value === value)
return value
if (oldSlot != null) {
remoteStates.forEach { it.remove(oldSlot) }
value2slot.remove(oldSlot.value)
} else {
idAllocator.allocate(key)
}
value2slot[value] = slot
remoteStates.forEach { it.add(slot) }
return oldSlot?.value
}
override fun get(key: Int): T? {
return id2slot.get(key)?.value ?: defaultReturnValue
}
override fun containsKey(key: Int): Boolean {
return id2slot.containsKey(key)
}
override fun defaultReturnValue(rv: T?) {
defaultReturnValue = rv
}
override fun defaultReturnValue(): T? {
return defaultReturnValue
}
override fun putAll(from: Map<out Int, T>) {
from.forEach { (k, v) ->
put(k, v)
}
}
override fun containsValue(value: T): Boolean {
return value2slot.containsKey(value)
}
private val entrySet: ObjectSet<Int2ReferenceMap.Entry<T>> by lazy(LazyThreadSafetyMode.PUBLICATION) {
object : ObjectSet<Int2ReferenceMap.Entry<T>> {
inner class Entry(private val ikey: Int) : Int2ReferenceMap.Entry<T> {
override val value: T
get() = id2slot[ikey]?.value ?: throw NoSuchElementException()
override fun setValue(newValue: T): T? {
return put(ikey, newValue)
}
override fun getIntKey(): Int {
return ikey
}
override fun equals(other: Any?): Boolean {
return this === other || other is Int2ReferenceMap.Entry<*> && other.intKey == ikey && other.value === id2slot[ikey]?.value
}
override fun hashCode(): Int {
return ikey * 31 + (id2slot[ikey] as Slot<T>?).hashCode()
}
override fun toString(): String {
return "Map.Entry[$ikey, ${id2slot[ikey]?.value}]"
}
}
override val size: Int
get() = id2slot.size
override fun add(element: Int2ReferenceMap.Entry<T>): Boolean {
return put(element.intKey, element.value) != null
}
override fun addAll(elements: Collection<Int2ReferenceMap.Entry<T>>): Boolean {
var any = false
elements.forEach { any = add(it) || any }
return any
}
override fun clear() {
this@DynamicSynchableGroup.clear()
}
override fun iterator(): ObjectIterator<Int2ReferenceMap.Entry<T>> {
return object : ObjectIterator<Int2ReferenceMap.Entry<T>> {
private val parent = id2slot.int2ReferenceEntrySet().iterator()
private var last: Slot<T>? = null
override fun hasNext(): Boolean {
return parent.hasNext()
}
override fun remove() {
if (last == null)
throw NoSuchElementException()
parent.remove()
removeBySlot(last!!)
last = null
}
override fun next(): Int2ReferenceMap.Entry<T> {
val value = parent.next()
last = value.value
return Entry(value.intKey)
}
}
}
override fun remove(element: Int2ReferenceMap.Entry<T>): Boolean {
val slot = id2slot.get(element.intKey)
if (slot?.value === element.value) {
this@DynamicSynchableGroup.removeBySlot(slot)
return true
}
return false
}
override fun removeAll(elements: Collection<Int2ReferenceMap.Entry<T>>): Boolean {
var any = false
elements.forEach { any = remove(it) || any }
return any
}
override fun retainAll(elements: Collection<Int2ReferenceMap.Entry<T>>): Boolean {
if (elements.isEmpty()) {
val isNotEmpty = id2slot.isNotEmpty()
this@DynamicSynchableGroup.clear()
return isNotEmpty
}
var any = false
val itr = id2slot.int2ReferenceEntrySet().iterator()
for (entry in itr) {
if (elements.none { it.intKey == entry.intKey && it.value === entry.value.value }) {
any = true
itr.remove()
removeBySlot(entry.value)
}
}
return any
}
override fun contains(element: Int2ReferenceMap.Entry<T>): Boolean {
return id2slot.get(element.intKey)?.value === element.value
}
override fun containsAll(elements: Collection<Int2ReferenceMap.Entry<T>>): Boolean {
return elements.all { contains(it) }
}
override fun isEmpty(): Boolean {
return id2slot.isEmpty()
}
}
}
override fun int2ReferenceEntrySet(): ObjectSet<Int2ReferenceMap.Entry<T>> {
return entrySet
}
override val keys: IntSet by lazy(LazyThreadSafetyMode.PUBLICATION) {
object : IntSet {
override fun add(key: Int): Boolean {
throw UnsupportedOperationException()
}
override fun addAll(c: IntCollection): Boolean {
throw UnsupportedOperationException()
}
override fun addAll(elements: Collection<Int>): Boolean {
throw UnsupportedOperationException()
}
override fun clear() {
this@DynamicSynchableGroup.clear()
}
override fun iterator(): IntIterator {
return object : IntIterator {
private val parent = id2slot.keys.iterator()
private var last = -1
private var hasLast = false
override fun hasNext(): Boolean {
return parent.hasNext()
}
override fun remove() {
if (!hasLast)
throw NoSuchElementException()
hasLast = false
val slot = id2slot.get(last)
parent.remove()
removeBySlot(slot)
}
override fun nextInt(): Int {
last = parent.nextInt()
hasLast = true
return last
}
}
}
override fun remove(k: Int): Boolean {
removeBySlot(id2slot.get(k) ?: return false)
return true
}
override fun removeAll(c: IntCollection): Boolean {
val itr = c.iterator()
var any = false
while (itr.hasNext()) {
any = remove(itr.nextInt()) || any
}
return any
}
override fun removeAll(elements: Collection<Int>): Boolean {
val itr = elements.iterator()
var any = false
while (itr.hasNext()) {
any = remove(itr.next()) || any
}
return any
}
override fun retainAll(c: IntCollection): Boolean {
val itr = id2slot.values.iterator()
var any = false
for (slot in itr) {
if (!c.contains(slot.id)) {
itr.remove()
check(value2slot.remove(slot.value) == slot)
remoteStates.forEach { it.remove(slot) }
any = true
}
}
return any
}
override fun retainAll(elements: Collection<Int>): Boolean {
val itr = id2slot.values.iterator()
var any = false
for (slot in itr) {
if (!elements.contains(slot.id)) {
itr.remove()
check(value2slot.remove(slot.value) == slot)
remoteStates.forEach { it.remove(slot) }
any = true
}
}
return any
}
override fun contains(key: Int): Boolean {
return id2slot.containsKey(key)
}
override fun containsAll(c: IntCollection): Boolean {
return id2slot.keys.containsAll(c)
}
override fun containsAll(elements: Collection<Int>): Boolean {
return id2slot.keys.containsAll(elements)
}
override fun isEmpty(): Boolean {
return id2slot.isEmpty()
}
override fun toArray(a: IntArray?): IntArray {
return id2slot.keys.toArray(a)
}
override fun toIntArray(): IntArray {
return id2slot.keys.toIntArray()
}
override val size: Int
get() = id2slot.size
}
}
override val values: ReferenceCollection<T>
get() = this@DynamicSynchableGroup
}
}
companion object {
private const val END = 0
private const val ADD_ENTRY = 1

View File

@ -51,6 +51,15 @@ object MBuiltInRegistries {
val ANDROID_FEATURE by Delegate(MRegistries.ANDROID_FEATURE) { sync(true) }
val STACK_TYPE by Delegate(MRegistries.STACK_TYPE)
val BOOLEAN_PROVIDER by Delegate(MRegistries.BOOLEAN_PROVIDER) {
defaultKey(ResourceLocation(OverdriveThatMatters.MOD_ID, "false"))
}
val FEATURE by Delegate(MRegistries.FEATURE)
val PLACEMENT_MODIFIER by Delegate(MRegistries.PLACEMENT_MODIFIER) {
defaultKey(ResourceLocation(OverdriveThatMatters.MOD_ID, "passthrough"))
}
val ITEM_FILTER by Delegate(MRegistries.ITEM_FILTER) {
defaultKey(ResourceLocation(OverdriveThatMatters.MOD_ID, "empty"))
}

View File

@ -5,6 +5,7 @@ import net.minecraft.resources.ResourceKey
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.mc.otm.util.ResourceLocation
import ru.dbotthepony.mc.otm.data.world.BooleanProvider
import ru.dbotthepony.mc.otm.data.world.DecimalProvider
import ru.dbotthepony.mc.otm.matter.AbstractRegistryAction
import ru.dbotthepony.mc.otm.matter.IMatterFunction
@ -13,6 +14,8 @@ import ru.dbotthepony.mc.otm.player.android.AndroidFeatureType
import ru.dbotthepony.mc.otm.player.android.AndroidResearchDescription
import ru.dbotthepony.mc.otm.player.android.AndroidResearchResult
import ru.dbotthepony.mc.otm.storage.StorageStack
import ru.dbotthepony.mc.otm.server.world.feature.EnhancedFeature
import ru.dbotthepony.mc.otm.server.world.placement.EnhancedPlacement
object MRegistries {
private fun <T> k(name: String): ResourceKey<Registry<T>> {
@ -27,5 +30,9 @@ object MRegistries {
val ANDROID_RESEARCH_RESULT = k<AndroidResearchResult.Type<*>>("android_research_result")
val ANDROID_FEATURE = k<AndroidFeatureType<*>>("android_feature")
val STACK_TYPE = k<StorageStack.Type<*>>("stack_type")
val BOOLEAN_PROVIDER = k<BooleanProvider.Type<*>>("boolean_provider")
val FEATURE = k<EnhancedFeature<*>>("feature")
val CONFIGURED_FEATURE = k<EnhancedFeature.Configured<*, *>>("configured_feature")
val PLACEMENT_MODIFIER = k<EnhancedPlacement.Type<*>>("placement_modifier")
val ITEM_FILTER = k<ItemFilter.Type<*>>("item_filter")
}

View File

@ -36,7 +36,6 @@ import ru.dbotthepony.mc.otm.block.decorative.CargoCrateBlock
import ru.dbotthepony.mc.otm.block.decorative.ComputerTerminalBlock
import ru.dbotthepony.mc.otm.block.decorative.StarChairBlock
import ru.dbotthepony.mc.otm.block.decorative.TritaniumPressurePlate
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.matteryEnergy
import ru.dbotthepony.mc.otm.client.MatteryGUI
import ru.dbotthepony.mc.otm.util.ResourceLocation
@ -318,21 +317,6 @@ object MRegistry : IBlockItemRegistryAcceptor {
val tag = stack.getOrDefault(MDataComponentTypes.Configurator.CONFIGURATION, CompoundTag())
if (tag.isEmpty) 0f else 1f
}
for (item in MItems.MATTER_CAPACITORS) {
ItemProperties.register(item, ResourceLocation(OverdriveThatMatters.MOD_ID, "capacitor_gauge")) { stack, _, _, _ ->
val cap = stack.getCapability(MatteryCapability.MATTER_ITEM) ?: return@register 1f
(cap.storedMatter / cap.maxStoredMatter).toFloat()
}
}
for (item in MItems.ALL_BATTERIES.toMutableList().also { it.add(MItems.PROCEDURAL_BATTERY) }) {
ItemProperties.register(item, ResourceLocation(OverdriveThatMatters.MOD_ID, "capacitor_gauge")) { stack, _, _, _ ->
val energy = stack.matteryEnergy ?: return@register 1f
(energy.batteryLevel / energy.maxBatteryLevel).toFloat()
}
}
}
}

View File

@ -3,7 +3,7 @@ package ru.dbotthepony.mc.otm.registry.data
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.world.level.levelgen.heightproviders.HeightProviderType
import net.neoforged.bus.api.IEventBus
import ru.dbotthepony.mc.otm.data.world.StandardDeviationHeightProvider
import ru.dbotthepony.mc.otm.server.world.placement.StandardDeviationHeightProvider
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
object MHeightProviders {

View File

@ -0,0 +1,22 @@
package ru.dbotthepony.mc.otm.registry.data
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.util.valueproviders.FloatProviderType
import net.minecraft.util.valueproviders.IntProviderType
import net.neoforged.bus.api.IEventBus
import ru.dbotthepony.mc.otm.data.world.OneOfFloatProvider
import ru.dbotthepony.mc.otm.data.world.OneOfIntProvider
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
object MNumberProviders {
private val floats = MDeferredRegister(BuiltInRegistries.FLOAT_PROVIDER_TYPE)
private val ints = MDeferredRegister(BuiltInRegistries.INT_PROVIDER_TYPE)
fun register(bus: IEventBus) {
floats.register(bus)
ints.register(bus)
}
val ONE_OF_FLOAT by floats.register("one_of_float") { FloatProviderType { OneOfFloatProvider.CODEC } }
val ONE_OF_INT by ints.register("one_of_int") { IntProviderType { OneOfIntProvider.CODEC } }
}

View File

@ -3,15 +3,37 @@ package ru.dbotthepony.mc.otm.registry.data
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.world.level.levelgen.placement.PlacementModifierType
import net.neoforged.bus.api.IEventBus
import ru.dbotthepony.mc.otm.data.world.EllipsoidPlacement
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
import ru.dbotthepony.mc.otm.registry.MRegistries
import ru.dbotthepony.mc.otm.server.world.feature.EnhancedFeature
import ru.dbotthepony.mc.otm.server.world.placement.ChainPlacement
import ru.dbotthepony.mc.otm.server.world.placement.EllipsoidPlacement
import ru.dbotthepony.mc.otm.server.world.placement.EnhancedChainPlacement
import ru.dbotthepony.mc.otm.server.world.placement.EnhancedCountPlacement
import ru.dbotthepony.mc.otm.server.world.placement.EnhancedSplitPlacement
import ru.dbotthepony.mc.otm.server.world.placement.SplitPlacement
import ru.dbotthepony.mc.otm.server.world.placement.WormPlacement
object MPlacementModifiers {
private val registry = MDeferredRegister(BuiltInRegistries.PLACEMENT_MODIFIER_TYPE)
private val enhancedRegistry = MDeferredRegister(MRegistries.PLACEMENT_MODIFIER)
internal fun register(bus: IEventBus) {
registry.register(bus)
enhancedRegistry.register(bus)
}
val ELLIPSOID_PLACEMENT by registry.register("ellipsoid") { PlacementModifierType { EllipsoidPlacement.CODEC } }
val WORM_PLACEMENT by registry.register("worm") { PlacementModifierType { WormPlacement.CODEC } }
val SPLIT by registry.register("split") { PlacementModifierType { SplitPlacement.CODEC} }
val CHAIN by registry.register("chain") { PlacementModifierType { ChainPlacement.CODEC } }
init {
enhancedRegistry.register("feature") { EnhancedFeature.ConfiguredWrapper.Companion }
enhancedRegistry.register("ellipsoid") { EllipsoidPlacement.Companion }
enhancedRegistry.register("worm") { WormPlacement.Companion }
enhancedRegistry.register("split") { EnhancedSplitPlacement.Companion }
enhancedRegistry.register("chain") { EnhancedChainPlacement.Companion }
enhancedRegistry.register("count") { EnhancedCountPlacement.Companion }
}
}

View File

@ -2,8 +2,11 @@ package ru.dbotthepony.mc.otm.registry.data
import net.minecraft.core.registries.BuiltInRegistries
import net.neoforged.bus.api.IEventBus
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
import ru.dbotthepony.mc.otm.server.world.EnhancedPlacedFeature
import ru.dbotthepony.mc.otm.server.world.feature.BlackHolePlacerFeature
import ru.dbotthepony.mc.otm.server.world.feature.DebugPlacerFeature
object MWorldGenFeatures {
private val registry = MDeferredRegister(BuiltInRegistries.FEATURE)
@ -13,4 +16,6 @@ object MWorldGenFeatures {
}
val BLACK_HOLE_PLACER by registry.register("black_hole_placer") { BlackHolePlacerFeature }
val DEBUG_PLACEMENT by registry.register("debug") { DebugPlacerFeature }
val ENHANCED_FEATURE by registry.register("enhanced_feature") { EnhancedPlacedFeature }
}

View File

@ -223,7 +223,7 @@ private fun addMainCreativeTabItems(consumer: CreativeModeTab.Output) {
accept(MItems.GRILL.values)
accept(MItems.MATTER_DUST.makeStack())
// accept(MItems.MATTER_DUST)
accept(MItems.TRITANIUM_ORE)
accept(MItems.DEEPSLATE_TRITANIUM_ORE)

View File

@ -0,0 +1,180 @@
package ru.dbotthepony.mc.otm.server.world
import com.github.benmanes.caffeine.cache.Cache
import com.github.benmanes.caffeine.cache.Caffeine
import com.github.benmanes.caffeine.cache.Scheduler
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
import it.unimi.dsi.fastutil.objects.ObjectArrayList
import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet
import net.minecraft.Util
import net.minecraft.core.BlockPos
import net.minecraft.core.Holder
import net.minecraft.core.SectionPos
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.level.ChunkPos
import net.minecraft.world.level.WorldGenLevel
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature
import net.minecraft.world.level.levelgen.feature.Feature
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration
import net.minecraft.world.level.levelgen.placement.PlacedFeature
import ru.dbotthepony.kommons.util.XXHash64
import ru.dbotthepony.mc.otm.THREAD_LOCAL_RANDOM
import ru.dbotthepony.mc.otm.data.codec.minRange
import ru.dbotthepony.mc.otm.util.GJRAND64RandomSource
import ru.dbotthepony.mc.otm.util.shuffle
import ru.dbotthepony.mc.otm.server.world.feature.EnhancedFeature
import ru.dbotthepony.mc.otm.server.world.placement.EnhancedPlacement
import java.io.DataOutputStream
import java.time.Duration
import java.util.*
import kotlin.collections.ArrayList
import kotlin.math.sqrt
object EnhancedPlacedFeature : Feature<EnhancedPlacedFeature.Config>(
RecordCodecBuilder.create {
it.group(
Codec.INT.minRange(0).fieldOf("chunk_scan_range").forGetter(Config::chunkScanRange),
Codec.LONG.fieldOf("seed_mix").forGetter(Config::seedMix),
EnhancedPlacement.CODEC.fieldOf("root").forGetter(Config::root),
).apply(it, ::Config)
}
) {
private val emptyVariableMap = PlacementVariableMap()
fun configure(
chunkScanRange: Int,
seedMix: Long,
root: EnhancedPlacement
): PlacedFeature {
return PlacedFeature(Holder.direct(ConfiguredFeature(EnhancedPlacedFeature, Config(chunkScanRange, seedMix, root))), listOf())
}
class Config(
val chunkScanRange: Int,
val seedMix: Long,
val root: EnhancedPlacement,
) : FeatureConfiguration {
private class GeneratedChunk : EnhancedPlacementContext.Placer {
private data class Placement(val context: EnhancedPlacementContext, val positions: TreeSet<PlacementPos>, val feature: EnhancedFeature.Configured<*, *>)
private val placed = LinkedList<Placement>()
override fun place(context: EnhancedPlacementContext, positions: List<PlacementPos>, feature: EnhancedFeature.Configured<*, *>) {
if (positions.isNotEmpty()) {
placed.add(Placement(context, TreeSet<PlacementPos>().also { it.addAll(positions) }, feature))
}
}
fun place(context: FeaturePlaceContext<*>): Boolean {
var any = false
val pos = ChunkPos(context.origin())
for ((eContext, positions, feature) in placed) {
val itr = positions.tailSet(PlacementPos(BlockPos(pos.minBlockX, Int.MIN_VALUE, pos.minBlockZ), emptyVariableMap)).iterator()
if (!itr.hasNext())
continue
val result = TreeSet<PlacementPos>()
for (placementPos in itr) {
if (SectionPos.blockToSectionCoord(placementPos.pos.x) != pos.x || SectionPos.blockToSectionCoord(placementPos.pos.z) != pos.z)
break
result.add(placementPos)
}
if (result.isNotEmpty()) {
any = feature.place(eContext.push(context.level()), result, positions) || any
}
}
return any
}
}
private val level2cache = Caffeine.newBuilder()
.scheduler(Scheduler.systemScheduler())
.executor(Util.backgroundExecutor())
.expireAfterAccess(Duration.ofMinutes(10))
.weakKeys()
.build<ServerLevel, Cache<ChunkPos, GeneratedChunk>>()
private fun getCache(level: WorldGenLevel): Cache<ChunkPos, GeneratedChunk> {
return level2cache.get(level.level) {
Caffeine.newBuilder()
.scheduler(Scheduler.systemScheduler())
.executor(Util.backgroundExecutor())
.maximumSize(16384L)
.expireAfterWrite(Duration.ofMinutes(5))
.softValues()
.build()
}
}
private fun evaluate(context: FeaturePlaceContext<*>, chunkPos: ChunkPos): GeneratedChunk {
val bytes = FastByteArrayOutputStream()
val dataStream = DataOutputStream(bytes)
dataStream.writeLong(seedMix)
dataStream.writeInt(chunkPos.x)
dataStream.writeInt(chunkPos.z)
val hash = XXHash64()
hash.update(bytes.array, 0, bytes.length)
val random = GJRAND64RandomSource(context.level().seed, hash.digestAsLong())
val chunk = GeneratedChunk()
val enhancedContext = EnhancedPlacementContext(context.level(), context.chunkGenerator(), random, chunkPos, chunk)
root.evaluate(enhancedContext, listOf(PlacementPos(BlockPos(chunkPos.minBlockX, 0, chunkPos.minBlockZ), PlacementVariableMap())))
return chunk
}
fun place(context: FeaturePlaceContext<*>): Boolean {
val cache = getCache(context.level())
val chunkPos = ChunkPos(context.origin())
val chunkPositions = ArrayList<ChunkPos>()
for (x in -chunkScanRange .. chunkScanRange) {
for (z in -chunkScanRange .. chunkScanRange) {
// floor, so chunk scan range of 1 will give square instead of diamond
val radius = sqrt(x.toDouble() * x + z.toDouble() * z).toInt()
if (radius <= chunkScanRange) {
val thisPos = ChunkPos(chunkPos.x + x, chunkPos.z + z)
chunkPositions.add(thisPos)
}
}
}
val withIndex = ObjectArrayList(chunkPositions.withIndex().iterator())
// evaluate chunks in random order, so in multithreaded environments we experience less congestion
// between threads which lookup cache while it is being populated on neighbouring chunks
withIndex.shuffle(THREAD_LOCAL_RANDOM)
data class P(val i: Int, val v: GeneratedChunk) : Comparable<P> {
override fun compareTo(other: P): Int {
return i.compareTo(other.i)
}
}
val instances = ArrayList<P>()
withIndex.forEach { (i, it) ->
instances.add(P(i, cache.get(it) { evaluate(context, it) }))
}
instances.sort()
var any = false
instances.forEach { any = it.v.place(context) }
return any
}
}
override fun place(context: FeaturePlaceContext<Config>): Boolean {
return context.config().place(context)
}
}

View File

@ -0,0 +1,89 @@
package ru.dbotthepony.mc.otm.server.world
import net.minecraft.core.BlockPos
import net.minecraft.util.RandomSource
import net.minecraft.world.level.ChunkPos
import net.minecraft.world.level.WorldGenLevel
import net.minecraft.world.level.chunk.ChunkGenerator
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration
import net.minecraft.world.level.levelgen.placement.PlacementContext
import ru.dbotthepony.mc.otm.server.world.feature.EnhancedFeature
import java.util.*
class EnhancedPlacementContext {
fun interface Placer {
fun place(context: EnhancedPlacementContext, positions: List<PlacementPos>, feature: EnhancedFeature.Configured<*, *>)
}
private data class SharedState(
val level: WorldGenLevel,
val generator: ChunkGenerator,
val random: RandomSource,
val origin: ChunkPos,
val placer: Placer,
val vanillaContext: PlacementContext,
)
private val state: SharedState
val level: WorldGenLevel
get() = state.level
val generator: ChunkGenerator
get() = state.generator
val random: RandomSource
get() = state.random
val origin: ChunkPos
get() = state.origin
private val placer: Placer
get() = state.placer
val vanillaContext: PlacementContext
get() = state.vanillaContext
val variables: PlacementVariableMap
constructor(
level: WorldGenLevel,
generator: ChunkGenerator,
random: RandomSource,
origin: ChunkPos,
placer: Placer,
) {
state = SharedState(
level = level,
generator = generator,
random = random,
origin = origin,
placer = placer,
vanillaContext = PlacementContext(level, generator, Optional.empty())
)
variables = PlacementVariableMap()
}
private constructor(parent: EnhancedPlacementContext) {
this.state = parent.state
this.variables = parent.variables.push()
}
private constructor(parent: EnhancedPlacementContext, context: WorldGenLevel) {
this.state = parent.state.copy(level = context)
this.variables = parent.variables.push()
}
fun push(): EnhancedPlacementContext {
return EnhancedPlacementContext(this)
}
fun place(positions: List<PlacementPos>, feature: EnhancedFeature.Configured<*, *>) {
placer.place(this, positions, feature)
}
fun <FC : FeatureConfiguration> vanillaFeatureContext(config: FC, position: BlockPos): FeaturePlaceContext<FC> {
return FeaturePlaceContext(Optional.empty(), level, generator, random, position, config)
}
fun push(context: WorldGenLevel): EnhancedPlacementContext {
return EnhancedPlacementContext(this, context)
}
}

View File

@ -0,0 +1,25 @@
package ru.dbotthepony.mc.otm.server.world
import net.minecraft.core.BlockPos
import net.minecraft.core.Holder
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature
import net.minecraft.world.level.levelgen.placement.PlacementModifier
import ru.dbotthepony.mc.otm.util.collect.Vec3iHashStrategy
import ru.dbotthepony.mc.otm.server.world.feature.EnhancedFeature
import ru.dbotthepony.mc.otm.server.world.placement.EnhancedPlacement
fun PlacementModifier.wrap(): EnhancedPlacement {
return EnhancedPlacement.Wrapper(this)
}
data class PlacementPos(val pos: BlockPos, val variables: PlacementVariableMap) : Comparable<PlacementPos> {
override fun compareTo(other: PlacementPos): Int {
return Vec3iHashStrategy.compare(pos, other.pos)
}
}
@JvmName("placement")
fun Holder<EnhancedFeature.Configured<*, *>>.placement() = EnhancedFeature.ConfiguredWrapper(this)
@JvmName("vanillaPlacement")
fun Holder<ConfiguredFeature<*, *>>.placement() = EnhancedFeature.Wrapper.configure(this)
fun ConfiguredFeature<*, *>.placement() = EnhancedFeature.Wrapper.configure(this)

View File

@ -0,0 +1,9 @@
package ru.dbotthepony.mc.otm.server.world
import net.minecraft.resources.ResourceLocation
class PlacementVariable<T>(val name: ResourceLocation) : Comparable<PlacementVariable<T>> {
override fun compareTo(other: PlacementVariable<T>): Int {
return name.compareTo(other.name)
}
}

View File

@ -0,0 +1,56 @@
package ru.dbotthepony.mc.otm.server.world
import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap
import ru.dbotthepony.kommons.util.KOptional
/**
* This is a "map stack" of sorts, and when you want to push some data to it
* you need to push new map to stack (by calling [push]) and then write new data to
* the map returned by latter.
*/
class PlacementVariableMap {
private val parent: PlacementVariableMap?
private val variables = Object2ObjectAVLTreeMap<PlacementVariable<*>, KOptional<*>>()
constructor() {
parent = null
}
private constructor(parent: PlacementVariableMap) {
this.parent = parent
}
private fun <T> recursiveGet(index: PlacementVariable<T>): KOptional<T>? {
return variables[index] as KOptional<T>? ?: parent?.recursiveGet(index)
}
operator fun <T> get(index: PlacementVariable<T>): KOptional<T> {
return recursiveGet(index) ?: KOptional()
}
operator fun <T> set(index: PlacementVariable<T>, value: T): PlacementVariableMap {
variables[index] = KOptional(value)
return this
}
fun <T> remove(index: PlacementVariable<T>): KOptional<T> {
val old = variables.put(index, KOptional<T>())
return old as KOptional<T>? ?: KOptional()
}
fun remove(index: Collection<PlacementVariable<*>>) {
index.forEach { remove(it) }
}
fun push(): PlacementVariableMap {
return PlacementVariableMap(this)
}
override fun equals(other: Any?): Boolean {
return other === this || other is PlacementVariableMap && variables == other.variables && parent == other.parent
}
override fun hashCode(): Int {
return parent.hashCode() * 31 + variables.hashCode() + 5
}
}

View File

@ -0,0 +1,16 @@
package ru.dbotthepony.mc.otm.server.world.feature
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.levelgen.feature.Feature
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration
import ru.dbotthepony.mc.otm.registry.game.MBlocks
object DebugPlacerFeature : Feature<DebugPlacerFeature.Config>(BlockState.CODEC.xmap(::Config, Config::blockState)) {
data class Config(val blockState: BlockState) : FeatureConfiguration
override fun place(context: FeaturePlaceContext<Config>): Boolean {
return context.level().setBlock(context.origin(), context.config().blockState, Block.UPDATE_CLIENTS)
}
}

View File

@ -0,0 +1,89 @@
package ru.dbotthepony.mc.otm.server.world.feature
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
import net.minecraft.core.BlockPos
import net.minecraft.core.Holder
import net.minecraft.resources.RegistryFileCodec
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature
import net.neoforged.bus.api.IEventBus
import net.neoforged.neoforge.registries.DataPackRegistryEvent
import ru.dbotthepony.mc.otm.registry.MBuiltInRegistries
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
import ru.dbotthepony.mc.otm.registry.MRegistries
import ru.dbotthepony.mc.otm.server.world.EnhancedPlacementContext
import ru.dbotthepony.mc.otm.server.world.PlacementPos
import ru.dbotthepony.mc.otm.server.world.placement.EnhancedPlacement
abstract class EnhancedFeature<FC>(codec: Codec<FC>) {
abstract fun place(context: EnhancedPlacementContext, config: FC, positions: Set<PlacementPos>, allPositions: Set<PlacementPos>): Boolean
data class Configured<F : EnhancedFeature<FC>, FC>(val feature: F, val config: FC) {
fun place(context: EnhancedPlacementContext, positions: Set<PlacementPos>, allPositions: Set<PlacementPos>): Boolean {
return feature.place(context, config, positions, allPositions)
}
fun placement(): ConfiguredWrapper {
return ConfiguredWrapper(Holder.direct(this))
}
}
data class ConfiguredWrapper(val configured: Holder<Configured<*, *>>) : EnhancedPlacement {
override fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos> {
context.place(positions, configured.value())
return positions
}
override val type: EnhancedPlacement.Type<*>
get() = Companion
companion object : EnhancedPlacement.Type<ConfiguredWrapper> {
override val codec: MapCodec<ConfiguredWrapper> by lazy {
CODEC.xmap(::ConfiguredWrapper, ConfiguredWrapper::configured).fieldOf("feature")
}
}
}
val codec: MapCodec<Configured<*, FC>> = codec.fieldOf("config").xmap({ Configured(this, it) }, { it.config })
fun configure(config: FC) = Configured(this, config).placement()
object Wrapper : EnhancedFeature<Holder<ConfiguredFeature<*, *>>>(ConfiguredFeature.CODEC) {
override fun place(
context: EnhancedPlacementContext,
config: Holder<ConfiguredFeature<*, *>>,
positions: Set<PlacementPos>,
allPositions: Set<PlacementPos>
): Boolean {
var any = false
positions.forEach { any = config.value().place(context.level, context.generator, context.random, it.pos) || any }
return any
}
fun configure(config: ConfiguredFeature<*, *>) = configure(Holder.direct(config))
}
companion object {
private val registrar = MDeferredRegister(MRegistries.FEATURE)
val DIRECT_CODEC: Codec<Configured<*, *>> by lazy {
MBuiltInRegistries.FEATURE.byNameCodec().dispatch({ it.feature }, { it.codec })
}
val CODEC: Codec<Holder<Configured<*, *>>> by lazy {
RegistryFileCodec.create(MRegistries.CONFIGURED_FEATURE, DIRECT_CODEC)
}
init {
registrar.register("wrapper") { Wrapper }
}
private fun registerRegistry(event: DataPackRegistryEvent.NewRegistry) {
event.dataPackRegistry(MRegistries.CONFIGURED_FEATURE, DIRECT_CODEC)
}
internal fun register(bus: IEventBus) {
registrar.register(bus)
bus.addListener(::registerRegistry)
}
}
}

View File

@ -0,0 +1,35 @@
package ru.dbotthepony.mc.otm.server.world.placement
import com.google.common.collect.ImmutableList
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
import net.minecraft.core.BlockPos
import net.minecraft.util.RandomSource
import net.minecraft.world.level.levelgen.placement.PlacementContext
import net.minecraft.world.level.levelgen.placement.PlacementModifier
import net.minecraft.world.level.levelgen.placement.PlacementModifierType
import ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers
import java.util.stream.Stream
/**
* Daisy-chaining placements. Required only when using placement modifiers which operate on children list
*/
class ChainPlacement(
val children: List<PlacementModifier>
) : PlacementModifier() {
constructor(vararg children: PlacementModifier) : this(ImmutableList.copyOf(children))
override fun getPositions(context: PlacementContext, random: RandomSource, pos: BlockPos): Stream<BlockPos> {
var stream = Stream.of(pos)
children.forEach { c -> stream = stream.flatMap { c.getPositions(context, random, it) } }
return stream
}
override fun type(): PlacementModifierType<*> {
return MPlacementModifiers.CHAIN
}
companion object {
val CODEC: MapCodec<ChainPlacement> = Codec.list(PlacementModifier.CODEC).xmap(::ChainPlacement, ChainPlacement::children).fieldOf("children")
}
}

View File

@ -0,0 +1,179 @@
package ru.dbotthepony.mc.otm.server.world.placement
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.core.BlockPos
import net.minecraft.util.RandomSource
import net.minecraft.util.valueproviders.ConstantFloat
import net.minecraft.util.valueproviders.FloatProvider
import net.minecraft.util.valueproviders.IntProvider
import net.minecraft.world.level.levelgen.placement.PlacementContext
import net.minecraft.world.level.levelgen.placement.PlacementModifier
import net.minecraft.world.level.levelgen.placement.PlacementModifierType
import ru.dbotthepony.mc.otm.util.collect.Vec3iHashStrategy
import ru.dbotthepony.mc.otm.data.world.BooleanProvider
import ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers
import ru.dbotthepony.mc.otm.util.math.VECTOR_POSITIVE_X
import ru.dbotthepony.mc.otm.util.math.VECTOR_POSITIVE_Z
import ru.dbotthepony.mc.otm.util.math.VECTOR_UP
import ru.dbotthepony.mc.otm.util.math.Vector
import ru.dbotthepony.mc.otm.util.math.rotate
import ru.dbotthepony.mc.otm.util.math.rotateAroundThis
import ru.dbotthepony.mc.otm.util.nextDouble
import ru.dbotthepony.mc.otm.server.world.EnhancedPlacementContext
import ru.dbotthepony.mc.otm.server.world.PlacementPos
import java.util.*
import java.util.stream.Stream
import kotlin.math.PI
/**
* Ellipsoid ("cloud") placement
*/
data class EllipsoidPlacement(
/**
* X position within ellipsoid sampler, expected to be in range -1 .. 1
*/
val x: FloatProvider,
/**
* Z position within ellipsoid sampler, expected to be in range -1 .. 1
*/
val z: FloatProvider,
/**
* Y position within ellipsoid sampler, expected to be in range -1 .. 1
*/
val y: FloatProvider,
/**
* Total amount of block positions to generate
*/
val count: IntProvider,
/**
* Ellipsoid size sampler on X axis
*/
val xLength: FloatProvider,
/**
* Ellipsoid size sampler on Z axis
*/
val zLength: FloatProvider,
/**
* Ellipsoid size sampler on Y axis
*/
val yLength: FloatProvider,
val rotateXZ: BooleanProvider = BooleanProvider.AlwaysFalse,
val rotateXY: FloatProvider = ConstantFloat.ZERO,
val rotateZY: FloatProvider = ConstantFloat.ZERO,
) : PlacementModifier(), EnhancedPlacement {
init {
require(xLength.minValue >= 1f) { "Bad ellipsoid x minimal size: $xLength" }
require(zLength.minValue >= 1f) { "Bad ellipsoid z minimal size: $zLength" }
require(yLength.minValue >= 1f) { "Bad ellipsoid y minimal size: $yLength" }
}
private fun evaluate(random: RandomSource, position: BlockPos): Set<BlockPos> {
val count = count.sample(random)
val results = TreeSet<BlockPos>(Vec3iHashStrategy)
if (count <= 0)
return results
val xLength = xLength.sample(random)
val zLength = zLength.sample(random)
val yLength = yLength.sample(random)
val xPow = xLength * xLength
val zPow = zLength * zLength
val yPow = yLength * yLength
var iterations = 0
val maxIterations = count * 10
val rotateXZ = rotateXZ.instance().sample(random)
val rotateXY = rotateXY.sample(random)
val rotateZY = rotateZY.sample(random)
val rotationXZ: Double
if (rotateXZ) {
rotationXZ = random.nextDouble(-PI, PI)
} else {
rotationXZ = 0.0
}
val qXZ = VECTOR_UP.rotateAroundThis(rotationXZ)
val qXY = VECTOR_POSITIVE_Z.rotateAroundThis(rotateXY)
val qZY = VECTOR_POSITIVE_X.rotateAroundThis(rotateZY)
while (results.size < count && ++iterations < maxIterations) {
val x = this.x.sample(random) * xLength
val y = this.y.sample(random) * yLength
val z = this.z.sample(random) * zLength
val isValidPoint = (x * x) / xPow +
(y * y) / yPow +
(z * z) / zPow <= 1.0f
if (isValidPoint) {
var point = Vector(x.toDouble(), y.toDouble(), z.toDouble())
point = point.rotate(qXZ)
point = point.rotate(qXY)
point = point.rotate(qZY)
results.add(BlockPos(point.x.toInt() + position.x, point.y.toInt() + position.y, point.z.toInt() + position.z))
}
}
return results
}
override fun getPositions(
context: PlacementContext,
random: RandomSource,
position: BlockPos
): Stream<BlockPos> {
return evaluate(random, position).stream()
}
override fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos> {
val result = ArrayList<PlacementPos>()
val results = positions.map { (it, c) -> evaluate(context.random, it).map { PlacementPos(it, c) } }
result.ensureCapacity(result.size + results.sumOf { it.size } + 1)
results.forEach { result.addAll(it) }
return result
}
override val type: EnhancedPlacement.Type<*>
get() = Companion
override fun type(): PlacementModifierType<*> {
return MPlacementModifiers.ELLIPSOID_PLACEMENT
}
companion object : EnhancedPlacement.Type<EllipsoidPlacement> {
val CODEC: MapCodec<EllipsoidPlacement> by lazy {
RecordCodecBuilder.mapCodec {
it.group(
FloatProvider.CODEC.fieldOf("x").forGetter(EllipsoidPlacement::x),
FloatProvider.CODEC.fieldOf("y").forGetter(EllipsoidPlacement::y),
FloatProvider.CODEC.fieldOf("z").forGetter(EllipsoidPlacement::z),
IntProvider.CODEC.fieldOf("count").forGetter(EllipsoidPlacement::count),
FloatProvider.codec(1f, Float.MAX_VALUE).fieldOf("x_size").forGetter(EllipsoidPlacement::xLength),
FloatProvider.codec(1f, Float.MAX_VALUE).fieldOf("z_size").forGetter(EllipsoidPlacement::zLength),
FloatProvider.codec(1f, Float.MAX_VALUE).fieldOf("y_size").forGetter(EllipsoidPlacement::yLength),
BooleanProvider.CODEC.optionalFieldOf("rotateXZ", BooleanProvider.AlwaysFalse).forGetter(EllipsoidPlacement::rotateXZ),
FloatProvider.CODEC.optionalFieldOf("rotateXY", ConstantFloat.ZERO).forGetter(EllipsoidPlacement::rotateXY),
FloatProvider.CODEC.optionalFieldOf("rotateZY", ConstantFloat.ZERO).forGetter(EllipsoidPlacement::rotateZY),
).apply(it, ::EllipsoidPlacement)
}
}
override val codec: MapCodec<EllipsoidPlacement>
get() = CODEC
}
}

View File

@ -0,0 +1,39 @@
package ru.dbotthepony.mc.otm.server.world.placement
import com.google.common.collect.ImmutableList
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
import ru.dbotthepony.mc.otm.server.world.EnhancedPlacementContext
import ru.dbotthepony.mc.otm.server.world.PlacementPos
/**
* Chains placements, feeding results from one placement into next
*
* Each placement gets its own, new [EnhancedPlacementContext], so variables can be pushed safely into it
*/
class EnhancedChainPlacement(
val children: List<EnhancedPlacement>
) : EnhancedPlacement {
constructor(vararg children: EnhancedPlacement) : this(ImmutableList.copyOf(children))
override fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos> {
var currentContext = context
var current = positions
children.forEach {
current = it.evaluate(context, current)
currentContext = currentContext.push()
}
return current
}
override val type: EnhancedPlacement.Type<*>
get() = Companion
companion object : EnhancedPlacement.Type<EnhancedChainPlacement> {
override val codec: MapCodec<EnhancedChainPlacement> by lazy {
Codec.list(EnhancedPlacement.CODEC).xmap(::EnhancedChainPlacement, EnhancedChainPlacement::children).fieldOf("children")
}
}
}

View File

@ -0,0 +1,30 @@
package ru.dbotthepony.mc.otm.server.world.placement
import com.mojang.serialization.MapCodec
import net.minecraft.util.valueproviders.IntProvider
import ru.dbotthepony.mc.otm.server.world.EnhancedPlacementContext
import ru.dbotthepony.mc.otm.server.world.PlacementPos
class EnhancedCountPlacement(val provider: IntProvider) : EnhancedPlacement {
override fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos> {
val count = provider.sample(context.random)
if (count <= 0) {
return listOf()
} else if (count == 1) {
return positions
} else {
val result = ArrayList<PlacementPos>()
result.ensureCapacity(positions.size * count)
for (i in 0 until count) result.addAll(positions)
return result
}
}
override val type: EnhancedPlacement.Type<*>
get() = Companion
companion object : EnhancedPlacement.Type<EnhancedCountPlacement> {
override val codec: MapCodec<EnhancedCountPlacement> = IntProvider.CODEC.xmap(::EnhancedCountPlacement, EnhancedCountPlacement::provider).fieldOf("count")
}
}

View File

@ -0,0 +1,64 @@
package ru.dbotthepony.mc.otm.server.world.placement
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
import net.minecraft.world.level.levelgen.placement.PlacementModifier
import net.neoforged.bus.api.IEventBus
import ru.dbotthepony.mc.otm.registry.MBuiltInRegistries
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
import ru.dbotthepony.mc.otm.registry.MRegistries
import ru.dbotthepony.mc.otm.server.world.EnhancedPlacementContext
import ru.dbotthepony.mc.otm.server.world.PlacementPos
import java.util.stream.Collectors
interface EnhancedPlacement {
interface Type<T : EnhancedPlacement> {
val codec: MapCodec<T>
}
fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos>
val type: Type<*>
class Wrapper(val parent: PlacementModifier) : EnhancedPlacement {
override fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos> {
return positions.stream()
.flatMap { (pos, vars) -> parent.getPositions(context.vanillaContext, context.random, pos).map { PlacementPos(it, vars) } }
.collect(Collectors.toCollection(::ArrayList))
}
override val type: Type<*>
get() = Companion
companion object : Type<Wrapper> {
override val codec: MapCodec<Wrapper> = PlacementModifier.CODEC.xmap(::Wrapper, Wrapper::parent).fieldOf("parent")
}
}
object Passthrough : EnhancedPlacement, Type<Passthrough> {
override fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos> {
return positions
}
override val type: Type<*>
get() = this
override val codec: MapCodec<Passthrough> = MapCodec.unit(this)
}
companion object {
private val registrar = MDeferredRegister(MRegistries.PLACEMENT_MODIFIER)
init {
registrar.register("wrapper") { Wrapper.Companion }
registrar.register("passthrough") { Passthrough }
}
val CODEC: Codec<EnhancedPlacement> by lazy {
MBuiltInRegistries.PLACEMENT_MODIFIER.byNameCodec().dispatch({ it.type }, { it.codec })
}
internal fun register(bus: IEventBus) {
registrar.register(bus)
}
}
}

View File

@ -0,0 +1,62 @@
package ru.dbotthepony.mc.otm.server.world.placement
import com.google.common.collect.ImmutableList
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.util.StringRepresentable
import net.minecraft.world.level.levelgen.placement.PlacementModifier
import ru.dbotthepony.mc.otm.server.world.EnhancedPlacementContext
import ru.dbotthepony.mc.otm.server.world.PlacementPos
/**
* Or "shard" placement, if you will.
*
* Basically, allows multiple [PlacementModifier]s to split/branch off from provided point.
*/
class EnhancedSplitPlacement(
val mode: Mode,
val children: List<EnhancedPlacement>,
) : EnhancedPlacement {
constructor(mode: Mode, vararg children: EnhancedPlacement) : this(mode, ImmutableList.copyOf(children))
enum class Mode : StringRepresentable {
COMBINE,
FORK;
private val lname = name.lowercase()
override fun getSerializedName(): String {
return lname
}
companion object {
val CODEC: Codec<Mode> = StringRepresentable.fromEnum(::values)
}
}
override fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos> {
if (mode == Mode.COMBINE) {
val result = ArrayList<PlacementPos>()
children.forEach { result.addAll(it.evaluate(context.push(), positions)) }
return result
} else {
children.forEach { it.evaluate(context.push(), positions) }
return positions
}
}
override val type: EnhancedPlacement.Type<*>
get() = Companion
companion object : EnhancedPlacement.Type<EnhancedSplitPlacement> {
override val codec: MapCodec<EnhancedSplitPlacement> by lazy {
RecordCodecBuilder.mapCodec {
it.group(
Mode.CODEC.fieldOf("mode").forGetter(EnhancedSplitPlacement::mode),
Codec.list(EnhancedPlacement.CODEC).fieldOf("children").forGetter(EnhancedSplitPlacement::children),
).apply(it, ::EnhancedSplitPlacement)
}
}
}
}

View File

@ -0,0 +1,35 @@
package ru.dbotthepony.mc.otm.server.world.placement
import com.google.common.collect.ImmutableList
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
import net.minecraft.core.BlockPos
import net.minecraft.util.RandomSource
import net.minecraft.world.level.levelgen.placement.PlacementContext
import net.minecraft.world.level.levelgen.placement.PlacementModifier
import net.minecraft.world.level.levelgen.placement.PlacementModifierType
import ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers
import java.util.stream.Stream
/**
* Or "shard" placement, if you will.
*
* Basically, allows multiple [PlacementModifier]s to split/branch off from provided point.
*/
class SplitPlacement(
val children: List<PlacementModifier>
) : PlacementModifier() {
constructor(vararg children: PlacementModifier) : this(ImmutableList.copyOf(children))
override fun getPositions(context: PlacementContext, random: RandomSource, pos: BlockPos): Stream<BlockPos> {
return children.stream().flatMap { it.getPositions(context, random, pos) }
}
override fun type(): PlacementModifierType<*> {
return MPlacementModifiers.SPLIT
}
companion object {
val CODEC: MapCodec<SplitPlacement> = Codec.list(PlacementModifier.CODEC).xmap(::SplitPlacement, SplitPlacement::children).fieldOf("children")
}
}

View File

@ -1,4 +1,4 @@
package ru.dbotthepony.mc.otm.data.world
package ru.dbotthepony.mc.otm.server.world.placement
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec

View File

@ -0,0 +1,246 @@
package ru.dbotthepony.mc.otm.server.world.placement
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.core.BlockPos
import net.minecraft.util.RandomSource
import net.minecraft.util.valueproviders.ClampedNormalFloat
import net.minecraft.util.valueproviders.ConstantFloat
import net.minecraft.util.valueproviders.FloatProvider
import net.minecraft.util.valueproviders.IntProvider
import net.minecraft.util.valueproviders.UniformFloat
import net.minecraft.world.level.levelgen.placement.PlacementContext
import net.minecraft.world.level.levelgen.placement.PlacementModifier
import net.minecraft.world.level.levelgen.placement.PlacementModifierType
import ru.dbotthepony.mc.otm.data.codec.minRange
import ru.dbotthepony.mc.otm.data.world.BooleanProvider
import ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers
import ru.dbotthepony.mc.otm.util.math.Vector
import ru.dbotthepony.mc.otm.util.math.angleDifference
import ru.dbotthepony.mc.otm.util.math.normalizeAngle
import ru.dbotthepony.mc.otm.util.math.plus
import ru.dbotthepony.mc.otm.util.math.toBlockPos
import ru.dbotthepony.mc.otm.util.nextDouble
import ru.dbotthepony.mc.otm.server.world.EnhancedPlacementContext
import ru.dbotthepony.mc.otm.server.world.PlacementPos
import java.util.stream.Stream
import kotlin.math.PI
import kotlin.math.absoluteValue
import kotlin.math.cos
import kotlin.math.sign
import kotlin.math.sin
/**
* Creates one block thick "worm" positions which twists and turns
*/
class WormPlacement(
val length: IntProvider,
val turnChanceXZ: BooleanProvider,
val turnChanceXY: BooleanProvider,
val turnSpeedXZ: FloatProvider,
val turnSpeedXY: FloatProvider,
val turnRateXZ: FloatProvider,
val turnRateXY: FloatProvider,
val initialAngleXY: FloatProvider = DEFAULT_INITIAL_ANGLE_XY,
val maxTravelDown: Int = Int.MAX_VALUE,
val maxTravelUp: Int = Int.MAX_VALUE,
) : PlacementModifier(), EnhancedPlacement {
private inner class Worm(private val random: RandomSource, private var position: Vector) {
private var remainingDistance = length.sample(random)
private var xzRotation = random.nextDouble(-PI, PI)
private var xyRotation = normalizeAngle(initialAngleXY.sample(random).toDouble())
private var xzTargetRotation = random.nextDouble(-PI, PI)
private var xyTargetRotation = normalizeAngle(initialAngleXY.sample(random).toDouble())
private var xzSin = sin(xzRotation)
private var xzCos = cos(xzRotation)
private var xySin = sin(xyRotation)
private val turnChanceXZ = this@WormPlacement.turnChanceXZ.instance()
private val turnChanceXY = this@WormPlacement.turnChanceXY.instance()
val hasFinished: Boolean
get() = remainingDistance <= 0
private var prevPos = BlockPos.ZERO
fun follow(): BlockPos? {
if (hasFinished) return null
remainingDistance--
// wormy turn
if (turnChanceXZ.sample(random)) {
// wormy angle
xzTargetRotation += turnRateXZ.sample(random)
xzTargetRotation = normalizeAngle(xzTargetRotation)
}
// wormy-o!
if (turnChanceXY.sample(random)) {
// worm?
xyTargetRotation += turnRateXY.sample(random)
xyTargetRotation = normalizeAngle(xyTargetRotation)
}
if (xzTargetRotation != xzRotation) {
val diff = angleDifference(xzTargetRotation, xzRotation)
val abs = diff.absoluteValue
val speed = turnSpeedXZ.sample(random).toDouble()
if (abs <= speed)
xzRotation = xzTargetRotation
else
xzRotation += speed * diff.sign
xzSin = sin(xzRotation)
xzCos = cos(xzRotation)
}
if (xyTargetRotation != xyRotation) {
val diff = angleDifference(xyTargetRotation, xyRotation)
val abs = diff.absoluteValue
val speed = turnSpeedXZ.sample(random).toDouble()
if (abs <= speed)
xyRotation = xyTargetRotation
else
xyRotation += speed * diff.sign
xySin = sin(xyRotation)
}
// advance worm
position += Vector(xzCos, xySin, xzSin)
if (position.y > maxTravelUp) {
xyTargetRotation = 0.0
xySin = 0.0
position = Vector(position.x, maxTravelUp.toDouble(), position.z)
} else if (position.y < -maxTravelDown) {
xyTargetRotation = 0.0
xySin = 0.0
position = Vector(position.x, -maxTravelDown.toDouble(), position.z)
}
val calc = position.toBlockPos()
if (calc != prevPos) {
// if worm made a wurm, add new position to set
prevPos = calc
return calc
}
return null
}
}
private fun evaluate(random: RandomSource, position: BlockPos): List<BlockPos> {
val worms = ArrayList<Worm>()
worms.add(Worm(random, Vector.ZERO))
val results = ArrayList<BlockPos>()
results.add(position)
while (worms.isNotEmpty()) {
worms.removeIf {
val pos = it.follow()
if (pos != null) results.add(pos + position)
it.hasFinished
}
}
return results
}
override fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos> {
if (positions.isEmpty())
return positions
else {
val result = ArrayList<PlacementPos>()
val results = positions.map { (it, c) -> evaluate(context.random, it).map { PlacementPos(it, c) } }
result.ensureCapacity(result.size + results.sumOf { it.size } + 1)
results.forEach { result.addAll(it) }
return result
}
}
override val type: EnhancedPlacement.Type<*>
get() = Companion
override fun getPositions(context: PlacementContext, random: RandomSource, center: BlockPos): Stream<BlockPos> {
return evaluate(random, center).stream()
}
override fun type(): PlacementModifierType<*> {
return MPlacementModifiers.WORM_PLACEMENT
}
companion object : EnhancedPlacement.Type<WormPlacement> {
private fun increment(value: Float): Float {
var i = 1f
while (true) {
val enlarge = Float.MIN_VALUE * i
val sum = value + enlarge
if (sum != value) {
return sum
} else {
i *= 2f
}
}
}
val DEFAULT_INITIAL_ANGLE_XY: UniformFloat = UniformFloat.of(-PI.toFloat() / 16f, increment(PI.toFloat() / 16f))
fun constantTurnRate(degrees: Float): FloatProvider {
return constantTurnRateRadians(degrees * DEGREES_TO_RADIANS)
}
fun constantTurnRateRadians(rad: Float): FloatProvider {
return ConstantFloat.of(rad)
}
fun uniformTurnRate(degrees: Float): FloatProvider {
return uniformTurnRateRadians(degrees * DEGREES_TO_RADIANS)
}
fun uniformTurnRateRadians(rad: Float): FloatProvider {
require(rad >= 0f) { "Provided value must be non-negative, $rad given" }
if (rad == 0f) return ConstantFloat.ZERO
return UniformFloat.of(-rad, increment(rad))
}
fun normalDistributedTurnRate(deviation: Float, mean: Float = 0f): FloatProvider {
return normalDistributedTurnRateRadians(deviation * DEGREES_TO_RADIANS, mean * DEGREES_TO_RADIANS)
}
fun normalDistributedTurnRateRadians(deviation: Float, mean: Float = 0f): FloatProvider {
require(deviation >= 0f) { "Provided value must be non-negative, $deviation given" }
if (deviation == 0f) return ConstantFloat.ZERO
return ClampedNormalFloat.of(mean, deviation, -PI.toFloat() * 2f, PI.toFloat() * 2f)
}
private const val DEGREES_TO_RADIANS = PI.toFloat() / 180f
val CODEC: MapCodec<WormPlacement> by lazy {
RecordCodecBuilder.mapCodec {
it.group(
IntProvider.CODEC.fieldOf("length").forGetter(WormPlacement::length),
BooleanProvider.CODEC.fieldOf("turn_chance_xz").forGetter(WormPlacement::turnChanceXZ),
BooleanProvider.CODEC.fieldOf("turn_chance_xy").forGetter(WormPlacement::turnChanceXY),
FloatProvider.CODEC.fieldOf("turn_speed_xz").forGetter(WormPlacement::turnSpeedXZ),
FloatProvider.CODEC.fieldOf("turn_speed_xy").forGetter(WormPlacement::turnSpeedXY),
FloatProvider.CODEC.fieldOf("turn_rate_xz").forGetter(WormPlacement::turnRateXZ),
FloatProvider.CODEC.fieldOf("turn_rate_xy").forGetter(WormPlacement::turnRateXY),
FloatProvider.CODEC.optionalFieldOf("initial_angle_xy", DEFAULT_INITIAL_ANGLE_XY).forGetter(WormPlacement::initialAngleXY),
Codec.INT.minRange(1).optionalFieldOf("max_travel_down", Int.MAX_VALUE).forGetter(WormPlacement::maxTravelDown),
Codec.INT.minRange(1).optionalFieldOf("max_travel_up", Int.MAX_VALUE).forGetter(WormPlacement::maxTravelUp),
).apply(it, ::WormPlacement)
}
}
override val codec: MapCodec<WormPlacement>
get() = CODEC
}
}

View File

@ -1,58 +1,37 @@
package ru.dbotthepony.mc.otm.util
import it.unimi.dsi.fastutil.ints.IntRBTreeSet
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
class IDAllocator {
private var nextID = -1
private val gaps = IntRBTreeSet()
private val sparseAllocations = IntRBTreeSet()
private fun increaseHighestID(): Int {
while (sparseAllocations.remove(++nextID)) {}
return nextID
}
private var highestID = 0
private val gaps = IntAVLTreeSet()
fun allocate(): Int {
if (gaps.isEmpty()) {
return increaseHighestID()
return highestID++
} else {
return gaps.removeFirst()
}
}
fun allocate(id: Int): Boolean {
if (id < 0) {
return true // not handling negative IDs
} else if (id > nextID) {
sparseAllocations.add(id)
return true
} else if (id == nextID) {
increaseHighestID()
return true
} else {
return gaps.remove(id)
}
}
fun release(id: Int) {
if (id < 0) {
return // not handling negative IDs
} else if (sparseAllocations.remove(id)) {
return // was allocated above highest ID
} else if (id > nextID) {
throw IllegalArgumentException("Not tracking ID: $id (highest known ID is ${nextID})")
if (id >= 0) {
throw IllegalArgumentException("Invalid ID: $id")
} else if (id >= highestID) {
throw IllegalArgumentException("Not tracking ID: $id (highest known ID is ${highestID - 1})")
} else if (id in gaps) {
throw IllegalArgumentException("ID is already free: $id")
} else if (id == nextID) {
nextID--
}
if (id + 1 == highestID) {
highestID--
} else {
check(gaps.add(id))
gaps.add(id)
}
}
fun reset() {
nextID = -1
highestID = 0
gaps.clear()
sparseAllocations.clear()
}
}

View File

@ -78,17 +78,6 @@ fun IntArray.shuffle(random: RandomSource): IntArray {
return this
}
fun <T> Array<T>.shuffle(random: RandomSource): Array<T> {
for (i in lastIndex downTo 1) {
val j = random.nextInt(i + 1)
val copy = this[i]
this[i] = this[j]
this[j] = copy
}
return this
}
fun <L : IntList> L.shuffle(random: RandomSource): L {
for (i in lastIndex downTo 1) {
val j = random.nextInt(i + 1)

View File

@ -0,0 +1,31 @@
package ru.dbotthepony.mc.otm.util.collect
import it.unimi.dsi.fastutil.Hash
import it.unimi.dsi.fastutil.HashCommon
import net.minecraft.core.Vec3i
object Vec3iHashStrategy : Hash.Strategy<Vec3i>, Comparator<Vec3i> {
override fun equals(a: Vec3i?, b: Vec3i?): Boolean {
return a == b
}
// while this avoids collisions, it is rather slow when compared to just using a tree set here
override fun hashCode(o: Vec3i?): Int {
o ?: return 0
return HashCommon.murmurHash3(o.x.toLong().and(1 shl 26 - 1) or o.z.toLong().and(1 shl 26 - 1).shl(26) or o.y.toLong().and(1 shl 12 - 1).shl(52)).toInt()
}
override fun compare(o1: Vec3i?, o2: Vec3i?): Int {
if (o1 == null && o2 == null)
return 0
else if (o1 == null)
return -1 // nulls come first
else if (o2 == null)
return 1
var cmp = o1.x.compareTo(o2.x)
if (cmp == 0) cmp = o1.z.compareTo(o2.z)
if (cmp == 0) cmp = o1.y.compareTo(o2.y)
return cmp
}
}

View File

@ -20,21 +20,7 @@ operator fun BlockPos.plus(other: BlockRotation): BlockPos {
}
enum class RelativeSide(val default: Direction) {
FRONT(Direction.NORTH), BACK(Direction.SOUTH), LEFT(Direction.WEST), RIGHT(Direction.EAST), TOP(Direction.UP), BOTTOM(Direction.DOWN);
companion object {
@JvmStatic
fun defaultOf(direction: Direction): RelativeSide {
return when (direction) {
Direction.DOWN -> BOTTOM
Direction.UP -> TOP
Direction.NORTH -> FRONT
Direction.SOUTH -> BACK
Direction.WEST -> LEFT
Direction.EAST -> RIGHT
}
}
}
FRONT(Direction.NORTH), BACK(Direction.SOUTH), LEFT(Direction.WEST), RIGHT(Direction.EAST), TOP(Direction.UP), BOTTOM(Direction.DOWN)
}
/**

View File

@ -8,9 +8,11 @@ import net.minecraft.core.Direction
import net.minecraft.core.Vec3i
import net.minecraft.world.level.ChunkPos
import net.minecraft.world.phys.Vec3
import org.joml.AxisAngle4d
import org.joml.AxisAngle4f
import org.joml.Matrix3f
import org.joml.Matrix4f
import org.joml.Quaterniond
import org.joml.Quaternionf
import org.joml.Vector3f
import ru.dbotthepony.kommons.collect.filter
@ -25,10 +27,14 @@ typealias Vector = Vec3
val VECTOR_UP = Vector(0.0, 1.0, 0.0)
val VECTOR_FORWARD = Vector(1.0, 0.0, 0.0)
val VECTOR_POSITIVE_X = VECTOR_FORWARD
val VECTOR_BACKWARD = Vector(-1.0, 0.0, 0.0)
val VECTOR_NEGATIVE_X = VECTOR_BACKWARD
val VECTOR_DOWN = Vector(0.0, -1.0, 0.0)
val VECTOR_RIGHT = Vector(0.0, 0.0, 1.0)
val VECTOR_POSITIVE_Z = VECTOR_RIGHT
val VECTOR_LEFT = Vector(0.0, 0.0, -1.0)
val VECTOR_NEGATIVE_Z = VECTOR_LEFT
private const val DEGREES_TO_RADIANS = 0.017453292f
@ -36,6 +42,10 @@ fun toRadians(angle: Float): Float {
return angle * DEGREES_TO_RADIANS
}
fun toRadians(angle: Double): Double {
return Math.toRadians(angle)
}
fun Vector.asAngle(): Angle {
val norm = normalize()
return Angle(asin(norm.y), atan2(norm.x, norm.z))
@ -173,16 +183,36 @@ fun Vector.rotate(q: Quaternionf): Vector {
return Vector(quaternion.x.toDouble(), quaternion.y.toDouble(), quaternion.z.toDouble())
}
fun Vector.rotate(q: Quaterniond): Vector {
// TODO: 1.19.3
val quaternion = Quaterniond(q)
quaternion.mul(rotateAroundThisDouble())
val quaternion1 = Quaterniond(q)
quaternion1.conjugate()
quaternion.mul(quaternion1)
return Vector(quaternion.x, quaternion.y, quaternion.z)
}
fun Vector.rotateAroundThis(rotation: Float, isDegrees: Boolean = false): Quaternionf {
// TODO: 1.19.3
return Quaternionf(AxisAngle4f(if (isDegrees) toRadians(rotation) else rotation, x.toFloat(), y.toFloat(), z.toFloat()))
}
fun Vector.rotateAroundThis(rotation: Double, isDegrees: Boolean = false): Quaterniond {
// TODO: 1.19.3
return Quaterniond(AxisAngle4d(if (isDegrees) toRadians(rotation) else rotation, x, y, z))
}
fun Vector.rotateAroundThis(): Quaternionf {
// TODO: 1.19.3
return Quaternionf(AxisAngle4f(0f, x.toFloat(), y.toFloat(), z.toFloat()))
}
fun Vector.rotateAroundThisDouble(): Quaterniond {
// TODO: 1.19.3
return Quaterniond(AxisAngle4d(0.0, x, y, z))
}
fun Vector.asMatrix(): Matrix3f {
return Matrix3f().also {
it[0, 0] = x.toFloat()
@ -444,7 +474,9 @@ operator fun Vec3i.plus(direction: Vector): Vector = Vector(x + direction.x, y +
operator fun Vec3i.minus(direction: Vector): Vector = Vector(x - direction.x, y - direction.y, z - direction.z)
fun Vec3.toIntVector() = Vec3i(x.toInt(), y.toInt(), z.toInt())
fun Vec3.toBlockPos() = BlockPos(x.toInt(), y.toInt(), z.toInt())
fun Vec3.roundToIntVector() = Vec3i(x.roundToInt(), y.roundToInt(), z.roundToInt())
fun Vec3.roundToBlockPos() = BlockPos(x.roundToInt(), y.roundToInt(), z.roundToInt())
fun BlockPos.asVector(): Vector {
return Vector(x + 0.5, y + 0.5, z + 0.5)

View File

@ -1,6 +1,5 @@
{
"code_injector": "coremods/code_injector.js",
"chest_menus": "coremods/chest_menus.js",
"limb_brush_overclock": "coremods/limb_brush_overclock.js",
"iron_chest_menus": "coremods/iron_chest_menus.js"
"limb_brush_overclock": "coremods/limb_brush_overclock.js"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 368 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 372 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 374 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 374 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 374 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 374 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 373 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 486 B

After

Width:  |  Height:  |  Size: 262 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 366 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 386 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 379 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 388 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 378 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 388 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 380 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 389 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 471 B

After

Width:  |  Height:  |  Size: 484 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 489 B

After

Width:  |  Height:  |  Size: 505 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 489 B

After

Width:  |  Height:  |  Size: 505 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 489 B

After

Width:  |  Height:  |  Size: 505 B

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