Skip to content

Commit 8436108

Browse files
committedJan 20, 2025
Add MinoBot config GUI
1 parent 5f5b8b0 commit 8436108

File tree

15 files changed

+368
-35
lines changed

15 files changed

+368
-35
lines changed
 

‎build.gradle

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
plugins {
2-
id 'dev.architectury.loom' version '1.6-SNAPSHOT' apply false
2+
id 'dev.architectury.loom' version '1.7-SNAPSHOT' apply false
33
id 'architectury-plugin' version '3.4-SNAPSHOT'
44
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
55
id 'base'
@@ -38,6 +38,10 @@ subprojects {
3838
name = 'ParchmentMC'
3939
url = 'https://maven.parchmentmc.org'
4040
}
41+
maven {
42+
name = "Xander Maven"
43+
url = "https://maven.isxander.dev/releases"
44+
}
4145
}
4246

4347
dependencies {

‎common/build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ dependencies {
88
// Do NOT use other classes from Fabric Loader.
99
modImplementation "net.fabricmc:fabric-loader:$rootProject.fabric_loader_version"
1010

11+
modImplementation "dev.isxander:yet-another-config-lib:$rootProject.yacl_version-fabric"
1112
}

‎common/src/main/java/cn/zbx1425/minopp/Mino.java

+4
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
import cn.zbx1425.minopp.item.ItemHandCards;
1010
import cn.zbx1425.minopp.network.C2SPlayCardPacket;
1111
import cn.zbx1425.minopp.network.C2SSeatControlPacket;
12+
import cn.zbx1425.minopp.network.C2SAutoPlayerConfigPacket;
1213
import cn.zbx1425.minopp.network.S2CActionEphemeralPacket;
1314
import cn.zbx1425.minopp.network.S2CEffectListPacket;
15+
import cn.zbx1425.minopp.network.S2CAutoPlayerScreenPacket;
1416
import cn.zbx1425.minopp.platform.GroupedItem;
1517
import cn.zbx1425.minopp.platform.RegistriesWrapper;
1618
import cn.zbx1425.minopp.platform.RegistryObject;
@@ -77,8 +79,10 @@ public static void init(RegistriesWrapper registries) {
7779

7880
ServerPlatform.registerPacket(S2CActionEphemeralPacket.ID);
7981
ServerPlatform.registerPacket(S2CEffectListPacket.ID);
82+
ServerPlatform.registerPacket(S2CAutoPlayerScreenPacket.ID);
8083
ServerPlatform.registerNetworkReceiver(C2SPlayCardPacket.ID, C2SPlayCardPacket::handleC2S);
8184
ServerPlatform.registerNetworkReceiver(C2SSeatControlPacket.ID, C2SSeatControlPacket::handleC2S);
85+
ServerPlatform.registerNetworkReceiver(C2SAutoPlayerConfigPacket.ID, C2SAutoPlayerConfigPacket::handleC2S);
8286
}
8387

8488
public static boolean onServerChatMessage(String rawText, ServerPlayer sender) {

‎common/src/main/java/cn/zbx1425/minopp/MinoClient.java

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import cn.zbx1425.minopp.network.S2CActionEphemeralPacket;
44
import cn.zbx1425.minopp.network.S2CEffectListPacket;
5+
import cn.zbx1425.minopp.network.S2CAutoPlayerScreenPacket;
56
import cn.zbx1425.minopp.platform.ClientPlatform;
67
import cn.zbx1425.minopp.effect.EffectQueue;
78
import cn.zbx1425.minopp.platform.RegistryObject;
@@ -25,6 +26,7 @@ public static void init() {
2526
ClientPlatform.registerKeyBinding(KEY_SHOUT_MODIFIER);
2627
ClientPlatform.registerNetworkReceiver(S2CActionEphemeralPacket.ID, S2CActionEphemeralPacket.Client::handleS2C);
2728
ClientPlatform.registerNetworkReceiver(S2CEffectListPacket.ID, S2CEffectListPacket.Client::handleS2C);
29+
ClientPlatform.registerNetworkReceiver(S2CAutoPlayerScreenPacket.ID, S2CAutoPlayerScreenPacket.Client::handleS2C);
2830
}
2931

3032
public static void tick() {

‎common/src/main/java/cn/zbx1425/minopp/entity/EntityAutoPlayer.java

+75-28
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,20 @@
44
import cn.zbx1425.minopp.block.BlockEntityMinoTable;
55
import cn.zbx1425.minopp.block.BlockMinoTable;
66
import cn.zbx1425.minopp.game.*;
7+
import cn.zbx1425.minopp.gui.AutoPlayerScreen;
78
import cn.zbx1425.minopp.item.ItemHandCards;
9+
import cn.zbx1425.minopp.network.S2CAutoPlayerScreenPacket;
810
import com.mojang.authlib.GameProfile;
11+
import net.minecraft.client.Minecraft;
912
import net.minecraft.commands.arguments.EntityAnchorArgument;
1013
import net.minecraft.core.BlockPos;
1114
import net.minecraft.core.NonNullList;
1215
import net.minecraft.nbt.CompoundTag;
1316
import net.minecraft.network.syncher.EntityDataAccessor;
1417
import net.minecraft.network.syncher.EntityDataSerializers;
1518
import net.minecraft.network.syncher.SynchedEntityData;
19+
import net.minecraft.world.InteractionHand;
20+
import net.minecraft.world.InteractionResult;
1621
import net.minecraft.world.damagesource.DamageSource;
1722
import net.minecraft.world.entity.EntityType;
1823
import net.minecraft.world.entity.EquipmentSlot;
@@ -28,6 +33,7 @@
2833
import net.minecraft.world.level.block.state.BlockState;
2934
import net.minecraft.world.phys.Vec3;
3035
import org.jetbrains.annotations.NotNull;
36+
import net.minecraft.server.level.ServerPlayer;
3137

3238
import java.util.List;
3339
import java.util.Optional;
@@ -52,7 +58,7 @@ public EntityAutoPlayer(EntityType<? extends LivingEntity> entityType, Level lev
5258
private long thinkingFinishTime = 0;
5359
private long gameEndTime = 0;
5460

55-
private final AutoPlayer autoPlayer = new AutoPlayer();
61+
public final AutoPlayer autoPlayer = new AutoPlayer();
5662

5763
public CompletableFuture<Optional<GameProfile>> clientSkinGameProfile = CompletableFuture.completedFuture(Optional.empty());
5864
public String clientSkinGameProfileValidFor = "";
@@ -198,6 +204,21 @@ public boolean hurt(DamageSource source, float amount) {
198204
return super.hurt(source, amount);
199205
}
200206

207+
@Override
208+
public @NotNull InteractionResult interact(Player player, InteractionHand hand) {
209+
if (level().isClientSide) {
210+
if (player.hasPermissions(2) && player.isShiftKeyDown()) {
211+
return InteractionResult.SUCCESS;
212+
}
213+
} else {
214+
if (player instanceof ServerPlayer serverPlayer && player.hasPermissions(2) && player.isShiftKeyDown()) {
215+
S2CAutoPlayerScreenPacket.sendS2C(serverPlayer, this);
216+
return InteractionResult.SUCCESS;
217+
}
218+
}
219+
return super.interact(player, hand);
220+
}
221+
201222
@Override
202223
public @NotNull Iterable<ItemStack> getArmorSlots() {
203224
return armorItems;
@@ -233,16 +254,7 @@ public void addAdditionalSaveData(CompoundTag compound) {
233254
if (tablePos != null) compound.putLong("TablePos", tablePos.asLong());
234255
if (cardPlayer != null) compound.put("CardPlayer", cardPlayer.toTag());
235256
if (!entityData.get(HAND_STACK).isEmpty()) compound.put("HandStack", entityData.get(HAND_STACK).save(level().registryAccess(), new CompoundTag()));
236-
compound.putBoolean("Active", entityData.get(ACTIVE));
237-
compound.putString("Skin", entityData.get(SKIN));
238-
CompoundTag aiConfig = new CompoundTag();
239-
aiConfig.putBoolean("NoWin", autoPlayer.aiNoWin);
240-
aiConfig.putBoolean("NoPlayerDraw", autoPlayer.aiNoPlayerDraw);
241-
aiConfig.putBoolean("NoForget", autoPlayer.aiNoForget);
242-
aiConfig.putByte("NoDelay", autoPlayer.aiNoDelay);
243-
aiConfig.putBoolean("StartGame", autoPlayer.aiStartGame);
244-
compound.put("AI", aiConfig);
245-
if (noPush) compound.putBoolean("NoPush", true);
257+
writeConfigToTag(compound); // Write config values directly to maintain backward compatibility
246258
}
247259

248260
@Override
@@ -258,23 +270,7 @@ public void readAdditionalSaveData(CompoundTag compound) {
258270
} else {
259271
cardPlayer = null;
260272
}
261-
if (compound.contains("Active", CompoundTag.TAG_BYTE)) {
262-
entityData.set(ACTIVE, compound.getBoolean("Active"));
263-
}
264-
if (compound.contains("Skin", CompoundTag.TAG_STRING)) {
265-
entityData.set(SKIN, compound.getString("Skin"));
266-
}
267-
if (compound.contains("AI", CompoundTag.TAG_COMPOUND)) {
268-
CompoundTag aiConfig = compound.getCompound("AI");
269-
autoPlayer.aiNoWin = aiConfig.getBoolean("NoWin");
270-
autoPlayer.aiNoPlayerDraw = aiConfig.getBoolean("NoPlayerDraw");
271-
autoPlayer.aiNoForget = aiConfig.getBoolean("NoForget");
272-
autoPlayer.aiNoDelay = aiConfig.getByte("NoDelay");
273-
autoPlayer.aiStartGame = aiConfig.getBoolean("StartGame");
274-
}
275-
if (compound.contains("NoPush", CompoundTag.TAG_BYTE)) {
276-
noPush = compound.getBoolean("NoPush");
277-
}
273+
readConfigFromTag(compound); // Read config values directly from root tag
278274

279275
// Try fix hand stack
280276
if (tablePos != null && cardPlayer != null) {
@@ -308,5 +304,56 @@ public static AttributeSupplier createAttributes() {
308304
.add(Attributes.KNOCKBACK_RESISTANCE, 1.0D)
309305
.build();
310306
}
307+
308+
public boolean getActive() {
309+
return entityData.get(ACTIVE);
310+
}
311+
312+
public void setActive(boolean active) {
313+
entityData.set(ACTIVE, active);
314+
}
315+
316+
public boolean getNoPush() {
317+
return noPush;
318+
}
319+
320+
public void setNoPush(boolean noPush) {
321+
this.noPush = noPush;
322+
}
323+
324+
public String getSkin() {
325+
return entityData.get(SKIN);
326+
}
327+
328+
public void setSkin(String skin) {
329+
entityData.set(SKIN, skin);
330+
}
331+
332+
public CompoundTag writeConfigToTag() {
333+
CompoundTag tag = new CompoundTag();
334+
writeConfigToTag(tag);
335+
return tag;
336+
}
337+
338+
public void writeConfigToTag(CompoundTag tag) {
339+
tag.putBoolean("Active", getActive());
340+
tag.putBoolean("NoPush", getNoPush());
341+
tag.putString("Skin", getSkin());
342+
tag.put("AI", autoPlayer.toConfigNbt());
343+
}
344+
345+
public void readConfigFromTag(CompoundTag tag) {
346+
setActive(tag.getBoolean("Active"));
347+
setNoPush(tag.getBoolean("NoPush"));
348+
setSkin(tag.getString("Skin"));
349+
autoPlayer.useConfigNbt(tag.getCompound("AI"));
350+
}
351+
352+
private static class Client {
353+
354+
public static void openAutoPlayerScreen(EntityAutoPlayer autoPlayer) {
355+
Minecraft.getInstance().setScreen(AutoPlayerScreen.create(autoPlayer, Minecraft.getInstance().screen));
356+
}
357+
}
311358
}
312359

‎common/src/main/java/cn/zbx1425/minopp/game/AutoPlayer.java

+21-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package cn.zbx1425.minopp.game;
22

3+
import net.minecraft.nbt.CompoundTag;
34
import net.minecraft.server.MinecraftServer;
45

56
import java.util.Random;
@@ -10,13 +11,13 @@ public class AutoPlayer {
1011

1112
public boolean aiNoWin = false;
1213
public boolean aiNoPlayerDraw = false;
13-
public boolean aiNoForget = false;
14+
public float aiForgetChance = 0.2f;
1415
public byte aiNoDelay = 0;
1516
public boolean aiStartGame = false;
1617

1718
public ActionReport playAtGame(CardGame game, CardPlayer realPlayer, MinecraftServer server) {
1819
Card topCard = game.topCard;
19-
boolean forgetsMino = aiNoForget ? false : new Random().nextFloat() < 0.2;
20+
boolean forgetsMino = new Random().nextFloat() < aiForgetChance;
2021
boolean shoutsMino = !forgetsMino && realPlayer.hand.size() <= 2;
2122

2223
// If the next player is a human player, we should not play Draw cards
@@ -87,4 +88,22 @@ private Card.Suit getMostCommonSuit(CardPlayer realPlayer) {
8788
}
8889
return mostCommonSuit;
8990
}
91+
92+
public void useConfigNbt(CompoundTag aiConfig) {
93+
aiNoWin = aiConfig.getBoolean("NoWin");
94+
aiNoPlayerDraw = aiConfig.getBoolean("NoPlayerDraw");
95+
aiForgetChance = aiConfig.contains("ForgetChance", CompoundTag.TAG_FLOAT) ? aiConfig.getFloat("ForgetChance") : 0.2f;
96+
aiNoDelay = aiConfig.getByte("NoDelay");
97+
aiStartGame = aiConfig.getBoolean("StartGame");
98+
}
99+
100+
public CompoundTag toConfigNbt() {
101+
CompoundTag aiConfig = new CompoundTag();
102+
aiConfig.putBoolean("NoWin", aiNoWin);
103+
aiConfig.putBoolean("NoPlayerDraw", aiNoPlayerDraw);
104+
aiConfig.putFloat("ForgetChance", aiForgetChance);
105+
aiConfig.putByte("NoDelay", aiNoDelay);
106+
aiConfig.putBoolean("StartGame", aiStartGame);
107+
return aiConfig;
108+
}
90109
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package cn.zbx1425.minopp.gui;
2+
3+
import cn.zbx1425.minopp.entity.EntityAutoPlayer;
4+
import cn.zbx1425.minopp.network.C2SAutoPlayerConfigPacket;
5+
import dev.isxander.yacl3.api.*;
6+
import dev.isxander.yacl3.api.controller.BooleanControllerBuilder;
7+
import dev.isxander.yacl3.api.controller.FloatSliderControllerBuilder;
8+
import dev.isxander.yacl3.api.controller.IntegerSliderControllerBuilder;
9+
import dev.isxander.yacl3.api.controller.StringControllerBuilder;
10+
import net.minecraft.client.gui.screens.Screen;
11+
import net.minecraft.network.chat.Component;
12+
13+
public class AutoPlayerScreen {
14+
15+
public static Screen create(EntityAutoPlayer target, Screen parent) {
16+
ConfigCategory.Builder categoryBuilder = ConfigCategory.createBuilder();
17+
putEntityPreferences(categoryBuilder, target);
18+
putAIPreferences(categoryBuilder, target);
19+
return YetAnotherConfigLib.createBuilder()
20+
.title(Component.translatable("gui.minopp.bot_config.title"))
21+
.category(categoryBuilder.name(Component.translatable("gui.minopp.bot_config.title")).build())
22+
.save(() -> C2SAutoPlayerConfigPacket.Client.sendC2S(target))
23+
.build()
24+
.generateScreen(parent);
25+
}
26+
27+
private static void putEntityPreferences(ConfigCategory.Builder builder, EntityAutoPlayer target) {
28+
OptionGroup entityOpts = OptionGroup.createBuilder()
29+
.name(Component.translatable("gui.minopp.bot_config.category.entity"))
30+
.option(Option.<Boolean>createBuilder()
31+
.name(Component.translatable("gui.minopp.bot_config.active"))
32+
.binding(true, target::getActive, target::setActive)
33+
.controller(opt -> BooleanControllerBuilder.create(opt).onOffFormatter())
34+
.build()
35+
)
36+
.option(ButtonOption.createBuilder()
37+
.name(Component.translatable("gui.minopp.bot_config.remove"))
38+
.action((screen, opt) -> {
39+
C2SAutoPlayerConfigPacket.Client.sendDeleteC2S(target);
40+
screen.onClose();
41+
})
42+
.build()
43+
)
44+
.option(Option.<String>createBuilder()
45+
.name(Component.translatable("gui.minopp.bot_config.custom_name"))
46+
.binding("",
47+
() -> target.hasCustomName() ? target.getCustomName().getString() : "",
48+
v -> target.setCustomName(v.isEmpty() ? null : Component.literal(v)))
49+
.controller(opt -> StringControllerBuilder.create(opt))
50+
.build()
51+
)
52+
.option(Option.<String>createBuilder()
53+
.name(Component.translatable("gui.minopp.bot_config.skin"))
54+
.description(OptionDescription.of(Component.translatable("gui.minopp.bot_config.skin.summary")))
55+
.binding("", target::getSkin, target::setSkin)
56+
.controller(opt -> StringControllerBuilder.create(opt))
57+
.build()
58+
)
59+
.option(Option.<Boolean>createBuilder()
60+
.name(Component.translatable("gui.minopp.bot_config.no_push"))
61+
.description(OptionDescription.of(Component.translatable("gui.minopp.bot_config.no_push.summary")))
62+
.binding(false, target::getNoPush, target::setNoPush)
63+
.controller(opt -> BooleanControllerBuilder.create(opt).yesNoFormatter())
64+
.build()
65+
)
66+
.build();
67+
builder.group(entityOpts);
68+
}
69+
70+
private static void putAIPreferences(ConfigCategory.Builder builder, EntityAutoPlayer target) {
71+
OptionGroup aiOpts = OptionGroup.createBuilder()
72+
.name(Component.translatable("gui.minopp.bot_config.category.difficulty"))
73+
.option(Option.<Boolean>createBuilder()
74+
.name(Component.translatable("gui.minopp.bot_config.no_win"))
75+
.description(OptionDescription.of(Component.translatable("gui.minopp.bot_config.no_win.summary")))
76+
.binding(false, () -> target.autoPlayer.aiNoWin, value -> target.autoPlayer.aiNoWin = value)
77+
.controller(opt -> BooleanControllerBuilder.create(opt).yesNoFormatter())
78+
.build()
79+
)
80+
.option(Option.<Boolean>createBuilder()
81+
.name(Component.translatable("gui.minopp.bot_config.no_player_draw"))
82+
.description(OptionDescription.of(Component.translatable("gui.minopp.bot_config.no_player_draw.summary")))
83+
.binding(false, () -> target.autoPlayer.aiNoPlayerDraw, value -> target.autoPlayer.aiNoPlayerDraw = value)
84+
.controller(opt -> BooleanControllerBuilder.create(opt).yesNoFormatter())
85+
.build()
86+
)
87+
.option(Option.<Float>createBuilder()
88+
.name(Component.translatable("gui.minopp.bot_config.forget_chance"))
89+
.description(OptionDescription.of(Component.translatable("gui.minopp.bot_config.forget_chance.summary")))
90+
.binding(0.2f, () -> target.autoPlayer.aiForgetChance, value -> target.autoPlayer.aiForgetChance = value)
91+
.controller(opt -> FloatSliderControllerBuilder.create(opt).range(0f, 1f).step(0.05f)
92+
.formatValue(f -> Component.literal("%d%%".formatted((int)(f * 100)))))
93+
.build()
94+
)
95+
.build();
96+
OptionGroup aiMetaOpts = OptionGroup.createBuilder()
97+
.name(Component.translatable("gui.minopp.bot_config.category.behavior"))
98+
.option(Option.<Integer>createBuilder()
99+
.name(Component.translatable("gui.minopp.bot_config.no_delay"))
100+
.binding(0, () -> (int)target.autoPlayer.aiNoDelay, value -> target.autoPlayer.aiNoDelay = (byte)(int)value)
101+
.controller(opt -> IntegerSliderControllerBuilder.create(opt).range(0, 2).step(1).formatValue(i ->
102+
Component.translatable("gui.minopp.bot_config.no_delay." + i)))
103+
.build()
104+
)
105+
.option(Option.<Boolean>createBuilder()
106+
.name(Component.translatable("gui.minopp.bot_config.start_game"))
107+
.description(OptionDescription.of(Component.translatable("gui.minopp.bot_config.start_game.summary")))
108+
.binding(false, () -> target.autoPlayer.aiStartGame, value -> target.autoPlayer.aiStartGame = value)
109+
.controller(opt -> BooleanControllerBuilder.create(opt).yesNoFormatter())
110+
.build()
111+
)
112+
.build();
113+
builder.group(aiOpts).group(aiMetaOpts);
114+
}
115+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package cn.zbx1425.minopp.network;
2+
3+
import cn.zbx1425.minopp.Mino;
4+
import cn.zbx1425.minopp.entity.EntityAutoPlayer;
5+
import cn.zbx1425.minopp.platform.ClientPlatform;
6+
import io.netty.buffer.Unpooled;
7+
import net.minecraft.nbt.CompoundTag;
8+
import net.minecraft.network.FriendlyByteBuf;
9+
import net.minecraft.resources.ResourceLocation;
10+
import net.minecraft.server.MinecraftServer;
11+
import net.minecraft.server.level.ServerPlayer;
12+
13+
public class C2SAutoPlayerConfigPacket {
14+
15+
public static final ResourceLocation ID = Mino.id("auto_player_config");
16+
17+
public static void handleC2S(MinecraftServer server, ServerPlayer player, FriendlyByteBuf packet) {
18+
int entityId = packet.readInt();
19+
boolean shouldDelete = packet.readBoolean();
20+
CompoundTag config = shouldDelete ? null : packet.readNbt();
21+
22+
server.execute(() -> {
23+
if (!player.hasPermissions(2)) return; // Re-check permission on server side
24+
if (player.level().getEntity(entityId) instanceof EntityAutoPlayer autoPlayer) {
25+
if (shouldDelete) {
26+
autoPlayer.kill();
27+
} else {
28+
autoPlayer.readConfigFromTag(config);
29+
}
30+
}
31+
});
32+
}
33+
34+
public static class Client {
35+
public static void sendC2S(EntityAutoPlayer autoPlayer) {
36+
FriendlyByteBuf packet = new FriendlyByteBuf(Unpooled.buffer());
37+
packet.writeInt(autoPlayer.getId());
38+
packet.writeBoolean(false); // Not deleting
39+
packet.writeNbt(autoPlayer.writeConfigToTag());
40+
ClientPlatform.sendPacketToServer(ID, packet);
41+
}
42+
43+
public static void sendDeleteC2S(EntityAutoPlayer autoPlayer) {
44+
FriendlyByteBuf packet = new FriendlyByteBuf(Unpooled.buffer());
45+
packet.writeInt(autoPlayer.getId());
46+
packet.writeBoolean(true); // Deleting
47+
ClientPlatform.sendPacketToServer(ID, packet);
48+
}
49+
}
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package cn.zbx1425.minopp.network;
2+
3+
import cn.zbx1425.minopp.Mino;
4+
import cn.zbx1425.minopp.entity.EntityAutoPlayer;
5+
import cn.zbx1425.minopp.gui.AutoPlayerScreen;
6+
import cn.zbx1425.minopp.platform.ServerPlatform;
7+
import io.netty.buffer.Unpooled;
8+
import net.minecraft.client.Minecraft;
9+
import net.minecraft.nbt.CompoundTag;
10+
import net.minecraft.network.FriendlyByteBuf;
11+
import net.minecraft.resources.ResourceLocation;
12+
import net.minecraft.server.level.ServerPlayer;
13+
14+
public class S2CAutoPlayerScreenPacket {
15+
16+
public static final ResourceLocation ID = Mino.id("auto_player_screen");
17+
18+
public static void sendS2C(ServerPlayer target, EntityAutoPlayer autoPlayer) {
19+
FriendlyByteBuf packet = new FriendlyByteBuf(Unpooled.buffer());
20+
packet.writeInt(autoPlayer.getId());
21+
packet.writeNbt(autoPlayer.writeConfigToTag());
22+
ServerPlatform.sendPacketToPlayer(target, ID, packet);
23+
}
24+
25+
public static class Client {
26+
27+
public static void handleS2C(FriendlyByteBuf packet) {
28+
int entityId = packet.readInt();
29+
CompoundTag config = packet.readNbt();
30+
31+
Minecraft.getInstance().execute(() -> {
32+
if (Minecraft.getInstance().level.getEntity(entityId) instanceof EntityAutoPlayer autoPlayer) {
33+
autoPlayer.readConfigFromTag(config);
34+
Minecraft.getInstance().setScreen(AutoPlayerScreen.create(autoPlayer, Minecraft.getInstance().screen));
35+
}
36+
});
37+
}
38+
}
39+
}

‎common/src/main/resources/assets/minopp/lang/en_us.json

+25-1
Original file line numberDiff line numberDiff line change
@@ -91,5 +91,29 @@
9191
"subtitles.minopp.doubt_success": "Mino doubt proven successful",
9292

9393
"key.categories.minopp": "Mino++",
94-
"key.minopp.shout_modifier": "Shout Mino while Playing"
94+
"key.minopp.shout_modifier": "Shout Mino while Playing",
95+
96+
"gui.minopp.bot_config.title": "MinoBot Preferences",
97+
"gui.minopp.bot_config.category.entity": "MinoBot Entity",
98+
"gui.minopp.bot_config.active": "Power",
99+
"gui.minopp.bot_config.remove": "Dismantle",
100+
"gui.minopp.bot_config.custom_name": "Custom Name",
101+
"gui.minopp.bot_config.skin": "Skin",
102+
"gui.minopp.bot_config.skin.summary": "Name or UUID of the player whose skin is to be used.",
103+
"gui.minopp.bot_config.no_push": "Disable Pushing",
104+
"gui.minopp.bot_config.no_push.summary": "Prevent the bot from being moved by other players by pushing.",
105+
"gui.minopp.bot_config.category.difficulty": "AI Difficulty",
106+
"gui.minopp.bot_config.no_win": "Avoid Winning",
107+
"gui.minopp.bot_config.no_win.summary": "If the bot should never play its last card.",
108+
"gui.minopp.bot_config.no_player_draw": "Avoid Making Players Draw",
109+
"gui.minopp.bot_config.no_player_draw.summary": "If the bot should avoid playing Draw cards when the next player is a human player.",
110+
"gui.minopp.bot_config.forget_chance": "Shout Forgetting Probability",
111+
"gui.minopp.bot_config.forget_chance.summary": "Chance of the bot forgetting to shout 'Mino' according to the rule.",
112+
"gui.minopp.bot_config.category.behavior": "AI Behavior",
113+
"gui.minopp.bot_config.no_delay": "Thinking Delay",
114+
"gui.minopp.bot_config.no_delay.0": "Normal",
115+
"gui.minopp.bot_config.no_delay.1": "Fast",
116+
"gui.minopp.bot_config.no_delay.2": "Instant",
117+
"gui.minopp.bot_config.start_game": "Proposes Starting the Game",
118+
"gui.minopp.bot_config.start_game.summary": "If the bot should automatically propose starting the game when enough players are seated."
95119
}

‎common/src/main/resources/assets/minopp/lang/zh_cn.json

+26-1
Original file line numberDiff line numberDiff line change
@@ -91,5 +91,30 @@
9191
"subtitles.minopp.doubt_success": "Mino: 怀疑成功",
9292

9393
"key.categories.minopp": "Mino++",
94-
"key.minopp.shout_modifier": "出牌同时喊出 Mino"
94+
"key.minopp.shout_modifier": "出牌同时喊出 Mino",
95+
96+
"gui.minopp.bot_config.title": "MinoBot 机器人偏好设置",
97+
"gui.minopp.bot_config.category.entity": "MinoBot 实体设定",
98+
"gui.minopp.bot_config.active": "电源",
99+
"gui.minopp.bot_config.remove": "拆除",
100+
"gui.minopp.bot_config.custom_name": "自定义名称",
101+
"gui.minopp.bot_config.skin": "皮肤",
102+
"gui.minopp.bot_config.skin.summary": "如要使用某玩家的皮肤,在此处填入名称或 UUID。",
103+
"gui.minopp.bot_config.no_push": "禁用推动",
104+
"gui.minopp.bot_config.no_push.summary": "防止其他玩家推动机器人。",
105+
"gui.minopp.bot_config.category.difficulty": "AI 难度设定",
106+
"gui.minopp.bot_config.no_win": "避免获胜",
107+
"gui.minopp.bot_config.no_win.summary": "启用时,机器人从不打出最后一张牌。",
108+
"gui.minopp.bot_config.no_player_draw": "避免让玩家抽牌",
109+
"gui.minopp.bot_config.no_player_draw.summary": "启用时,如果下一位玩家是真人,机器人就会避免出 +2 或 +4。",
110+
"gui.minopp.bot_config.forget_chance": "Mino 忘记概率",
111+
"gui.minopp.bot_config.forget_chance.summary": "机器人忘记喊 'Mino' 的概率。",
112+
"gui.minopp.bot_config.category.behavior": "AI 行为设定",
113+
"gui.minopp.bot_config.no_delay": "思考延迟",
114+
"gui.minopp.bot_config.no_delay.0": "正常",
115+
"gui.minopp.bot_config.no_delay.1": "快速",
116+
"gui.minopp.bot_config.no_delay.2": "最快",
117+
"gui.minopp.bot_config.start_game": "自动开始游戏",
118+
"gui.minopp.bot_config.start_game.summary": "启用时,机器人在入座人数满足条件时自动提议开始游戏。"
119+
95120
}

‎fabric/build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ dependencies {
2929

3030
modImplementation "net.fabricmc.fabric-api:fabric-api:$rootProject.fabric_api_version"
3131

32+
modImplementation "dev.isxander:yet-another-config-lib:$rootProject.yacl_version-fabric"
3233

3334
common(project(path: ':common', configuration: 'namedElements')) { transitive false }
3435
shadowBundle project(path: ':common', configuration: 'transformProductionFabric')

‎gradle.properties

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
org.gradle.jvmargs=-Xmx4G
33

44
# Mod properties
5-
mod_version=1.0.4
5+
mod_version=1.1.0
66
maven_group=cn.zbx1425
77
archives_name=minopp
88
enabled_platforms=fabric,neoforge
@@ -12,3 +12,4 @@ minecraft_version=1.21.1
1212
fabric_loader_version=0.16.2
1313
fabric_api_version=0.102.1+1.21.1
1414
neoforge_version=21.1.35
15+
yacl_version=3.6.1+1.21

‎neoforge/build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ dependencies {
4848
modImplementation ("org.teacon:SignMeUp-NeoForge-1.21.1:+") { transitive false }
4949

5050
// modImplementation "dev.architectury:architectury-neoforge:$rootProject.architectury_api_version"
51+
modImplementation "dev.isxander:yet-another-config-lib:$rootProject.yacl_version-neoforge"
5152

5253
common(project(path: ':common', configuration: 'namedElements')) { transitive false }
5354
shadowBundle project(path: ':common', configuration: 'transformProductionNeoForge')

‎neoforge/src/main/java/cn/zbx1425/minopp/neoforge/compat/touhou_little_maid/task/PlayMinoTask.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public class PlayMinoTask extends MaidCheckRateTask {
3131
public PlayMinoTask() {
3232
super(ImmutableMap.of(MemoryModuleType.WALK_TARGET, MemoryStatus.VALUE_ABSENT,
3333
InitEntities.TARGET_POS.get(), MemoryStatus.VALUE_ABSENT));
34-
autoPlayer.aiNoForget = true;
34+
autoPlayer.aiForgetChance = 0;
3535
}
3636

3737
@Override

0 commit comments

Comments
 (0)
Please sign in to comment.