Skip to content

Commit 9da8a5e

Browse files
committed
Command system and extensive DSL adapter
1 parent 02082bc commit 9da8a5e

34 files changed

+3169
-29
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.lambda.mixin;
2+
3+
import com.lambda.command.CommandManager;
4+
import com.mojang.brigadier.CommandDispatcher;
5+
import net.minecraft.client.gui.screen.ChatInputSuggestor;
6+
import net.minecraft.client.gui.widget.TextFieldWidget;
7+
import net.minecraft.client.network.ClientPlayNetworkHandler;
8+
import net.minecraft.command.CommandSource;
9+
import org.spongepowered.asm.mixin.Final;
10+
import org.spongepowered.asm.mixin.Mixin;
11+
import org.spongepowered.asm.mixin.Shadow;
12+
import org.spongepowered.asm.mixin.injection.At;
13+
import org.spongepowered.asm.mixin.injection.ModifyVariable;
14+
import org.spongepowered.asm.mixin.injection.Redirect;
15+
16+
@Mixin(ChatInputSuggestor.class)
17+
public class ChatInputSuggestorMixin {
18+
19+
@Shadow
20+
@Final
21+
TextFieldWidget textField;
22+
23+
@ModifyVariable(method = "refresh", at = @At(value = "STORE"), index = 3)
24+
private boolean refreshModify(boolean showCompletions) {
25+
return CommandManager.INSTANCE.isCommand(textField.getText());
26+
}
27+
28+
@Redirect(method = "refresh", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayNetworkHandler;getCommandDispatcher()Lcom/mojang/brigadier/CommandDispatcher;"))
29+
private CommandDispatcher<CommandSource> refreshRedirect(ClientPlayNetworkHandler instance) {
30+
return CommandManager.INSTANCE.currentDispatcher(textField.getText());
31+
}
32+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.lambda.mixin;
2+
3+
import com.lambda.command.CommandManager;
4+
import net.minecraft.client.gui.screen.ChatScreen;
5+
import org.spongepowered.asm.mixin.Mixin;
6+
import org.spongepowered.asm.mixin.injection.At;
7+
import org.spongepowered.asm.mixin.injection.Inject;
8+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
9+
10+
@Mixin(ChatScreen.class)
11+
public abstract class ChatScreenMixin {
12+
13+
@Inject(method = "sendMessage", at = @At("HEAD"), cancellable = true)
14+
void sendMessageInject(String chatText, boolean addToHistory, CallbackInfoReturnable<Boolean> cir) {
15+
if (!CommandManager.INSTANCE.isLambdaCommand(chatText)) return;
16+
CommandManager.INSTANCE.executeCommand(chatText);
17+
18+
cir.setReturnValue(true);
19+
}
20+
}

common/src/main/kotlin/com/lambda/Lambda.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import java.awt.Color
1717
object Lambda {
1818
const val MOD_NAME = "Lambda"
1919
const val MOD_ID = "lambda"
20-
private const val SYMBOL = "λ"
20+
const val SYMBOL = "λ"
2121
val VERSION: String = LoaderInfo.getVersion()
2222

2323
val LOG: Logger = LogManager.getLogger(SYMBOL)
@@ -31,9 +31,7 @@ object Lambda {
3131
.create()
3232

3333
fun initialize() {
34-
LOG.info("Initializing $MOD_NAME $VERSION")
35-
36-
initializeEagerObjects()
34+
Loader.initialize()
3735
}
3836

3937
/**
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.lambda
2+
3+
import com.lambda.config.Configuration
4+
import com.lambda.util.FolderRegister
5+
6+
object LambdaConfig : Configuration() {
7+
override val configName = "lambda"
8+
override val primary = FolderRegister.lambda.resolve("$configName.json")
9+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.lambda
2+
3+
interface Loadable {
4+
fun load(): String
5+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.lambda
2+
3+
import com.lambda.Lambda.LOG
4+
import com.lambda.command.CommandManager
5+
import com.lambda.module.ModuleRegistry
6+
import kotlin.system.measureTimeMillis
7+
8+
object Loader {
9+
private val loadables = listOf(
10+
ModuleRegistry,
11+
CommandManager
12+
)
13+
14+
fun initialize() {
15+
LOG.info("Initializing ${Lambda.MOD_NAME} ${Lambda.VERSION}")
16+
17+
val initTime = measureTimeMillis {
18+
loadables.forEach { loadable ->
19+
var info: String
20+
val phaseTime = measureTimeMillis {
21+
info = loadable.load()
22+
}
23+
24+
LOG.info("$info in ${phaseTime}ms")
25+
}
26+
}
27+
28+
LOG.info("${Lambda.MOD_NAME} ${Lambda.VERSION} was successfully initialized (${initTime}ms)")
29+
}
30+
}
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
/*
2+
* Copyright 2023 The Quilt Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.lambda.brigadier
18+
19+
import com.mojang.brigadier.arguments.ArgumentType
20+
import com.mojang.brigadier.builder.ArgumentBuilder
21+
import com.mojang.brigadier.builder.RequiredArgumentBuilder
22+
import com.mojang.brigadier.context.CommandContext
23+
24+
typealias RequiredArgumentConstructor<S, D> =
25+
ArgumentConstructor<S, RequiredArgumentBuilder<S, *>, D>
26+
27+
typealias DefaultArgumentConstructor<S, T> =
28+
RequiredArgumentConstructor<S, DefaultArgumentDescriptor<T>>
29+
30+
/**
31+
* Class containing all data necessary to construct an
32+
* [optional] or [required] argument.
33+
*/
34+
class ArgumentConstructor<S, B : ArgumentBuilder<S, *>, D : ArgumentDescriptor<*>>(
35+
private val builder: B,
36+
val name: String,
37+
private val descriptor: D
38+
) {
39+
/**
40+
* Converts this constructor into a required argument.
41+
*
42+
* @see CommandArgument.Required
43+
* @see required
44+
*/
45+
fun required(): CommandArgument.Required<S, B, D> {
46+
return CommandArgument.Required(builder, name, descriptor)
47+
}
48+
49+
/**
50+
* Converts this constructor into an optional argument.
51+
*
52+
* @see CommandArgument.Optional
53+
* @see optional
54+
*
55+
* @author Cypher121
56+
*/
57+
fun optional(): CommandArgument.Optional<S, D> {
58+
return CommandArgument.Optional(builder, name, descriptor)
59+
}
60+
}
61+
62+
/**
63+
* An argument ready to be registered into a command.
64+
* Can be either optional or required.
65+
*/
66+
sealed class CommandArgument<S, out B : ArgumentBuilder<S, *>, out D : ArgumentDescriptor<*>, out A>(
67+
val builder: B,
68+
val name: String,
69+
val descriptor: D
70+
) {
71+
/**
72+
* Registers the argument on the [parentBuilder].
73+
*
74+
* Exact behavior differs between [Required]
75+
* and [Optional] arguments.
76+
*
77+
* @see Required.register
78+
* @see Optional.register
79+
*/
80+
abstract fun register(parentBuilder: ArgumentBuilder<S, *>, action: B.(A) -> Unit)
81+
82+
/**
83+
* [CommandArgument] that must be present in the command.
84+
*
85+
* @see required
86+
*/
87+
class Required<S, B : ArgumentBuilder<S, *>, D : ArgumentDescriptor<*>>(
88+
builder: B,
89+
name: String,
90+
descriptor: D
91+
) : CommandArgument<S, B, D, ArgumentAccessor<S, D>>(builder, name, descriptor) {
92+
/**
93+
* Registers the argument on the [parentBuilder]
94+
* as a required argument and further configures the
95+
* resulting subcommand with the given [action].
96+
*
97+
* Accessor passed to [action] can be used on a [CommandContext]
98+
* within an [execute] block to obtain an [ArgumentReader] for this argument.
99+
*/
100+
// will inline if type is known, e.g. in `required`
101+
// calls normally otherwise
102+
@Suppress("OVERRIDE_BY_INLINE")
103+
override inline fun register(
104+
parentBuilder: ArgumentBuilder<S, *>,
105+
action: B.(ArgumentAccessor<S, D>) -> Unit
106+
) {
107+
builder.action {
108+
ArgumentReader(this, name, descriptor)
109+
}
110+
111+
parentBuilder.then(builder)
112+
}
113+
}
114+
115+
/**
116+
* [CommandArgument] that may be absent from the command.
117+
*
118+
* @see optional
119+
*/
120+
class Optional<S, D : ArgumentDescriptor<*>>(
121+
builder: ArgumentBuilder<S, *>,
122+
name: String,
123+
descriptor: D
124+
) : CommandArgument<S, ArgumentBuilder<S, *>, D, ArgumentAccessor<S, D>?>(builder, name, descriptor) {
125+
/**
126+
* Registers the argument on the [parentBuilder]
127+
* as an optional argument and further configures the
128+
* resulting subcommands with the given [action].
129+
*
130+
* The [action] is called once on the argument's builder,
131+
* and once on the parent builder, creating a branching path.
132+
*
133+
* On the path where the argument is present, the accessor
134+
* passed to [action] is not `null` and can be used on a [CommandContext]
135+
* within an [execute] block to obtain an [ArgumentReader] for this argument.
136+
*
137+
* On the path where the argument is not present,
138+
* the argument passed to [action] is instead `null`.
139+
*/
140+
// will inline if type is known, e.g. in `optional`
141+
// calls normally otherwise
142+
@Suppress("OVERRIDE_BY_INLINE")
143+
override inline fun register(
144+
parentBuilder: ArgumentBuilder<S, *>,
145+
action: (ArgumentBuilder<S, *>.(ArgumentAccessor<S, D>?) -> Unit)
146+
) {
147+
builder.action {
148+
ArgumentReader(this, name, descriptor)
149+
}
150+
151+
parentBuilder.then(builder)
152+
153+
parentBuilder.action(null)
154+
}
155+
}
156+
}
157+
158+
/**
159+
* Creates an argument of the specified [argumentType] with the
160+
* specified parameter [name] and [argumentDescriptor].
161+
*/
162+
@BrigadierDsl
163+
fun <S, D : ArgumentDescriptor<A>, AT, A : ArgumentType<AT>> argument(
164+
name: String,
165+
argumentType: A,
166+
argumentDescriptor: D
167+
): RequiredArgumentConstructor<S, D> {
168+
val builder = RequiredArgumentBuilder.argument<S, AT>(
169+
name,
170+
argumentType
171+
)
172+
173+
return ArgumentConstructor(builder, name, argumentDescriptor)
174+
}
175+
176+
/**
177+
* Creates an argument of the specified [argumentType] with the
178+
* specified parameter [name] and a [DefaultArgumentDescriptor]
179+
* for the argument type used.
180+
*/
181+
@BrigadierDsl
182+
fun <S, AT, A : ArgumentType<AT>> argument(
183+
name: String,
184+
argumentType: A
185+
): RequiredArgumentConstructor<S, DefaultArgumentDescriptor<A>> {
186+
return argument(name, argumentType, DefaultArgumentDescriptor())
187+
}
188+
189+
/**
190+
* Registers the argument specified by the [constructor]
191+
* as a required argument and further configures the
192+
* resulting subcommand with the given [action].
193+
*
194+
* Accessor passed to [action] can be used on a [CommandContext]
195+
* within an [execute] block to obtain an [ArgumentReader] for this argument.
196+
*
197+
* @see CommandArgument.Required
198+
*
199+
* @author Cypher121
200+
*/
201+
@BrigadierDsl
202+
inline fun <S, B : ArgumentBuilder<S, *>, D : ArgumentDescriptor<*>> ArgumentBuilder<S, *>.required(
203+
constructor: ArgumentConstructor<S, B, D>,
204+
action: B.(ArgumentAccessor<S, D>) -> Unit
205+
) {
206+
constructor.required().register(this, action)
207+
}
208+
209+
/**
210+
* Registers the argument specified by the [constructor]
211+
* as an optional argument and further configures the
212+
* resulting subcommands with the given [action].
213+
*
214+
* The [action] is called once on the argument's builder,
215+
* and once on the parent builder, creating a branching path.
216+
*
217+
* On the path where the argument is present, the accessor
218+
* passed to [action] is not `null` and can be used on a [CommandContext]
219+
* within an [execute] block to obtain an [ArgumentReader] for this argument.
220+
*
221+
* On the path where the argument is not present,
222+
* the argument passed to [action] is instead `null`.
223+
*
224+
* @see CommandArgument.Optional
225+
*/
226+
@BrigadierDsl
227+
inline fun <S, D : ArgumentDescriptor<*>> ArgumentBuilder<S, *>.optional(
228+
constructor: ArgumentConstructor<S, *, D>,
229+
action: ArgumentBuilder<S, *>.(ArgumentAccessor<S, D>?) -> Unit
230+
) {
231+
constructor.optional().register(this, action)
232+
}

0 commit comments

Comments
 (0)