[1.20.4] Feat: Click Friend by emyfops · Pull Request #88 · lambda-client/lambda · GitHub
Skip to content
155 changes: 155 additions & 0 deletions common/src/main/kotlin/com/lambda/command/commands/FriendCommand.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/*
* Copyright 2024 Lambda
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package com.lambda.command.commands

import com.lambda.Lambda.mc
import com.lambda.brigadier.CommandResult.Companion.failure
import com.lambda.brigadier.CommandResult.Companion.success
import com.lambda.brigadier.argument.literal
import com.lambda.brigadier.argument.string
import com.lambda.brigadier.argument.uuid
import com.lambda.brigadier.argument.value
import com.lambda.brigadier.execute
import com.lambda.brigadier.executeWithResult
import com.lambda.brigadier.required
import com.lambda.command.LambdaCommand
import com.lambda.config.configurations.FriendConfig
import com.lambda.friend.FriendManager
import com.lambda.util.Communication.info
import com.lambda.util.extension.CommandBuilder
import com.lambda.util.text.ClickEvents
import com.lambda.util.text.buildText
import com.lambda.util.text.literal
import com.lambda.util.text.styled
import java.awt.Color

object FriendCommand : LambdaCommand(
name = "friends",
usage = "friends <add | remove> <name | uuid>",
description = "Add or remove a friend"
) {
override fun CommandBuilder.create() {
execute {
this@FriendCommand.info(
buildText {
if (FriendManager.friends.isEmpty()) {
literal("You have no friends yet. Go make some! :3\n")
} else {
literal("Your friends (${FriendManager.friends.size}):\n")

FriendManager.friends.forEachIndexed { index, gameProfile ->
literal(" ${index + 1}. ${gameProfile.name}\n")
}
}

literal("\n")
styled(
color = Color.CYAN,
underlined = true,
clickEvent = ClickEvents.openFile(FriendConfig.primary.path),
) {
literal("Click to open your friends list as a file")
}
}
)
}

required(literal("add")) {
required(string("player name")) { player ->
suggests { _, builder ->
mc.networkHandler
?.playerList
?.filter { it.profile != mc.gameProfile }
?.map { it.profile.name }
?.forEach { builder.suggest(it) }

builder.buildFuture()
}

executeWithResult {
val name = player().value()
val id = mc.networkHandler
?.playerList
?.firstOrNull {
it.profile.name == name &&
it.profile != mc.gameProfile
} ?: return@executeWithResult failure("Could not find the player on the server")

return@executeWithResult if (FriendManager.befriend(id.profile)) {
this@FriendCommand.info(FriendManager.befriendedText(id.profile.name))
success()
} else {
failure("This player is already in your friend list")
}
}
}

required(uuid("player uuid")) { player ->
suggests { _, builder ->
mc.networkHandler
?.playerList
?.filter { it.profile != mc.gameProfile }
?.map { it.profile.id }
?.forEach { builder.suggest(it.toString()) }

builder.buildFuture()
}

executeWithResult {
val uuid = player().value()
val id = mc.networkHandler
?.playerList
?.firstOrNull {
it.profile.id == uuid && it.profile != mc.gameProfile
} ?: return@executeWithResult failure("Could not find the player on the server")

return@executeWithResult if (FriendManager.befriend(id.profile)) {
this@FriendCommand.info(FriendManager.befriendedText(id.profile.name))
success()
} else {
failure("This player is already in your friend list")
}
}
}
}

required(literal("remove")) {
required(string("player name")) { player ->
suggests { _, builder ->
FriendManager.friends.map { it.name }
.forEach { builder.suggest(it) }

builder.buildFuture()
}

executeWithResult {
val name = player().value()
val profile = FriendManager.gameProfile(name)
?: return@executeWithResult failure("This player is not in your friend list")

return@executeWithResult if (FriendManager.unfriend(profile)) {
this@FriendCommand.info(FriendManager.unfriendedText(name))
success()
} else {
failure("This player is not in your friend list")
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import com.lambda.util.extension.CommandBuilder

object ReplayCommand : LambdaCommand(
name = "replay",
usage = "replay <play|load|save|prune>",
usage = "replay <play | load | save | prune>",
description = "Play, load, save, or prune a replay"
) {
override fun CommandBuilder.create() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import com.lambda.util.extension.CommandBuilder

object TransferCommand : LambdaCommand(
name = "transfer",
usage = "transfer <move|cancel|undo> <item> <amount> <to>",
usage = "transfer <move | cancel | undo> <item> <amount> <to>",
description = "Transfer items from anywhere to anywhere",
) {
private var lastTransfer: TransferResult.Transfer? = null
Expand Down
4 changes: 4 additions & 0 deletions common/src/main/kotlin/com/lambda/config/AbstractSetting.kt
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ abstract class AbstractSetting<T : Any>(
value = gson.fromJson(serialized, type)
}

/**
* Will only register changes of the variable, not the content of the variable!
* E.g., if the variable is a list, it will only register if the list reference changes, not if the content of the list changes.
*/
fun onValueChange(block: SafeContext.(from: T, to: T) -> Unit) {
listeners.add(ValueListener(true) { from, to ->
runSafe {
Expand Down
3 changes: 0 additions & 3 deletions common/src/main/kotlin/com/lambda/config/Configurable.kt
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,6 @@ abstract class Configurable(
* @param name The unique identifier for the setting.
* @param defaultValue The default [Set] value of type [T] for the setting.
* @param description A brief explanation of the setting's purpose and behavior.
* @param hackDelegates A flag that determines whether the setting should be serialized with the default value.
* @param visibility A lambda expression that determines the visibility status of the setting.
*
* ```kotlin
Expand All @@ -262,14 +261,12 @@ abstract class Configurable(
name: String,
defaultValue: Set<T>,
description: String = "",
hackDelegates: Boolean = false,
noinline visibility: () -> Boolean = { true },
) = SetSetting(
name,
defaultValue.toMutableSet(),
TypeToken.getParameterized(MutableSet::class.java, T::class.java).type,
description,
hackDelegates,
visibility,
).also {
settings.add(it)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@

package com.lambda.config.settings.collections

import com.google.gson.JsonElement
import com.lambda.Lambda.gson
import com.lambda.config.AbstractSetting
import java.lang.reflect.Type

Expand All @@ -27,27 +25,13 @@ import java.lang.reflect.Type
*/
class SetSetting<T : Any>(
override val name: String,
private val defaultValue: MutableSet<T>,
defaultValue: MutableSet<T>,
type: Type,
description: String,
private val hackDelegates: Boolean,
visibility: () -> Boolean,
) : AbstractSetting<MutableSet<T>>(
defaultValue,
type,
description,
visibility
) {
override fun toJson(): JsonElement {
return if (hackDelegates) gson.toJsonTree(defaultValue, type)
else super.toJson()
}

override fun loadFromJson(serialized: JsonElement) {
if (hackDelegates) {
defaultValue.addAll(gson.fromJson(serialized, type))
setValue(this, ::value, defaultValue.distinct().toMutableSet())
}
else super.loadFromJson(serialized)
}
}
)
57 changes: 45 additions & 12 deletions common/src/main/kotlin/com/lambda/friend/FriendManager.kt
Loading