Skip to content

Commit

Permalink
add options to specify where the boat should be boosted
Browse files Browse the repository at this point in the history
  • Loading branch information
btwonion committed Aug 5, 2024
1 parent 791235c commit 465032a
Show file tree
Hide file tree
Showing 9 changed files with 202 additions and 80 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/readme-modrinth-sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
paths:
- "README.md"
branches:
- main
- master
workflow_dispatch:

jobs:
Expand Down
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@

```json5
{
"version": 1, // just ignore that, only for migrations
"version": 3, // just ignore that, only for migrations
"config": {
"stepHeight": 0.3, // the height the boat should travel upwards
"playerEjectTicks": 200.0, // defines the ticks that should pass before ejecting a player, when the player lost control over the boat
"boostUnderwater": true, // toggles, whether a boat, which is underwater should be boosted upwards with half of the step height
"onlyForPlayers": true // toggles, whether the boosts configured below should only work if the boat carries a player.
"stepHeight": 0.35, // The amount of blocks you are going to be boosted when triggering a boost
"playerEjectTicks": 200.0, // The ticks the game waits before kicking you out of a boat after the player lost control
"boostUnderwater": true, // Toggles, whether a boat, which is underwater should be boosted upwards
"boostOnBlocks": false, // Toggles, whether a boat, which is on a block should be boosted upwards when running against an elevation
"boostOnIce": true, // Toggles, whether a boat, which is on an ice block should be boosted upwards when running against an elevation
"boostOnWater": true, // Toggles, whether a boat, which is on water should be boosted upwards when floating against an elevation
"onlyForPlayers": true // Toggles, whether a boat should only be boosted when carrying a player
}
}
```
Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ plugins {
}

val beta: Int? = null // Pattern is '1.0.0-beta1-1.20.6-pre.2'
val featureVersion = "2.0.0${if (beta != null) "-beta$beta" else ""}"
val featureVersion = "2.1.0${if (beta != null) "-beta$beta" else ""}"
val mcVersion = property("mcVersion")!!.toString()
val mcVersionRange = property("mcVersionRange")!!.toString()
version = "$featureVersion-$mcVersion"
Expand Down
3 changes: 2 additions & 1 deletion changelog.md
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
- simplify movement logic
- add options to specify where the player wants to get boosted
- use yacl 3.5 for config screen creation
73 changes: 69 additions & 4 deletions src/main/java/dev/nyon/bbm/asm/BoatMixin.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import dev.nyon.bbm.config.Config;
import dev.nyon.bbm.config.ConfigKt;
import net.minecraft.core.BlockPos;
import net.minecraft.tags.BlockTags;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.vehicle.Boat;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
Expand All @@ -16,6 +21,9 @@
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant;

import java.util.ArrayList;
import java.util.List;

@Mixin(Boat.class)
abstract class BoatMixin extends Entity {
@Shadow
Expand All @@ -28,6 +36,39 @@ public BoatMixin(
super(entityType, level);
}

@Unique
private List<BlockState> getCarryingBlocks() {
List<BlockState> states = new ArrayList<>();

AABB aABB = this.getBoundingBox();
AABB aABB2 = new AABB(aABB.minX, aABB.minY - 0.001, aABB.minZ, aABB.maxX, aABB.minY, aABB.maxZ);
int i = Mth.floor(aABB2.minX) - 1;
int j = Mth.ceil(aABB2.maxX) + 1;
int k = Mth.floor(aABB2.minY) - 1;
int l = Mth.ceil(aABB2.maxY) + 1;
int m = Mth.floor(aABB2.minZ) - 1;
int n = Mth.ceil(aABB2.maxZ) + 1;
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();

for (int p = i; p < j; p++) {
for (int q = m; q < n; q++) {
int r = (p != i && p != j - 1 ? 0 : 1) + (q != m && q != n - 1 ? 0 : 1);
if (r != 2) {
for (int s = k; s < l; s++) {
if (r <= 0 || s != k && s != l - 1) {
mutableBlockPos.set(p, s, q);
BlockState blockState = this.level()
.getBlockState(mutableBlockPos);
states.add(blockState);
}
}
}
}
}

return states;
}

@SuppressWarnings("DataFlowIssue")
@ModifyExpressionValue(
method = "floatBoat",
Expand All @@ -39,9 +80,33 @@ public BoatMixin(
)
private Vec3 changeMovement(Vec3 original) {
if (failsPlayerCondition()) return original;
if (!horizontalCollision) return original;
if (status == Boat.Status.UNDER_WATER && !ConfigKt.getActiveConfig()
.getBoostUnderwater()) return original;

switch (status) {
case ON_LAND -> {
if (!ConfigKt.getActiveConfig()
.getBoostOnBlocks() && !ConfigKt.getActiveConfig()
.getBoostOnIce()) return original;
if (ConfigKt.getActiveConfig()
.getBoostOnIce()) {
List<BlockState> carryingBlocks = getCarryingBlocks();
if (carryingBlocks.stream()
.noneMatch(state -> state.is(BlockTags.ICE))) return original;
}
if (!horizontalCollision) return original;
}
case IN_WATER -> {
if (!ConfigKt.getActiveConfig()
.getBoostOnWater()) return original;
if (!horizontalCollision) return original;
}
case UNDER_WATER, UNDER_FLOWING_WATER -> {
if (!ConfigKt.getActiveConfig()
.getBoostUnderwater()) return original;
}
case IN_AIR -> {
return original;
}
}

return new Vec3(
original.x,
Expand Down Expand Up @@ -71,6 +136,6 @@ private boolean failsPlayerCondition() {

if (!config.getOnlyForPlayers()) return false;
return getPassengers().stream()
.anyMatch(entity -> entity instanceof Player);
.noneMatch(entity -> entity instanceof Player);
}
}
2 changes: 1 addition & 1 deletion src/main/kotlin/dev/nyon/bbm/BetterBoatMovement.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ object BetterBoatMovement : ModInitializer {
private fun instantiateConfig() {
config(
FabricLoader.getInstance().configDir.resolve("better-boat-movement.json"),
2,
3,
Config()
) { element, version -> migrate(element, version) }
internalConfig = loadConfig()
Expand Down
54 changes: 41 additions & 13 deletions src/main/kotlin/dev/nyon/bbm/config/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,51 @@ import dev.nyon.bbm.extensions.resourceLocation
import dev.nyon.bbm.serverConfig
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.*
import net.fabricmc.api.EnvType
import net.fabricmc.loader.api.FabricLoader
import net.minecraft.client.Minecraft
import net.minecraft.network.FriendlyByteBuf
/*? if <1.20.5 {*/

/*? if <1.20.6 {*/
/*import net.fabricmc.fabric.api.networking.v1.FabricPacket
import net.fabricmc.fabric.api.networking.v1.PacketType
@Serializable
data class Config(
var stepHeight: Float = 0.3f,
var stepHeight: Float = 0.35f,
var playerEjectTicks: Float = 20f * 10f,
var boostUnderwater: Boolean = true,
var boostOnBlocks: Boolean = true,
var boostOnIce: Boolean = true,
var boostOnWater: Boolean = true,
var onlyForPlayers: Boolean = true
) : FabricPacket {
companion object {
@Transient
val packetType: PacketType<Config> = PacketType.create(
resourceLocation("better-boat-movement:sync")!!
) { buffer ->
Config(buffer.readFloat(), buffer.readFloat(), buffer.readBoolean(), buffer.readBoolean())
Config(
buffer.readFloat(),
buffer.readFloat(),
buffer.readBoolean(),
buffer.readBoolean(),
buffer.readBoolean(),
buffer.readBoolean(),
buffer.readBoolean()
)
}
}
override fun write(buffer: FriendlyByteBuf) {
buffer.writeFloat(stepHeight)
buffer.writeFloat(playerEjectTicks)
buffer.writeBoolean(boostUnderwater)
buffer.writeBoolean(onlyForPlayers)
override fun write(buf: FriendlyByteBuf) {
buf.writeFloat(stepHeight)
buf.writeFloat(playerEjectTicks)
buf.writeBoolean(boostUnderwater)
buf.writeBoolean(boostOnBlocks)
buf.writeBoolean(boostOnIce)
buf.writeBoolean(boostOnWater)
buf.writeBoolean(onlyForPlayers)
}
override fun getType(): PacketType<*> {
Expand All @@ -46,9 +61,12 @@ import net.minecraft.network.protocol.common.custom.CustomPacketPayload

@Serializable
data class Config(
var stepHeight: Float = 0.2f,
var stepHeight: Float = 0.35f,
var playerEjectTicks: Float = 20f * 10f,
var boostUnderwater: Boolean = true,
var boostOnBlocks: Boolean = true,
var boostOnIce: Boolean = true,
var boostOnWater: Boolean = true,
var onlyForPlayers: Boolean = true
) : CustomPacketPayload {
companion object {
Expand All @@ -64,6 +82,9 @@ data class Config(
buf.readFloat(),
buf.readFloat(),
buf.readBoolean(),
buf.readBoolean(),
buf.readBoolean(),
buf.readBoolean(),
buf.readBoolean()
)
}
Expand All @@ -75,6 +96,9 @@ data class Config(
buf.writeFloat(config.stepHeight)
buf.writeFloat(config.playerEjectTicks)
buf.writeBoolean(config.boostUnderwater)
buf.writeBoolean(config.boostOnBlocks)
buf.writeBoolean(config.boostOnIce)
buf.writeBoolean(config.boostOnWater)
buf.writeBoolean(config.onlyForPlayers)
}
}
Expand All @@ -94,11 +118,15 @@ fun getActiveConfig(): Config? {
return serverConfig
}


@Suppress("UNUSED_PARAMETER")
fun migrate(tree: JsonElement, version: Int?): Config? {
val jsonObject = tree.jsonObject
return when (version) {
1 -> Config()
1 -> null
2 -> Config(
playerEjectTicks = jsonObject["playerEjectTicks"]?.jsonPrimitive?.floatOrNull ?: return null,
boostUnderwater = jsonObject["boostUnderwater"]?.jsonPrimitive?.booleanOrNull ?: return null,
onlyForPlayers = jsonObject["onlyForPlayers"]?.jsonPrimitive?.booleanOrNull ?: return null
)
else -> null
}
}
105 changes: 63 additions & 42 deletions src/main/kotlin/dev/nyon/bbm/config/ConfigScreen.kt
Original file line number Diff line number Diff line change
@@ -1,46 +1,67 @@
package dev.nyon.bbm.config

import dev.isxander.yacl3.api.ConfigCategory
import dev.isxander.yacl3.api.Option
import dev.isxander.yacl3.api.OptionDescription
import dev.isxander.yacl3.api.YetAnotherConfigLib
import dev.isxander.yacl3.api.controller.FloatFieldControllerBuilder
import dev.isxander.yacl3.api.controller.TickBoxControllerBuilder
import dev.isxander.yacl3.dsl.*
import dev.nyon.konfig.config.saveConfig
import net.minecraft.client.gui.screens.Screen
import net.minecraft.network.chat.Component

fun generateYaclScreen(parent: Screen?): Screen {
val builder = YetAnotherConfigLib.createBuilder()

builder.title(Component.translatable("menu.bbm.config.title"))
builder.appendCategory()

builder.save { saveConfig(config) }
val yacl = builder.build()
return yacl.generateScreen(parent)
}

private fun YetAnotherConfigLib.Builder.appendCategory() = category(
ConfigCategory.createBuilder().name(Component.translatable("menu.bbm.config.category.title")).option(
Option.createBuilder<Float>().name(Component.translatable("menu.bbm.config.category.ground_step_height.title"))
.description(OptionDescription.of(Component.translatable("menu.bbm.config.category.ground_step_height.description")))
.binding(config.stepHeight, { config.stepHeight }, { config.stepHeight = it })
.controller(FloatFieldControllerBuilder::create).build()
).option(
Option.createBuilder<Float>().name(Component.translatable("menu.bbm.config.category.player_eject_ticks.title"))
.description(OptionDescription.of(Component.translatable("menu.bbm.config.category.player_eject_ticks.description")))
.binding(config.playerEjectTicks, { config.playerEjectTicks }, { config.playerEjectTicks = it })
.controller(FloatFieldControllerBuilder::create).build()
).option(
Option.createBuilder<Boolean>().name(Component.translatable("menu.bbm.config.category.boost_underwater.title"))
.description(OptionDescription.of(Component.translatable("menu.bbm.config.category.boost_underwater.description")))
.binding(config.boostUnderwater, { config.boostUnderwater }, { config.boostUnderwater = it })
.controller(TickBoxControllerBuilder::create).build()
).option(
Option.createBuilder<Boolean>().name(Component.translatable("menu.bbm.config.category.only_for_players.title"))
.description(OptionDescription.of(Component.translatable("menu.bbm.config.category.only_for_players.description")))
.binding(config.onlyForPlayers, { config.onlyForPlayers }, { config.onlyForPlayers = it })
.controller(TickBoxControllerBuilder::create).build()
).build()
)

fun generateYaclScreen(parent: Screen?): Screen = YetAnotherConfigLib("bbm") {
val general by categories.registering {
val stepHeight by rootOptions.registering {
binding(1f, { config.stepHeight }, { config.stepHeight = it})
controller = numberField(0f)
descriptionBuilder {
addDefaultText(1)
}
}

val playerEjectTicks by rootOptions.registering {
binding(0.2f, { config.playerEjectTicks }, { config.playerEjectTicks = it})
controller = numberField(0f, 10000f)
descriptionBuilder {
addDefaultText(1)
}
}

val boostUnderwater by rootOptions.registering {
binding(true, { config.boostUnderwater }, { config.boostUnderwater = it })
controller = tickBox()
descriptionBuilder {
addDefaultText(1)
}
}

val boostOnBlocks by rootOptions.registering {
binding(true, { config.boostOnBlocks }, { config.boostOnBlocks = it })
controller = tickBox()
descriptionBuilder {
addDefaultText(1)
}
}

val boostOnIce by rootOptions.registering {
binding(true, { config.boostOnIce }, { config.boostOnIce = it })
controller = tickBox()
descriptionBuilder {
addDefaultText(1)
}
}

val boostOnWater by rootOptions.registering {
binding(true, { config.boostOnWater }, { config.boostOnWater = it })
controller = tickBox()
descriptionBuilder {
addDefaultText(1)
}
}

val onlyForPlayers by rootOptions.registering {
binding(true, { config.onlyForPlayers }, { config.onlyForPlayers = it })
controller = tickBox()
descriptionBuilder {
addDefaultText(1)
}
}
}

save { saveConfig(config) }
}.generateScreen(parent)
Loading

0 comments on commit 465032a

Please sign in to comment.