Skip to content

Commit

Permalink
Half functioning "assert sound"
Browse files Browse the repository at this point in the history
Some sounds are determined by the client, and these are not logged
  • Loading branch information
misode committed Dec 27, 2023
1 parent a3fe4c2 commit 5f5b618
Show file tree
Hide file tree
Showing 13 changed files with 218 additions and 6 deletions.
4 changes: 2 additions & 2 deletions src/main/java/io/github/misode/packtest/ChatListener.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.github.misode.packtest;

import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player;
import org.apache.commons.compress.utils.Lists;

import java.util.List;
Expand All @@ -11,7 +11,7 @@ public class ChatListener {

private static final List<ChatListener> listeners = Lists.newArrayList();

public static void broadcast(ServerPlayer player, Component chatMessage) {
public static void broadcast(Player player, Component chatMessage) {
Message message = new Message(player.getName().getString(), chatMessage.getString());
ChatListener.listeners.forEach(l -> l.messages.add(message));
}
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/io/github/misode/packtest/PackTestFunction.java
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,10 @@ private Consumer<GameTestHelper> createTestBody(int permissionLevel) {
((PackTestInfo)((PackTestHelper)helper).packtest$getInfo()).packtest$setChatListener(chatListener);
helper.onEachTick(chatListener::reset);

SoundListener soundListener = new SoundListener();
((PackTestInfo)((PackTestHelper)helper).packtest$getInfo()).packtest$setSoundListener(soundListener);
helper.onEachTick(soundListener::reset);

GameTestSequence sequence = helper.startSequence();
for (Step step : this.steps) {
step.register(sequence, source);
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/io/github/misode/packtest/PackTestInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@
public interface PackTestInfo {
void packtest$setChatListener(ChatListener listener);
ChatListener packtest$getChatListener();
void packtest$setSoundListener(SoundListener listener);
SoundListener packtest$getSoundListener();
}
46 changes: 46 additions & 0 deletions src/main/java/io/github/misode/packtest/SoundListener.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package io.github.misode.packtest;

import net.minecraft.core.Holder;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import org.apache.commons.compress.utils.Lists;
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.function.Predicate;

public class SoundListener {
private static final List<SoundListener> listeners = Lists.newArrayList();

public static void broadcast(Player player, Vec3 pos, Holder<SoundEvent> holder) {
String playerName = player == null ? null : player.getName().getString();
ResourceLocation sound = holder.value().getLocation();
SoundListener.Event message = new SoundListener.Event(playerName, pos, sound);
SoundListener.listeners.forEach(l -> l.events.add(message));
}

public SoundListener() {
SoundListener.listeners.add(this);
}

public final List<SoundListener.Event> events = Lists.newArrayList();

public void stop() {
SoundListener.listeners.remove(this);
}

public List<String> filter(Predicate<SoundListener.Event> predicate) {
return this.events.stream()
.filter(predicate)
.map(m -> m.sound.toString())
.toList();
}

public void reset() {
this.events.clear();
}

public record Event(@Nullable String player, Vec3 pos, ResourceLocation sound) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.mojang.brigadier.Command;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
Expand All @@ -22,6 +23,7 @@
import net.minecraft.commands.execution.CustomCommandExecutor;
import net.minecraft.commands.execution.ExecutionControl;
import net.minecraft.commands.execution.Frame;
import net.minecraft.commands.synchronization.SuggestionProviders;
import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.gametest.framework.GameTestHelper;
Expand Down Expand Up @@ -106,7 +108,14 @@ public static void addConditions(LiteralArgumentBuilder<CommandSourceStack> buil
.then(argument("pattern", StringArgumentType.string())
.executes(expect.apply(AssertCommand::assertChatUnfiltered))
.then(argument("receivers", EntityArgument.players())
.executes(expect.apply(AssertCommand::assertChatFiltered)))));
.executes(expect.apply(AssertCommand::assertChatFiltered)))))
.then(literal("sound")
.then(argument("sound", ResourceLocationArgument.id())
.suggests(SuggestionProviders.AVAILABLE_SOUNDS)
.then(argument("distance", IntegerArgumentType.integer(0))
.executes(expect.apply(AssertCommand::assertSoundUnfiltered))
.then(argument("receivers", EntityArgument.players())
.executes(expect.apply(AssertCommand::assertSoundFiltered))))));

for(DataCommands.DataProvider dataProvider : DataCommands.SOURCE_PROVIDERS) {
builder.then(dataProvider.wrap(literal("data"),
Expand Down Expand Up @@ -242,6 +251,31 @@ private static AssertResult assertChat(CommandContext<CommandSourceStack> ctx, P
return result(!matching.isEmpty(), pattern + " in chat", got);
}

private static AssertResult assertSoundUnfiltered(CommandContext<CommandSourceStack> ctx) throws CommandSyntaxException {
return assertSound(ctx, m -> true);
}

private static AssertResult assertSoundFiltered(CommandContext<CommandSourceStack> ctx) throws CommandSyntaxException {
Collection<ServerPlayer> receivers = EntityArgument.getPlayers(ctx, "receivers");
List<String> receiverNames = receivers.stream().map(p -> p.getName().getString()).toList();
return assertSound(ctx, m -> receiverNames.contains(m.player()));
}

private static AssertResult assertSound(CommandContext<CommandSourceStack> ctx, Predicate<SoundListener.Event> filter) throws CommandSyntaxException {
ResourceLocation sound = ResourceLocationArgument.getId(ctx, "sound");
GameTestHelper helper = ((PackTestSourceStack)ctx.getSource()).packtest$getHelper();
if (helper == null) {
throw ERROR_NO_HELPER.create();
}
SoundListener soundListener = ((PackTestInfo)((PackTestHelper)helper).packtest$getInfo()).packtest$getSoundListener();
List<String> matching = soundListener.filter(e -> filter.test(e) && e.sound().equals(sound));
List<String> all = matching.isEmpty() ? soundListener.filter(filter) : matching;
String got = all.isEmpty() ? "no sounds"
: all.size() == 1 ? all.get(0)
: all.get(all.size() - 1) + " and " + (all.size() - 1) + " more";
return result(!matching.isEmpty(), sound + " to play", got);
}

static class AssertCustomExecutor implements CustomCommandExecutor.CommandAdapter<CommandSourceStack> {
private final boolean expectOk;
private final AssertPredicate predicate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;

/**
* Adds a {@link GameTestHelper} field and accessors
*/
@Mixin(CommandSourceStack.class)
public class CommandSourceStackMixin implements PackTestSourceStack {
@Unique
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.github.misode.packtest.ChatListener;
import io.github.misode.packtest.PackTestInfo;
import io.github.misode.packtest.SoundListener;
import net.minecraft.gametest.framework.GameTestInfo;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
Expand All @@ -10,14 +11,17 @@
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

/**
* Adds chat listener field and accessors. Removes the listener when finishing.
* Adds chat and sound listener field and accessors.
* Removes the listeners when finished.
* Prevents crash when test has already started.
*/
@Mixin(GameTestInfo.class)
public class GameTestInfoMixin implements PackTestInfo {

@Unique
private ChatListener chatListener;
@Unique
private SoundListener soundListener;

@Override
public void packtest$setChatListener(ChatListener chatListener) {
Expand All @@ -29,6 +33,16 @@ public class GameTestInfoMixin implements PackTestInfo {
return this.chatListener;
}

@Override
public void packtest$setSoundListener(SoundListener soundListener) {
this.soundListener = soundListener;
}

@Override
public SoundListener packtest$getSoundListener() {
return this.soundListener;
}

@Inject(method = "startTest", cancellable = true, at = @At(value = "INVOKE", target = "Ljava/lang/IllegalStateException;<init>(Ljava/lang/String;)V"))
private void startTest(CallbackInfo ci) {
ci.cancel();
Expand All @@ -37,5 +51,6 @@ private void startTest(CallbackInfo ci) {
@Inject(method = "finish", at = @At("HEAD"))
private void finish(CallbackInfo ci) {
this.chatListener.stop();
this.soundListener.stop();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package io.github.misode.packtest.mixin;

import com.llamalad7.mixinextras.sugar.Local;
import io.github.misode.packtest.SoundListener;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.core.Holder;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.commands.PlaySoundCommand;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

import java.util.Collection;

/**
* Logs all sounds that are played
*/
@Mixin(PlaySoundCommand.class)
public class PlaySoundCommandMixin {

@Inject(method = "playSound", at = @At(value = "NEW", target = "(Lnet/minecraft/core/Holder;Lnet/minecraft/sounds/SoundSource;DDDFFJ)Lnet/minecraft/network/protocol/game/ClientboundSoundPacket;"))
private static void playSound(CommandSourceStack commandSourceStack, Collection<ServerPlayer> players, ResourceLocation resourceLocation, SoundSource soundSource, Vec3 vec3, float f, float g, float h, CallbackInfoReturnable<Integer> cir, @Local(ordinal = 0) ServerPlayer player, @Local(ordinal = 1) Vec3 pos, @Local(ordinal = 0) Holder<SoundEvent> sound) {
SoundListener.broadcast(player, pos, sound);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.llamalad7.mixinextras.sugar.Local;
import com.mojang.authlib.GameProfile;
import io.github.misode.packtest.SoundListener;
import io.github.misode.packtest.dummy.Dummy;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ClientInformation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.players.PlayerList;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
Expand All @@ -20,6 +23,7 @@
/**
* Fixes starting position of dummies when they load in.
* Respawns dummies and in the correct position.
* Log respawn anchor depletion sound.
*/
@Mixin(PlayerList.class)
public class PlayerListMixin {
Expand Down Expand Up @@ -48,4 +52,9 @@ private void teleportDummy(ServerPlayer serverPlayer, boolean bl, CallbackInfoRe
dummy.teleportTo(dummy.serverLevel(), pos.x, pos.y, pos.z, 0, 0);
}
}

@Inject(method = "respawn", at = @At(value = "NEW", target = "(Lnet/minecraft/core/Holder;Lnet/minecraft/sounds/SoundSource;DDDFFJ)Lnet/minecraft/network/protocol/game/ClientboundSoundPacket;"))
private void logSound(ServerPlayer oldPlayer, boolean bl, CallbackInfoReturnable<ServerPlayer> cir, @Local(ordinal = 1) ServerPlayer player, @Local(ordinal = 0) BlockPos pos) {
SoundListener.broadcast(player, new Vec3(pos.getX(), pos.getY(), pos.getZ()), SoundEvents.RESPAWN_ANCHOR_DEPLETE);
}
}
24 changes: 24 additions & 0 deletions src/main/java/io/github/misode/packtest/mixin/RaidMixin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.github.misode.packtest.mixin;

import com.llamalad7.mixinextras.sugar.Local;
import io.github.misode.packtest.SoundListener;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.world.entity.raid.Raid;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

/**
* Log raid horn sound
*/
@Mixin(Raid.class)
public class RaidMixin {
@Inject(method = "playSound", at = @At(value = "NEW", target = "(Lnet/minecraft/core/Holder;Lnet/minecraft/sounds/SoundSource;DDDFFJ)Lnet/minecraft/network/protocol/game/ClientboundSoundPacket;"))
private void playSound(BlockPos blockPos, CallbackInfo ci, @Local(ordinal = 0) ServerPlayer player, @Local(ordinal = 1) double x, @Local(ordinal = 2) double z) {
SoundListener.broadcast(player, new Vec3(x, player.getY(), z), SoundEvents.RAID_HORN);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.github.misode.packtest.mixin;

import io.github.misode.packtest.SoundListener;
import net.minecraft.core.Holder;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

/**
* Logs all sounds that are played
*/
@Mixin(ServerLevel.class)
public class ServerLevelMixin {

@Inject(method = "playSeededSound(Lnet/minecraft/world/entity/player/Player;DDDLnet/minecraft/core/Holder;Lnet/minecraft/sounds/SoundSource;FFJ)V", at = @At("HEAD"))
private void playSeededSound(Player player, double x, double y, double z, Holder<SoundEvent> holder, SoundSource soundSource, float volume, float pitch, long seed, CallbackInfo ci) {
SoundListener.broadcast(player, new Vec3((float)x, (float)y, (float)z), holder);
}

@Inject(method = "playSeededSound(Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/entity/Entity;Lnet/minecraft/core/Holder;Lnet/minecraft/sounds/SoundSource;FFJ)V", at = @At("HEAD"))
private void playSeededSound(Player player, Entity entity, Holder<SoundEvent> holder, SoundSource soundSource, float volume, float pitch, long seed, CallbackInfo ci) {
SoundListener.broadcast(player, entity.position(), holder);
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,32 @@
package io.github.misode.packtest.mixin;

import io.github.misode.packtest.ChatListener;
import io.github.misode.packtest.SoundListener;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.entity.player.Player;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

/**
* Logs all chat messages sent to players
* Logs all chat messages sent to players.
* Logs sounds that are played.
*/
@Mixin(ServerPlayer.class)
public class ServerPlayerMixin {

@Inject(method = "sendSystemMessage(Lnet/minecraft/network/chat/Component;Z)V", at = @At("HEAD"))
private void sendSystemMessage(Component message, boolean bl, CallbackInfo ci) {
ChatListener.broadcast((ServerPlayer)(Object)this, message);
ChatListener.broadcast((Player)(Object)this, message);
}

@Inject(method = "playNotifySound", at = @At("HEAD"))
private void playNotifySound(SoundEvent soundEvent, SoundSource soundSource, float volume, float pitch, CallbackInfo ci) {
SoundListener.broadcast((Player)(Object)this, ((Player)(Object)this).position(), BuiltInRegistries.SOUND_EVENT.wrapAsHolder(soundEvent));
}
}
3 changes: 3 additions & 0 deletions src/main/resources/packtest.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@
"LogTestReporterMixin",
"MinecraftServerMixin",
"PlayerListMixin",
"PlaySoundCommandMixin",
"RaidMixin",
"ReloadableServerResourcesMixin",
"ServerLevelMixin",
"ServerPlayerMixin",
"StructureUtilsMixin",
"TestCommandMixin"
Expand Down

0 comments on commit 5f5b618

Please sign in to comment.