Skip to content

Commit f485076

Browse files
feat: Command Manager
Use an accessTransformer due to Essential mixin failing to apply in default stage if we use an accessor
1 parent 8839675 commit f485076

File tree

9 files changed

+267
-0
lines changed

9 files changed

+267
-0
lines changed

build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ loom {
6666
property("mixin.debug.verbose", "true")
6767
property("mixin.debug.export", "true")
6868
property("mixin.dumpTargetOnFailure", "true")
69+
property("fml.debugAccessTransformer", "true")
6970
property("legacy.debugClassLoading", "true")
7071
property("legacy.debugClassLoadingSave", "true")
7172
property("legacy.debugClassLoadingFiner", "true")
@@ -78,6 +79,7 @@ loom {
7879
}
7980
forge {
8081
mixinConfig("mixins.skytils.json", "mixins.skytils-events.json")
82+
accessTransformer("src/main/resources/META-INF/accesstransformer.cfg")
8183
}
8284
mixin {
8385
defaultRefmapName = "mixins.skytils.refmap.json"
@@ -180,6 +182,7 @@ tasks {
180182
attributes(
181183
mapOf(
182184
"Main-Class" to "SkytilsInstallerFrame",
185+
"FMLAT" to "accesstransformer.cfg",
183186
"FMLCorePlugin" to "gg.skytils.skytilsmod.tweaker.SkytilsLoadingPlugin",
184187
"FMLCorePluginContainsFMLMod" to true,
185188
"ForceLoadAsMod" to true,

src/main/java/gg/skytils/skytilsmod/mixins/transformers/accessors/AccessorCommandHandler.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,13 @@ public interface AccessorCommandHandler {
3131
@Accessor
3232
Set<ICommand> getCommandSet();
3333

34+
@Accessor
35+
void setCommandSet(Set<ICommand> set);
36+
3437
@Accessor
3538
Map<String, ICommand> getCommandMap();
3639

40+
@Accessor
41+
void setCommandMap(Map<String, ICommand> map);
42+
3743
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Skytils - Hypixel Skyblock Quality of Life Mod
3+
* Copyright (C) 2020-2024 Skytils
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Affero General Public License as published
7+
* by the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public License
16+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
*/
18+
19+
package gg.skytils.skytilsmod.mixins.transformers.accessors;
20+
21+
import com.google.common.collect.ListMultimap;
22+
import net.minecraftforge.fml.common.LoadController;
23+
import net.minecraftforge.fml.common.ModContainer;
24+
import org.spongepowered.asm.mixin.Mixin;
25+
import org.spongepowered.asm.mixin.gen.Accessor;
26+
27+
@Mixin(LoadController.class)
28+
public interface AccessorLoadController {
29+
@Accessor
30+
ListMultimap<String, ModContainer> getPackageOwners();
31+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Skytils - Hypixel Skyblock Quality of Life Mod
3+
* Copyright (C) 2020-2024 Skytils
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Affero General Public License as published
7+
* by the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public License
16+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
*/
18+
19+
package gg.skytils.skytilsmod.mixins.transformers.forge;
20+
21+
import gg.skytils.skytilsmod.features.impl.handlers.CommandManager;
22+
import gg.skytils.skytilsmod.mixins.transformers.accessors.AccessorCommandHandler;
23+
import gg.skytils.skytilsmod.utils.ObservableSet;
24+
import net.minecraft.command.CommandHandler;
25+
import net.minecraft.command.ICommand;
26+
import net.minecraftforge.client.ClientCommandHandler;
27+
import org.spongepowered.asm.mixin.Mixin;
28+
import org.spongepowered.asm.mixin.injection.At;
29+
import org.spongepowered.asm.mixin.injection.Inject;
30+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
31+
32+
@Mixin(value = ClientCommandHandler.class)
33+
public abstract class MixinClientCommandHandler extends CommandHandler implements AccessorCommandHandler {
34+
@Inject(method = "<init>", at = @At("RETURN"))
35+
private void hijackCommandHandler(CallbackInfo ci) {
36+
ObservableSet<ICommand> hijacked = new ObservableSet<>(this.getCommandSet());
37+
CommandManager.INSTANCE.setup(hijacked);
38+
this.setCommandSet(hijacked);
39+
}
40+
}

src/main/kotlin/gg/skytils/skytilsmod/Skytils.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,7 @@ class Skytils {
307307
ChangeAllToSameColorSolver,
308308
DungeonChestProfit,
309309
ClickInOrderSolver,
310+
CommandManager,
310311
CreeperSolver,
311312
CommandAliases,
312313
ContainerSellValue,
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Skytils - Hypixel Skyblock Quality of Life Mod
3+
* Copyright (C) 2020-2024 Skytils
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Affero General Public License as published
7+
* by the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public License
16+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
*/
18+
19+
package gg.skytils.skytilsmod.features.impl.handlers
20+
21+
import gg.essential.universal.UChat
22+
import gg.skytils.skytilsmod.Skytils.Companion.mc
23+
import gg.skytils.skytilsmod.events.impl.SendChatMessageEvent
24+
import gg.skytils.skytilsmod.mixins.transformers.accessors.AccessorCommandHandler
25+
import gg.skytils.skytilsmod.mixins.transformers.accessors.AccessorLoadController
26+
import gg.skytils.skytilsmod.utils.ObservableAddEvent
27+
import gg.skytils.skytilsmod.utils.ObservableClearEvent
28+
import gg.skytils.skytilsmod.utils.ObservableRemoveEvent
29+
import gg.skytils.skytilsmod.utils.ObservableSet
30+
import net.minecraft.command.ICommand
31+
import net.minecraftforge.client.ClientCommandHandler
32+
import net.minecraftforge.fml.common.Loader
33+
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
34+
35+
object CommandManager {
36+
val cch by lazy {
37+
ClientCommandHandler.instance as AccessorCommandHandler
38+
}
39+
40+
val loadController by lazy {
41+
Loader.instance().modController as AccessorLoadController
42+
}
43+
44+
val aliasMap = mutableMapOf<ICommand, String>()
45+
46+
fun setup(listeningSet: ObservableSet<ICommand>) {
47+
listeningSet.addObserver { _, arg ->
48+
when (arg) {
49+
is ObservableAddEvent<*> -> {
50+
registerCommandHelper(arg.element as ICommand)
51+
}
52+
is ObservableRemoveEvent<*> -> {
53+
cch.commandMap.remove(aliasMap.remove(arg.element))
54+
}
55+
is ObservableClearEvent<*> -> {
56+
aliasMap.entries.removeAll {
57+
cch.commandMap.remove(it.value)
58+
true
59+
}
60+
}
61+
}
62+
}
63+
64+
listeningSet.forEach {
65+
registerCommandHelper(it)
66+
}
67+
}
68+
69+
fun registerCommandHelper(command: ICommand) {
70+
val clazzName = command.javaClass.name
71+
val pkg = clazzName.substringBeforeLast('.')
72+
val owners = loadController.packageOwners[pkg].distinct()
73+
if (owners.size != 1) {
74+
println("WARNING! Command $clazzName has ${owners.size}; owners: $owners")
75+
}
76+
77+
val owner = owners.firstOrNull()
78+
79+
val prefix = owner?.modId ?: owner?.name ?: "unknown"
80+
81+
val helper = "${prefix}:${command.commandName}"
82+
cch.commandMap[helper] = command
83+
84+
aliasMap[command] = helper
85+
}
86+
87+
@SubscribeEvent
88+
fun onSendChat(event: SendChatMessageEvent) {
89+
if (event.message.startsWith("/server:")) {
90+
event.isCanceled = true
91+
UChat.say('/' + event.message.substringAfter("/server:"))
92+
mc.ingameGUI.chatGUI.addToSentMessages(event.message)
93+
}
94+
}
95+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Skytils - Hypixel Skyblock Quality of Life Mod
3+
* Copyright (C) 2020-2024 Skytils
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Affero General Public License as published
7+
* by the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public License
16+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
*/
18+
19+
package gg.skytils.skytilsmod.utils
20+
21+
import java.util.*
22+
23+
/**
24+
* @see gg.essential.elementa.utils.ObservableList
25+
*/
26+
class ObservableSet<E>(val backingSet: MutableSet<E>) : MutableSet<E> by backingSet, Observable() {
27+
override fun add(element: E): Boolean {
28+
return backingSet.add(element).also {
29+
if (it) {
30+
setChanged()
31+
notifyObservers(ObservableAddEvent(element))
32+
}
33+
}
34+
}
35+
36+
override fun addAll(elements: Collection<E>): Boolean {
37+
return backingSet.addAll(elements).also {
38+
if (it) {
39+
elements.forEach {
40+
notifyObservers(ObservableAddEvent(it))
41+
}
42+
}
43+
}
44+
}
45+
46+
override fun remove(element: E): Boolean {
47+
return backingSet.remove(element).also {
48+
if (it) {
49+
notifyObservers(ObservableRemoveEvent(element))
50+
}
51+
}
52+
}
53+
54+
override fun removeAll(elements: Collection<E>): Boolean {
55+
return backingSet.removeAll(elements).also {
56+
if (it) {
57+
elements.forEach {
58+
notifyObservers(ObservableRemoveEvent(it))
59+
}
60+
}
61+
}
62+
}
63+
64+
override fun retainAll(elements: Collection<E>): Boolean {
65+
return backingSet.retainAll(elements).also {
66+
if (it) {
67+
backingSet.forEach {
68+
notifyObservers(ObservableRemoveEvent(it))
69+
}
70+
}
71+
}
72+
}
73+
74+
override fun clear() {
75+
notifyObservers(ObservableClearEvent(backingSet.toSet()))
76+
backingSet.clear()
77+
}
78+
}
79+
80+
sealed class ObservableSetEvent<E>
81+
82+
class ObservableAddEvent<E>(val element: E) : ObservableSetEvent<E>()
83+
84+
class ObservableRemoveEvent<E>(val element: E) : ObservableSetEvent<E>()
85+
86+
class ObservableClearEvent<E>(val oldChildren: Set<E>) : ObservableSetEvent<E>()
87+
88+
fun <E> MutableSet<E>.asObservable() = ObservableSet(this)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
public net.minecraftforge.fml.common.Loader modController

src/main/resources/mixins.skytils.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"accessors.AccessorGuiMainMenu",
2121
"accessors.AccessorGuiNewChat",
2222
"accessors.AccessorGuiStreamUnavailable",
23+
"accessors.AccessorLoadController",
2324
"accessors.AccessorMinecraft",
2425
"accessors.AccessorModelDragon",
2526
"accessors.AccessorRenderItem",
@@ -40,6 +41,7 @@
4041
"entity.MixinEntityFishHook",
4142
"entity.MixinEntityLivingBase",
4243
"entity.MixinEntityPlayerSP",
44+
"forge.MixinClientCommandHandler",
4345
"forge.MixinSplashProgressThread",
4446
"gui.MixinChatLine",
4547
"gui.MixinGuiContainer",

0 commit comments

Comments
 (0)