Skip to content

Commit 2176b68

Browse files
committed
Merge branch 'master' into 1.21.3
2 parents 5d19c26 + 6cab785 commit 2176b68

File tree

181 files changed

+6190
-4494
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

181 files changed

+6190
-4494
lines changed

build.gradle.kts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ subprojects {
8585

8686
if (path == ":common") return@subprojects
8787

88+
loom.runs {
89+
all {
90+
property("lambda.dev", "youtu.be/7iNbnineUCI")
91+
}
92+
}
93+
8894
tasks {
8995
register<Exec>("renderDoc") {
9096
val javaHome = Jvm.current().javaHome
@@ -144,7 +150,7 @@ allprojects {
144150
tasks {
145151
compileKotlin {
146152
compilerOptions {
147-
jvmTarget.set(JvmTarget.JVM_21)
153+
jvmTarget = JvmTarget.JVM_21
148154
}
149155
}
150156
}

common/build.gradle.kts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ val kotlinxCoroutinesVersion: String by project
2323
val discordIPCVersion: String by project
2424
val baritoneVersion: String by project
2525
val fuelVersion: String by project
26+
val resultVersion: String by project
2627

2728
base.archivesName = "${base.archivesName.get()}-api"
2829

@@ -47,9 +48,10 @@ dependencies {
4748
implementation("com.github.Edouard127:KDiscordIPC:$discordIPCVersion")
4849
implementation("com.pngencoder:pngencoder:0.15.0")
4950

50-
// Fuel HTTP library
51+
// Fuel HTTP library and dependencies
5152
implementation("com.github.kittinunf.fuel:fuel:$fuelVersion")
5253
implementation("com.github.kittinunf.fuel:fuel-gson:$fuelVersion")
54+
implementation("com.github.kittinunf.result:result-jvm:$resultVersion")
5355

5456
// Add Kotlin
5557
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinxCoroutinesVersion")

common/src/main/java/com/lambda/mixin/network/ClientPlayNetworkHandlerMixin.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,40 @@
1919

2020
import com.lambda.event.EventFlow;
2121
import com.lambda.event.events.InventoryEvent;
22+
import com.lambda.module.modules.render.NoRender;
23+
import com.lambda.event.events.WorldEvent;
2224
import net.minecraft.client.network.ClientPlayNetworkHandler;
25+
import net.minecraft.client.network.PlayerListEntry;
26+
import net.minecraft.network.packet.s2c.play.GameJoinS2CPacket;
27+
import net.minecraft.network.packet.s2c.play.PlayerListS2CPacket;
2328
import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket;
2429
import net.minecraft.network.packet.s2c.play.UpdateSelectedSlotS2CPacket;
2530
import org.spongepowered.asm.mixin.Mixin;
2631
import org.spongepowered.asm.mixin.injection.At;
2732
import org.spongepowered.asm.mixin.injection.Inject;
33+
import org.spongepowered.asm.mixin.injection.Redirect;
2834
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
2935

3036
@Mixin(ClientPlayNetworkHandler.class)
3137
public class ClientPlayNetworkHandlerMixin {
38+
@Inject(method = "onGameJoin(Lnet/minecraft/network/packet/s2c/play/GameJoinS2CPacket;)V", at = @At("TAIL"))
39+
void injectJoinPacket(GameJoinS2CPacket packet, CallbackInfo ci) {
40+
EventFlow.post(new WorldEvent.Join());
41+
}
42+
43+
@Inject(method = "handlePlayerListAction(Lnet/minecraft/network/packet/s2c/play/PlayerListS2CPacket$Action;Lnet/minecraft/network/packet/s2c/play/PlayerListS2CPacket$Entry;Lnet/minecraft/client/network/PlayerListEntry;)V", at = @At("TAIL"))
44+
void injectPlayerList(PlayerListS2CPacket.Action action, PlayerListS2CPacket.Entry receivedEntry, PlayerListEntry currentEntry, CallbackInfo ci) {
45+
if (action != PlayerListS2CPacket.Action.ADD_PLAYER) return;
46+
47+
var name = currentEntry.getProfile().getName();
48+
var uuid = currentEntry.getProfile().getId();
49+
50+
if (receivedEntry.listed())
51+
EventFlow.post(new WorldEvent.Player.Join(name, uuid, currentEntry));
52+
else
53+
EventFlow.post(new WorldEvent.Player.Leave(name, uuid, currentEntry));
54+
}
55+
3256
@Inject(method = "onUpdateSelectedSlot", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/NetworkThreadUtils;forceMainThread(Lnet/minecraft/network/packet/Packet;Lnet/minecraft/network/listener/PacketListener;Lnet/minecraft/util/thread/ThreadExecutor;)V", shift = At.Shift.AFTER), cancellable = true)
3357
private void onUpdateSelectedSlot(UpdateSelectedSlotS2CPacket packet, CallbackInfo ci) {
3458
if (EventFlow.post(new InventoryEvent.HotbarSlot.Sync(packet.getSlot())).isCanceled()) ci.cancel();
@@ -38,4 +62,9 @@ private void onUpdateSelectedSlot(UpdateSelectedSlotS2CPacket packet, CallbackIn
3862
private void onScreenHandlerSlotUpdate(ScreenHandlerSlotUpdateS2CPacket packet, CallbackInfo ci) {
3963
EventFlow.post(new InventoryEvent.SlotUpdate(packet.getSyncId(), packet.getRevision(), packet.getSlot(), packet.getStack()));
4064
}
65+
66+
@Redirect(method = "onServerMetadata", at = @At(value = "FIELD", target = "Lnet/minecraft/client/network/ClientPlayNetworkHandler;displayedUnsecureChatWarning:Z", ordinal = 0))
67+
public boolean onServerMetadata(ClientPlayNetworkHandler clientPlayNetworkHandler) {
68+
return NoRender.getNoChatVerificationToast();
69+
}
4170
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2025 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.mixin.render;
19+
20+
import com.lambda.module.modules.client.Capes;
21+
import com.lambda.network.CapeManager;
22+
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
23+
import net.minecraft.client.network.AbstractClientPlayerEntity;
24+
import net.minecraft.client.render.VertexConsumerProvider;
25+
import net.minecraft.client.render.entity.feature.CapeFeatureRenderer;
26+
import net.minecraft.client.util.math.MatrixStack;
27+
import net.minecraft.util.Identifier;
28+
import org.spongepowered.asm.mixin.Mixin;
29+
import org.spongepowered.asm.mixin.injection.At;
30+
31+
@Mixin(CapeFeatureRenderer.class)
32+
public class CapeFeatureRendererMixin {
33+
@ModifyExpressionValue(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/client/network/AbstractClientPlayerEntity;FFFFFF)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/SkinTextures;capeTexture()Lnet/minecraft/util/Identifier;"))
34+
Identifier renderCape(Identifier original, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, AbstractClientPlayerEntity player, float f, float g, float h, float j, float k, float l) {
35+
if (!Capes.INSTANCE.isEnabled() || !CapeManager.INSTANCE.containsKey(player.getUuid())) return original;
36+
37+
return Identifier.of("lambda", CapeManager.INSTANCE.get(player.getUuid()));
38+
}
39+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2025 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.mixin.render;
19+
20+
import com.lambda.module.modules.client.LambdaMoji;
21+
import net.minecraft.client.font.TextRenderer;
22+
import net.minecraft.client.gui.DrawContext;
23+
import net.minecraft.client.gui.hud.ChatHud;
24+
import net.minecraft.text.OrderedText;
25+
import org.spongepowered.asm.mixin.Mixin;
26+
import org.spongepowered.asm.mixin.injection.At;
27+
import org.spongepowered.asm.mixin.injection.Redirect;
28+
29+
@Mixin(ChatHud.class)
30+
public class ChatHudMixin {
31+
@Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawTextWithShadow(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/text/OrderedText;III)I"))
32+
int redirectRenderCall(DrawContext instance, TextRenderer textRenderer, OrderedText text, int x, int y, int color) {
33+
return instance.drawTextWithShadow(textRenderer, LambdaMoji.INSTANCE.parse(text, x, y, color), 0, y, 16777215 + (color << 24));
34+
}
35+
}

common/src/main/java/com/lambda/mixin/render/ChatInputSuggestorMixin.java

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,47 @@
1717

1818
package com.lambda.mixin.render;
1919

20+
import com.google.common.base.Strings;
2021
import com.lambda.command.CommandManager;
22+
import com.lambda.graphics.renderer.gui.font.core.LambdaAtlas;
23+
import com.lambda.module.modules.client.LambdaMoji;
24+
import com.lambda.module.modules.client.RenderSettings;
2125
import com.mojang.brigadier.CommandDispatcher;
26+
import com.mojang.brigadier.suggestion.Suggestions;
27+
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
2228
import net.minecraft.client.gui.screen.ChatInputSuggestor;
2329
import net.minecraft.client.gui.widget.TextFieldWidget;
2430
import net.minecraft.client.network.ClientPlayNetworkHandler;
2531
import net.minecraft.command.CommandSource;
32+
import org.jetbrains.annotations.Nullable;
2633
import org.spongepowered.asm.mixin.Final;
2734
import org.spongepowered.asm.mixin.Mixin;
2835
import org.spongepowered.asm.mixin.Shadow;
36+
import org.spongepowered.asm.mixin.Unique;
2937
import org.spongepowered.asm.mixin.injection.At;
38+
import org.spongepowered.asm.mixin.injection.Inject;
3039
import org.spongepowered.asm.mixin.injection.ModifyVariable;
3140
import org.spongepowered.asm.mixin.injection.Redirect;
41+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
42+
43+
import java.util.concurrent.CompletableFuture;
44+
import java.util.regex.Matcher;
45+
import java.util.regex.Pattern;
46+
import java.util.stream.Stream;
3247

3348
@Mixin(ChatInputSuggestor.class)
34-
public class ChatInputSuggestorMixin {
49+
public abstract class ChatInputSuggestorMixin {
3550

3651
@Shadow
3752
@Final
3853
TextFieldWidget textField;
3954

55+
@Shadow
56+
private @Nullable CompletableFuture<Suggestions> pendingSuggestions;
57+
58+
@Shadow
59+
public abstract void show(boolean narrateFirstSuggestion);
60+
4061
@ModifyVariable(method = "refresh", at = @At(value = "STORE"), index = 3)
4162
private boolean refreshModify(boolean showCompletions) {
4263
return CommandManager.INSTANCE.isCommand(textField.getText());
@@ -46,4 +67,55 @@ private boolean refreshModify(boolean showCompletions) {
4667
private CommandDispatcher<CommandSource> refreshRedirect(ClientPlayNetworkHandler instance) {
4768
return CommandManager.INSTANCE.currentDispatcher(textField.getText());
4869
}
70+
71+
@Inject(method = "refresh", at = @At("TAIL"))
72+
private void refreshEmojiSuggestion(CallbackInfo ci) {
73+
if (!LambdaMoji.INSTANCE.isEnabled() ||
74+
!LambdaMoji.INSTANCE.getSuggestions()) return;
75+
76+
String typing = textField.getText();
77+
78+
// Don't suggest emojis in commands
79+
if (CommandManager.INSTANCE.isCommand(typing) ||
80+
CommandManager.INSTANCE.isLambdaCommand(typing)) return;
81+
82+
int cursor = textField.getCursor();
83+
String textToCursor = typing.substring(0, cursor);
84+
if (textToCursor.isEmpty()) return;
85+
86+
// Most right index at the left of the regex expression
87+
int start = neoLambda$getLastColon(textToCursor);
88+
if (start == -1) return;
89+
90+
String emojiString = typing.substring(start + 1);
91+
92+
Stream<String> results = LambdaAtlas.INSTANCE.getKeys(RenderSettings.INSTANCE.getEmojiFont())
93+
.keySet().stream()
94+
.filter(s -> s.startsWith(emojiString))
95+
.map(s -> s + ":");
96+
97+
pendingSuggestions = CommandSource.suggestMatching(results, new SuggestionsBuilder(textToCursor, start + 1));
98+
pendingSuggestions.thenRun(() -> {
99+
if (!pendingSuggestions.isDone()) return;
100+
101+
show(false);
102+
});
103+
}
104+
105+
@Unique
106+
private static final Pattern COLON_PATTERN = Pattern.compile("(:[a-zA-Z0-9_]+)");
107+
108+
@Unique
109+
private int neoLambda$getLastColon(String input) {
110+
if (Strings.isNullOrEmpty(input)) return -1;
111+
112+
int i = -1;
113+
Matcher matcher = COLON_PATTERN.matcher(input);
114+
115+
while (matcher.find()) {
116+
i = matcher.start();
117+
}
118+
119+
return i;
120+
}
49121
}

common/src/main/java/com/lambda/mixin/render/ChatScreenMixin.java

Lines changed: 2 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -17,71 +17,20 @@
1717

1818
package com.lambda.mixin.render;
1919

20-
import com.lambda.Lambda;
2120
import com.lambda.command.CommandManager;
22-
import com.lambda.graphics.renderer.gui.font.FontRenderer;
23-
import com.lambda.graphics.renderer.gui.font.LambdaEmoji;
24-
import com.lambda.graphics.renderer.gui.font.glyph.GlyphInfo;
25-
import com.lambda.module.modules.client.LambdaMoji;
26-
import com.lambda.util.math.Vec2d;
27-
import kotlin.Pair;
28-
import kotlin.ranges.IntRange;
2921
import net.minecraft.client.gui.screen.ChatScreen;
3022
import org.spongepowered.asm.mixin.Mixin;
3123
import org.spongepowered.asm.mixin.injection.At;
3224
import org.spongepowered.asm.mixin.injection.Inject;
33-
import org.spongepowered.asm.mixin.injection.ModifyArg;
34-
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
3525
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
3626

37-
import java.util.ArrayList;
38-
import java.util.Collections;
39-
import java.util.List;
40-
4127
@Mixin(ChatScreen.class)
4228
public abstract class ChatScreenMixin {
43-
@ModifyArg(method = "sendMessage", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayNetworkHandler;sendChatMessage(Ljava/lang/String;)V"), index = 0)
44-
private String modifyChatText(String chatText) {
45-
if (LambdaMoji.INSTANCE.isDisabled()) return chatText;
46-
47-
List<Pair<GlyphInfo, IntRange>> emojis = FontRenderer.Companion.parseEmojis(chatText, LambdaEmoji.Twemoji);
48-
Collections.reverse(emojis);
49-
50-
List<String> pushEmojis = new ArrayList<>();
51-
List<Vec2d> pushPositions = new ArrayList<>();
52-
53-
for (Pair<GlyphInfo, IntRange> emoji : emojis) {
54-
String emojiString = chatText.substring(emoji.getSecond().getStart() + 1, emoji.getSecond().getEndInclusive());
55-
if (LambdaEmoji.Twemoji.get(emojiString) == null)
56-
continue;
57-
58-
// Because the width of a char is bigger than an emoji
59-
// we can simply replace the matches string by a space
60-
// and render it after the text
61-
chatText = chatText.substring(0, emoji.getSecond().getStart()) + " " + chatText.substring(emoji.getSecond().getEndInclusive() + 1);
62-
63-
// We cannot retain the position in the future, but we can
64-
// assume that every time you send a message the height of
65-
// the position will change by the height of the glyph
66-
// The positions are from the top left corner of the screen
67-
int x = Lambda.getMc().textRenderer.getWidth(chatText.substring(0, emoji.getSecond().getStart()));
68-
int y = Lambda.getMc().textRenderer.fontHeight;
69-
70-
pushEmojis.add(String.format(":%s:", emojiString));
71-
pushPositions.add(new Vec2d(x, y));
72-
}
73-
74-
// Not optimal because it has to parse the emoji again but who cares
75-
LambdaMoji.INSTANCE.add(pushEmojis, pushPositions);
76-
77-
return chatText;
78-
}
79-
8029
@Inject(method = "sendMessage", at = @At("HEAD"), cancellable = true)
81-
void sendMessageInject(String chatText, boolean addToHistory, CallbackInfo ci) {
30+
void sendMessageInject(String chatText, boolean addToHistory, CallbackInfoReturnable<Boolean> cir) {
8231
if (!CommandManager.INSTANCE.isLambdaCommand(chatText)) return;
8332
CommandManager.INSTANCE.executeCommand(chatText);
8433

85-
ci.cancel();
34+
cir.setReturnValue(true);
8635
}
8736
}

0 commit comments

Comments
 (0)