Skip to content

Commit

Permalink
Upgrade client game test
Browse files Browse the repository at this point in the history
  • Loading branch information
Pyrofab committed Jan 8, 2025
1 parent a0889f9 commit 173e40c
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 87 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ org.gradle.jvmargs=-Xmx2G
minecraft_version=1.21.4
yarn_mappings=1.21.4+build.2
loader_version=0.16.9
fabric_version=0.112.2+1.21.4
fabric_version=0.113.0+1.21.4

# Other Dependencies
jb_annotations_version = 23.0.0
Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[versions]
cca = "6.1.2"
fpa = "0.2-SNAPSHOT"
elmendorf = "0.13.0"
elmendorf = "0.14.0"

[libraries]
cca-base = { module = "org.ladysnake.cardinal-components-api:cardinal-components-base", version.ref = "cca" }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package org.ladysnake.impersonatest;

import com.mojang.authlib.GameProfile;
import net.fabricmc.fabric.api.client.gametest.v1.ClientGameTestContext;
import net.fabricmc.fabric.api.client.gametest.v1.FabricClientGameTest;
import net.fabricmc.fabric.api.client.gametest.v1.TestDedicatedServerContext;
import net.fabricmc.fabric.api.client.gametest.v1.TestServerConnection;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.option.Perspective;
import net.minecraft.client.util.InputUtil;
import net.minecraft.network.DisconnectionInfo;
import net.minecraft.network.NetworkSide;
import net.minecraft.network.packet.c2s.common.SyncedClientOptions;
import net.minecraft.network.packet.c2s.play.ChatMessageC2SPacket;
import net.minecraft.server.PlayerManager;
import net.minecraft.server.network.ConnectedClientData;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.text.Text;
import net.minecraft.util.math.Vec3d;
import org.jetbrains.annotations.NotNull;
import org.ladysnake.elmendorf.impl.MockClientConnection;
import org.ladysnake.impersonate.impl.ImpersonateGamerules;

import java.security.NoSuchAlgorithmException;
import java.util.UUID;

public class ImpersonateClientTest implements FabricClientGameTest {

public static final String MOCK_PLAYER_NAME = "test-mock-player";

@Override
public void runTest(ClientGameTestContext context) {
GameProfile profile = context.computeOnClient(MinecraftClient::getGameProfile);
try (TestDedicatedServerContext server = context.worldBuilder().createServer()) {
try (TestServerConnection connection = server.connect()) {
server.runOnServer(s -> s.getPlayerManager().addToOperators(profile));
connection.getClientWorld().waitForChunksRender();
context.runOnClient(client -> client.options.setPerspective(Perspective.THIRD_PERSON_FRONT));
sendChatMessage(context);
context.takeScreenshot("1_before_impersonation", 5);
context.runOnClient(client -> client.getNetworkHandler().sendChatCommand("impersonate disguise as Pyrofab"));
context.waitTicks(20);
sendChatMessage(context);
context.takeScreenshot("2_impersonating_pyrofab", 5);
context.runOnClient(client -> client.getNetworkHandler().sendChatCommand("impersonate disguise as doctor4t"));
context.waitTicks(20);
sendChatMessage(context);
context.takeScreenshot("3_impersonating_doctor4t", 5);
Vec3d newPlayerPos = context.computeOnClient(mc -> mc.player.getPos().add(2, 0, 1));
UUID otherPlayerId = UUID.randomUUID();
ServerPlayerEntity otherPlayer = server.computeOnServer(s -> spawnServerPlayer(s.getOverworld(), newPlayerPos, otherPlayerId, MOCK_PLAYER_NAME));
server.runOnServer(s ->
otherPlayer.networkHandler.onChatMessage(createChatMessagePacket(otherPlayerId, "Hi"))
);
context.waitTicks(20);
context.takeScreenshot("4_other_player_joined", 5);
context.runOnClient(client -> client.getNetworkHandler().sendChatCommand("impersonate disguise as Xiribidus " + MOCK_PLAYER_NAME));
context.waitTicks(20);
server.runOnServer(s ->
otherPlayer.networkHandler.onChatMessage(createChatMessagePacket(otherPlayerId, "Hi again"))
);
context.takeScreenshot("5_other_player_impersonating_xiribidus", 5);
server.runOnServer(s -> s.getGameRules().get(ImpersonateGamerules.OP_REVEAL_IMPERSONATIONS).set(false, s));
server.runOnServer(s ->
otherPlayer.networkHandler.onChatMessage(createChatMessagePacket(otherPlayerId, "Goodbye"))
);
context.takeScreenshot("6_other_player_impersonating_xiribidus_no_reveal", 5);
server.runOnServer(s -> otherPlayer.networkHandler.onDisconnected(new DisconnectionInfo(Text.empty())));
context.takeScreenshot("7_other_player_impersonating_xiribidus_no_reveal_disconnected", 5);
context.waitTicks(100);
}
}
}

private static @NotNull ChatMessageC2SPacket createChatMessagePacket(UUID otherPlayerId, String text) {
try {
return ImpersonateTestSuite.createChatMessagePacket(otherPlayerId, text);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}

public ServerPlayerEntity spawnServerPlayer(ServerWorld world, Vec3d pos, UUID uuid, String name) {
GameProfile profile = new GameProfile(uuid, name);
var connection = new MockClientConnection(NetworkSide.SERVERBOUND);
SyncedClientOptions clientOptions = SyncedClientOptions.createDefault();
PlayerManager playerManager = world.getServer().getPlayerManager();
ServerPlayerEntity mockPlayer = playerManager.createPlayer(profile, clientOptions);
playerManager.onPlayerConnect(connection, mockPlayer, ConnectedClientData.createDefault(profile, false));
mockPlayer.setPosition(pos);
return mockPlayer;
}


private static void sendChatMessage(ClientGameTestContext context) {
context.getInput().pressKey(options -> options.chatKey);
context.waitTick();
context.getInput().typeChars("Hello, World!");
context.getInput().pressKey(InputUtil.GLFW_KEY_ENTER);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import net.minecraft.text.Text;
import net.minecraft.text.TextContent;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.NotNull;
import org.ladysnake.elmendorf.GameTestUtil;
import org.ladysnake.elmendorf.impl.MockClientConnection;
import org.ladysnake.impersonate.Impersonate;
Expand Down Expand Up @@ -81,12 +82,7 @@ public void nameGetsRevealed(TestContext ctx) {
public void nameInChatGetsRevealed(TestContext ctx) throws NoSuchAlgorithmException {
// Do the bare minimum to simulate a legit client with a valid keypair
UUID senderUuid = UUID.randomUUID();
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
Signer signer = Signer.create(keyPair.getPrivate(), "SHA256withRSA");
MessageChain.Packer messagePacker = new MessageChain(senderUuid, UUID.randomUUID()).getPacker(signer);
LastSeenMessageList lastSeenMessages = LastSeenMessageList.EMPTY;
ChatMessageC2SPacket chatMessagePacket = createChatMessagePacket(senderUuid, "Hi");
ServerPlayerEntity player = new ServerPlayerEntity(
ctx.getWorld().getServer(),
ctx.getWorld(),
Expand All @@ -100,23 +96,14 @@ public void nameInChatGetsRevealed(TestContext ctx) throws NoSuchAlgorithmExcept
ConnectedClientData.createDefault(player.getGameProfile(), false)
);
Impersonator.get(player).impersonate(IMPERSONATION_KEY, new GameProfile(UUID.randomUUID(), "impersonated"));
String text = "Hi";
Instant timestamp = Instant.now();
long salt = NetworkEncryptionUtils.SecureRandomUtil.nextLong();
PlayerManager playerManager = ctx.getWorld().getServer().getPlayerManager();
ServerPlayerEntity otherPlayer = ctx.spawnServerPlayer(1, 0, 1);

try {
playerManager.getPlayerList().add(player);
playerManager.getPlayerList().add(otherPlayer);
playerManager.addToOperators(player.getGameProfile());
player.networkHandler.onChatMessage(new ChatMessageC2SPacket(
text,
timestamp,
salt,
messagePacker.pack(new MessageBody(text, timestamp, salt, lastSeenMessages)),
new LastSeenMessageList.Acknowledgment(0, new BitSet())
));
player.networkHandler.onChatMessage(chatMessagePacket);
ctx.verifyConnection(player, conn -> conn.sent(
ChatMessageS2CPacket.class,
chatPacket -> chatPacket.serializedParameters().name().getString()
Expand All @@ -134,4 +121,23 @@ public void nameInChatGetsRevealed(TestContext ctx) throws NoSuchAlgorithmExcept
playerManager.getPlayerList().remove(otherPlayer);
}
}

public static @NotNull ChatMessageC2SPacket createChatMessagePacket(UUID senderUuid, String text) throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
Signer signer = Signer.create(keyPair.getPrivate(), "SHA256withRSA");
MessageChain.Packer messagePacker = new MessageChain(senderUuid, UUID.randomUUID()).getPacker(signer);
LastSeenMessageList lastSeenMessages = LastSeenMessageList.EMPTY;
Instant timestamp = Instant.now();
long salt = NetworkEncryptionUtils.SecureRandomUtil.nextLong();
ChatMessageC2SPacket chatMessagePacket = new ChatMessageC2SPacket(
text,
timestamp,
salt,
messagePacker.pack(new MessageBody(text, timestamp, salt, lastSeenMessages)),
new LastSeenMessageList.Acknowledgment(0, new BitSet())
);
return chatMessagePacket;
}
}
2 changes: 1 addition & 1 deletion src/testmod/resources/fabric.mod.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"org.ladysnake.impersonatest.ImpersonateTestSuite"
],
"fabric-client-gametest": [
"org.ladysnake.impersonatest.ImpersonateClientTestSuite"
"org.ladysnake.impersonatest.ImpersonateClientTest"
]
},
"depends": {
Expand Down

0 comments on commit 173e40c

Please sign in to comment.