Skip to content

Commit 77fff5e

Browse files
authored
AutoSign module (#236)
1 parent 9bd8bf7 commit 77fff5e

File tree

4 files changed

+129
-4
lines changed

4 files changed

+129
-4
lines changed

src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,7 @@
1818
package com.lambda.mixin.entity;
1919

2020
import com.lambda.event.EventFlow;
21-
import com.lambda.event.events.MovementEvent;
22-
import com.lambda.event.events.PlayerEvent;
23-
import com.lambda.event.events.PlayerPacketEvent;
24-
import com.lambda.event.events.TickEvent;
21+
import com.lambda.event.events.*;
2522
import com.lambda.interaction.managers.rotating.RotationManager;
2623
import com.lambda.module.modules.movement.ElytraFly;
2724
import com.lambda.module.modules.movement.NoJumpCooldown;
@@ -35,6 +32,7 @@
3532
import com.llamalad7.mixinextras.sugar.Share;
3633
import com.llamalad7.mixinextras.sugar.ref.LocalRef;
3734
import com.mojang.authlib.GameProfile;
35+
import net.minecraft.block.entity.SignBlockEntity;
3836
import net.minecraft.client.gui.screen.Screen;
3937
import net.minecraft.client.input.Input;
4038
import net.minecraft.client.network.AbstractClientPlayerEntity;
@@ -78,6 +76,13 @@ private void injectTick(CallbackInfo ci, @Share(namespace = "shared_rotations",
7876
}
7977
}
8078

79+
@Inject(method = "openEditSignScreen", at = @At("HEAD"), cancellable = true)
80+
private void onOpenEditSignScreen(SignBlockEntity sign, boolean front, CallbackInfo ci) {
81+
if (EventFlow.post(new GuiEvent.SignEditorOpen(sign, front)).isCanceled()) {
82+
ci.cancel();
83+
}
84+
}
85+
8186
@WrapOperation(method = "move", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/AbstractClientPlayerEntity;move(Lnet/minecraft/entity/MovementType;Lnet/minecraft/util/math/Vec3d;)V"))
8287
private void wrapMove(ClientPlayerEntity instance, MovementType movementType, Vec3d vec3d, Operation<Void> original) {
8388
EventFlow.post(new MovementEvent.Player.Pre(movementType, vec3d));

src/main/kotlin/com/lambda/event/events/GuiEvent.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
package com.lambda.event.events
1919

2020
import com.lambda.event.Event
21+
import com.lambda.event.callback.Cancellable
22+
import com.lambda.event.callback.ICancellable
23+
import net.minecraft.block.entity.SignBlockEntity
2124

2225
sealed class GuiEvent {
2326
/**
@@ -33,4 +36,12 @@ sealed class GuiEvent {
3336
* By default, the game's framebuffer is bound.
3437
*/
3538
data object EndFrame : Event
39+
40+
/**
41+
* Triggered when the sign editor GUI is opened. Can be canceled.
42+
*/
43+
data class SignEditorOpen(
44+
var sign: SignBlockEntity,
45+
var front: Boolean
46+
) : ICancellable by Cancellable()
3647
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Copyright 2026 Lambda
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
package com.lambda.module.modules.player
19+
20+
import com.ibm.icu.util.Calendar
21+
import com.lambda.event.events.GuiEvent
22+
import com.lambda.event.listener.SafeListener.Companion.listen
23+
import com.lambda.module.Module
24+
import com.lambda.module.tag.ModuleTag
25+
import com.lambda.threading.runConcurrent
26+
import com.lambda.threading.runSafeGameScheduled
27+
import kotlinx.coroutines.delay
28+
import net.minecraft.block.entity.HangingSignBlockEntity
29+
import net.minecraft.client.gui.screen.ingame.AbstractSignEditScreen
30+
import net.minecraft.client.gui.screen.ingame.HangingSignEditScreen
31+
import net.minecraft.client.gui.screen.ingame.SignEditScreen
32+
import net.minecraft.network.packet.c2s.play.UpdateSignC2SPacket
33+
import java.util.*
34+
35+
@Suppress("unused")
36+
class AutoSign : Module(
37+
name = "AutoSign",
38+
description = """Auto fills signs with customizable text. Leave lines empty to skip them. Supports data formatting with:
39+
|<d> - Day of month (1-31)
40+
|<dd> - Day of month (01-31)
41+
|<M> - Month (1-12)
42+
|<MM> - Month (01-12)
43+
|<MMM> - Month (short name, e.g., Jan)
44+
|<MMMM> - Month (full name, e.g., January)
45+
|<yy> - Year (last two digits, e.g., 26)
46+
|<yyyy> - Year (e.g., 2026)
47+
|<HH> - Hour (00-23)
48+
|<mm> - Minute (00-59)
49+
|<ss> - Second (00-59)
50+
""".trimMargin(),
51+
tag = ModuleTag.PLAYER
52+
) {
53+
var autoWrite by setting("Auto Write", true)
54+
var line1 by setting("Line 1", "Welcome to Lambda!") { autoWrite }
55+
var line2 by setting("Line 2", "Enjoy your stay.") { autoWrite }
56+
var line3 by setting("Line 3", "Have fun!") { autoWrite }
57+
var line4 by setting("Line 4", "Lambda <dd>/<M>/<yy>") { autoWrite }
58+
var writeOnFront by setting("Write Front", true, description = "Write on front side of the sign") { autoWrite }
59+
60+
var autoClose by setting("Auto Close", true)
61+
var signWriteDelay by setting("Sign Write Delay", 400L, 100L..1000L, 50L, description = "Delay in milliseconds before sending the sign text to the server") { autoClose }
62+
63+
init {
64+
listen<GuiEvent.SignEditorOpen> { event ->
65+
val lines = Array(4) { i -> event.component1().frontText.getMessages(false)[i].string }
66+
if (autoWrite) {
67+
var formatLines = arrayOf(line1, line2, line3, line4)
68+
val calendar = Calendar.getInstance()
69+
val month = calendar.get(Calendar.MONTH) + 1 // Months are 0-based in Calendar
70+
71+
for (i in 0 until 4) {
72+
val formattedLine = formatLines[i]
73+
.replace("<dd>", String.format($$"%1$td", Date()))
74+
.replace("<d>", String.format($$"%1$te", Date()))
75+
.replace("<M>", month.toString())
76+
.replace("<MM>", String.format("%02d", month))
77+
.replace("<MMM>", String.format($$"%1$tb", Date()))
78+
.replace("<MMMM>", String.format($$"%1$tB", Date()))
79+
.replace("<yy>", String.format($$"%1$ty", Date()))
80+
.replace("<yyyy>", String.format($$"%1$tY", Date()))
81+
.replace("<HH>", String.format($$"%1$tH", Date()))
82+
.replace("<mm>", String.format($$"%1$tM", Date()))
83+
.replace("<ss>", String.format($$"%1$tS", Date()))
84+
85+
if (formattedLine.isNotEmpty()) lines[i] = String.format(formattedLine, Date())
86+
}
87+
}
88+
89+
var editor: AbstractSignEditScreen = if (event.sign is HangingSignBlockEntity) HangingSignEditScreen(event.sign, event.front, mc.shouldFilterText())
90+
else SignEditScreen(event.sign, event.front, mc.shouldFilterText())
91+
for (i in 0 until 4) editor.messages[i] = lines[i]
92+
if (autoClose) {
93+
val pos = event.sign.pos
94+
val messages = editor.messages.copyOf()
95+
runConcurrent {
96+
delay(signWriteDelay)
97+
runSafeGameScheduled {
98+
if (writeOnFront) connection.sendPacket(UpdateSignC2SPacket(pos, true, messages[0], messages[1], messages[2], messages[3]))
99+
else connection.sendPacket(UpdateSignC2SPacket(pos, false, messages[0], messages[1], messages[2], messages[3]))
100+
}
101+
}
102+
} else {
103+
mc.setScreen(editor)
104+
}
105+
event.cancel()
106+
}
107+
}
108+
}

src/main/resources/lambda.accesswidener

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,3 +154,4 @@ transitive-accessible method net/minecraft/item/BlockItem getPlacementState (Lne
154154
transitive-accessible method net/minecraft/block/AbstractBlock getPickStack (Lnet/minecraft/world/WorldView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Z)Lnet/minecraft/item/ItemStack;
155155
transitive-accessible field net/minecraft/client/gui/screen/ingame/HandledScreen focusedSlot Lnet/minecraft/screen/slot/Slot;
156156
transitive-accessible field net/minecraft/registry/SimpleRegistry frozen Z
157+
transitive-accessible field net/minecraft/client/gui/screen/ingame/AbstractSignEditScreen messages [Ljava/lang/String;

0 commit comments

Comments
 (0)