Skip to content

Commit 0837af3

Browse files
authored
[1.20.4] Ref: Network, authentication and discord (#106)
This pull request introduces a Network module which handles basic authentication against the lambda api Additionally, players can link their discord account using the rpc to access party features These features include: - Joining friends lobbies (without joining servers due to security concerns) - Qol features such as nametags on top of friends and those who are in the party The commit 3a3feba was accidentally pushed on the wrong branch due to github desktop bugs, it does not show up on my machine but does on remote
1 parent d3724ee commit 0837af3

Some content is hidden

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

41 files changed

+865
-608
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/RYnFIRc0k6E")
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_17)
153+
jvmTarget = JvmTarget.JVM_17
148154
}
149155
}
150156
}

common/build.gradle.kts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ val fabricLoaderVersion: String by project
2222
val kotlinxCoroutinesVersion: String by project
2323
val discordIPCVersion: String by project
2424
val fuelVersion: String by project
25+
val resultVersion: String by project
2526

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

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

49-
// Fuel HTTP library
50+
// Fuel HTTP library and dependencies
5051
implementation("com.github.kittinunf.fuel:fuel:$fuelVersion")
5152
implementation("com.github.kittinunf.fuel:fuel-gson:$fuelVersion")
53+
implementation("com.github.kittinunf.result:result-jvm:$resultVersion")
5254

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

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@
2020
import com.lambda.event.EventFlow;
2121
import com.lambda.event.events.InventoryEvent;
2222
import com.lambda.module.modules.render.NoRender;
23+
import com.lambda.event.events.WorldEvent;
2324
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;
2428
import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket;
2529
import net.minecraft.network.packet.s2c.play.UpdateSelectedSlotS2CPacket;
2630
import org.spongepowered.asm.mixin.Mixin;
@@ -31,6 +35,24 @@
3135

3236
@Mixin(ClientPlayNetworkHandler.class)
3337
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+
3456
@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)
3557
private void onUpdateSelectedSlot(UpdateSelectedSlotS2CPacket packet, CallbackInfo ci) {
3658
if (EventFlow.post(new InventoryEvent.HotbarSlot.Sync(packet.getSlot())).isCanceled()) ci.cancel();
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: 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.render.VertexConsumerProvider;
24+
import net.minecraft.client.render.entity.feature.ElytraFeatureRenderer;
25+
import net.minecraft.client.util.math.MatrixStack;
26+
import net.minecraft.entity.LivingEntity;
27+
import net.minecraft.util.Identifier;
28+
import org.spongepowered.asm.mixin.Mixin;
29+
import org.spongepowered.asm.mixin.injection.At;
30+
31+
@Mixin(ElytraFeatureRenderer.class)
32+
public class ElytraFeatureRendererMixin<T extends LivingEntity> {
33+
@ModifyExpressionValue(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/entity/LivingEntity;FFFFFF)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/SkinTextures;elytraTexture()Lnet/minecraft/util/Identifier;"))
34+
Identifier renderElytra(Identifier original, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, T livingEntity, float f, float g, float h, float j, float k, float l) {
35+
if (!Capes.INSTANCE.isEnabled() || !CapeManager.INSTANCE.containsKey(livingEntity.getUuid())) return original;
36+
37+
return Identifier.of("lambda", CapeManager.INSTANCE.get(livingEntity.getUuid()));
38+
}
39+
}

common/src/main/java/com/lambda/mixin/world/ClientWorldMixin.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,12 @@
2323
import com.lambda.module.modules.render.WorldColors;
2424
import com.lambda.util.math.ColorKt;
2525
import net.minecraft.block.BlockState;
26+
import net.minecraft.client.network.ClientPlayNetworkHandler;
27+
import net.minecraft.client.render.WorldRenderer;
2628
import net.minecraft.client.world.ClientWorld;
2729
import net.minecraft.entity.Entity;
30+
import net.minecraft.registry.RegistryKey;
31+
import net.minecraft.registry.entry.RegistryEntry;
2832
import net.minecraft.util.math.BlockPos;
2933
import net.minecraft.util.math.Vec3d;
3034
import org.spongepowered.asm.mixin.Mixin;
@@ -33,6 +37,8 @@
3337
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
3438
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
3539

40+
import java.util.function.Supplier;
41+
3642
@Mixin(ClientWorld.class)
3743
public class ClientWorldMixin {
3844
@Inject(method = "addEntity", at = @At("HEAD"), cancellable = true)

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ object Lambda {
4747
@JvmStatic
4848
val mc: MinecraftClient by lazy { MinecraftClient.getInstance() }
4949

50+
val isDebug = System.getProperty("lambda.dev") != null
51+
5052
val gson: Gson = GsonBuilder()
5153
.setPrettyPrinting()
5254
.registerTypeAdapter(KeyCode::class.java, KeyCodeSerializer)

common/src/main/kotlin/com/lambda/command/commands/RpcCommand.kt renamed to common/src/main/kotlin/com/lambda/command/commands/CapeCommand.kt

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2024 Lambda
2+
* Copyright 2025 Lambda
33
*
44
* This program is free software: you can redistribute it and/or modify
55
* it under the terms of the GNU General Public License as published by
@@ -18,24 +18,36 @@
1818
package com.lambda.command.commands
1919

2020
import com.lambda.brigadier.argument.literal
21+
import com.lambda.brigadier.argument.string
2122
import com.lambda.brigadier.argument.value
22-
import com.lambda.brigadier.argument.word
2323
import com.lambda.brigadier.execute
2424
import com.lambda.brigadier.required
2525
import com.lambda.command.LambdaCommand
26-
import com.lambda.module.modules.client.DiscordRPC
26+
import com.lambda.network.CapeManager.updateCape
27+
import com.lambda.network.NetworkManager
28+
import com.lambda.threading.runSafe
2729
import com.lambda.util.extension.CommandBuilder
2830

29-
object RpcCommand : LambdaCommand(
30-
name = "rpc",
31-
description = "Discord Rich Presence commands.",
32-
usage = "rpc <join [id] | accept>"
33-
) {
31+
object CapeCommand : LambdaCommand(
32+
name = "cape",
33+
usage = "set <id>",
34+
description = "Sets your cape",
35+
) {
3436
override fun CommandBuilder.create() {
35-
required(literal("join")) {
36-
required(word("id")) { id ->
37+
required(literal("set")) {
38+
required(string("id")) { id ->
39+
suggests { _, builder ->
40+
NetworkManager.capes
41+
.forEach { builder.suggest(it) }
42+
43+
builder.buildFuture()
44+
}
45+
3746
execute {
38-
DiscordRPC.join(id().value())
47+
runSafe {
48+
val cape = id().value()
49+
updateCape(cape)
50+
}
3951
}
4052
}
4153
}

common/src/main/kotlin/com/lambda/http/api/rpc/v1/endpoints/DeleteParty.kt renamed to common/src/main/kotlin/com/lambda/config/configurations/UserConfig.kt

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2024 Lambda
2+
* Copyright 2025 Lambda
33
*
44
* This program is free software: you can redistribute it and/or modify
55
* it under the terms of the GNU General Public License as published by
@@ -15,15 +15,13 @@
1515
* along with this program. If not, see <http://www.gnu.org/licenses/>.
1616
*/
1717

18-
package com.lambda.http.api.rpc.v1.endpoints
18+
package com.lambda.config.configurations
1919

20-
import com.github.kittinunf.fuel.Fuel
21-
import com.github.kittinunf.fuel.gson.responseObject
22-
import com.lambda.http.api.rpc.v1.models.Party
20+
import com.lambda.config.Configuration
21+
import com.lambda.util.FolderRegister
22+
import java.io.File
2323

24-
fun deleteParty(
25-
endpoint: String,
26-
version: String,
27-
) =
28-
Fuel.delete("$endpoint/api/$version/party/delete")
29-
.responseObject<Party>().third
24+
object UserConfig : Configuration() {
25+
override val configName get() = "preferences"
26+
override val primary: File = FolderRegister.config.resolve("$configName.json").toFile()
27+
}

common/src/main/kotlin/com/lambda/event/events/WorldEvent.kt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,12 @@ import com.lambda.threading.runSafe
2424
import com.lambda.util.BlockUtils.blockState
2525
import net.minecraft.block.BlockState
2626
import net.minecraft.block.Blocks
27+
import net.minecraft.client.network.PlayerListEntry
2728
import net.minecraft.util.math.BlockPos
2829
import net.minecraft.util.shape.VoxelShape
2930
import net.minecraft.world.chunk.WorldChunk
31+
import java.util.UUID
32+
import kotlin.uuid.Uuid
3033

3134
/**
3235
* Represents various events that can occur within the world.
@@ -36,6 +39,31 @@ import net.minecraft.world.chunk.WorldChunk
3639
* occurrences in the game world.
3740
*/
3841
sealed class WorldEvent {
42+
// ToDo: Add doc and determine if there's a better place for this event
43+
// Represents the player joining the world
44+
class Join() : Event
45+
46+
// ToDo: Maybe create a network event seal with some s2c events
47+
sealed class Player {
48+
/**
49+
* Event triggered upon player joining
50+
*/
51+
data class Join(
52+
val name: String,
53+
val uuid: UUID,
54+
val entry: PlayerListEntry,
55+
) : Event
56+
57+
/**
58+
* Event triggered upon player leaving
59+
*/
60+
data class Leave(
61+
val name: String,
62+
val uuid: UUID,
63+
val entry: PlayerListEntry,
64+
) : Event
65+
}
66+
3967
/**
4068
* Represents an event specific to chunk operations within the world.
4169
*

0 commit comments

Comments
 (0)