aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/features/chat/PartyCommands.kt
blob: de3a0d9804d2b88841646a19c2ef051ffef5f42b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package moe.nea.firmament.features.chat

import com.mojang.brigadier.CommandDispatcher
import com.mojang.brigadier.StringReader
import com.mojang.brigadier.exceptions.CommandSyntaxException
import com.mojang.brigadier.tree.LiteralCommandNode
import kotlin.time.Duration.Companion.seconds
import net.minecraft.util.math.BlockPos
import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.commands.CaseInsensitiveLiteralCommandNode
import moe.nea.firmament.commands.thenExecute
import moe.nea.firmament.events.CommandEvent
import moe.nea.firmament.events.PartyMessageReceivedEvent
import moe.nea.firmament.events.ProcessChatEvent
import moe.nea.firmament.gui.config.ManagedConfig
import moe.nea.firmament.util.ErrorUtil
import moe.nea.firmament.util.MC
import moe.nea.firmament.util.TimeMark
import moe.nea.firmament.util.tr
import moe.nea.firmament.util.useMatch

object PartyCommands {

	val messageInChannel = "(?<channel>Party|Guild) >([^:]+?)? (?<name>[^: ]+): (?<message>.+)".toPattern()

	@Subscribe
	fun onChat(event: ProcessChatEvent) {
		messageInChannel.useMatch(event.unformattedString) {
			val channel = group("channel")
			val message = group("message")
			val name = group("name")
			if (channel == "Party") {
				PartyMessageReceivedEvent.publish(PartyMessageReceivedEvent(
					event, message, name
				))
			}
		}
	}

	val commandPrefixes = "!-?$.&#+~€\"@°_;:³²`'´ß\\,|".toSet()

	data class PartyCommandContext(
		val name: String
	)

	val dispatch = CommandDispatcher<PartyCommandContext>().also { dispatch ->
		fun register(
			name: String,
			vararg alias: String,
			block: CaseInsensitiveLiteralCommandNode.Builder<PartyCommandContext>.() -> Unit = {},
		): LiteralCommandNode<PartyCommandContext> {
			val node =
				dispatch.register(CaseInsensitiveLiteralCommandNode.Builder<PartyCommandContext>(name).also(block))
			alias.forEach { register(it) { redirect(node) } }
			return node
		}

		register("warp", "pw", "pwarp", "partywarp") {
			executes {
				// TODO: add check if you are the party leader
				MC.sendCommand("p warp")
				0
			}
		}

		register("transfer", "pt", "ptme") {
			executes {
				MC.sendCommand("p transfer ${it.source.name}")
				0
			}
		}

		register("allinvite", "allinv") {
			executes {
				MC.sendCommand("p settings allinvite")
				0
			}
		}

		register("coords") {
			executes {
				val p = MC.player?.blockPos ?: BlockPos.ORIGIN
				MC.sendCommand("pc x: ${p.x}, y: ${p.y}, z: ${p.z}")
				0
			}
		}
		// TODO: downtime tracker (display message again at end of dungeon)
		// instance ends: kuudra, dungeons, bacte
		// TODO: at TPS command
	}

	object TConfig : ManagedConfig("party-commands", Category.CHAT) {
		val enable by toggle("enable") { false }
		val cooldown by duration("cooldown", 0.seconds, 20.seconds) { 2.seconds }
		val ignoreOwnCommands by toggle("ignore-own") { false }
	}

	var lastCommand = TimeMark.farPast()

	@Subscribe
	fun listPartyCommands(event: CommandEvent.SubCommand) {
		event.subcommand("partycommands") {
			thenExecute {
				// TODO: Better help, including descriptions and redirect detection
				MC.sendChat(tr("firmament.partycommands.help", "Available party commands: ${dispatch.root.children.map { it.name }}. Available prefixes: $commandPrefixes"))
			}
		}
	}

	@Subscribe
	fun onPartyMessage(event: PartyMessageReceivedEvent) {
		if (!TConfig.enable) return
		if (event.message.firstOrNull() !in commandPrefixes) return
		if (event.name == MC.playerName && TConfig.ignoreOwnCommands) return
		if (lastCommand.passedTime() < TConfig.cooldown) {
			MC.sendChat(tr("firmament.partycommands.cooldown", "Skipping party command. Cooldown not passed."))
			return
		}
		// TODO: add trust levels
		val commandLine = event.message.substring(1)
		try {
			dispatch.execute(StringReader(commandLine), PartyCommandContext(event.name))
		} catch (ex: Exception) {
			if (ex is CommandSyntaxException) {
				MC.sendChat(tr("firmament.partycommands.unknowncommand", "Unknown party command."))
				return
			} else {
				MC.sendChat(tr("firmament.partycommands.unknownerror", "Unknown error during command execution."))
				ErrorUtil.softError("Unknown error during command execution.", ex)
			}
		}
		lastCommand = TimeMark.now()
	}
}