Volver al índice
src/main/java/ar/com/companeros/horror/HorrorNetwork.java
package ar.com.companeros.horror;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.Arrays;
import java.util.Objects;
import java.util.UUID;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.network.*;
import net.minecraftforge.network.simple.SimpleChannel;
/** Preferencias y voz enviada sólo a los observadores autorizados. */
public final class HorrorNetwork {
public static Consumer<Boolean> CLIENT_RECEIVER = enabled -> { };
public static Consumer<Speech> CLIENT_SPEECH = speech -> { };
public static final int MAX_SPEECH_BYTES = 192 * 1024;
private static final String VERSION = "3";
private static final SimpleChannel CHANNEL = NetworkRegistry.newSimpleChannel(
new ResourceLocation("companeros", "horror"), () -> VERSION, VERSION::equals, VERSION::equals);
private static boolean registered;
private HorrorNetwork() { }
public static synchronized void register() {
if (registered) return; registered = true;
CHANNEL.messageBuilder(Sensitivity.class, 0, NetworkDirection.PLAY_TO_CLIENT)
.encoder(Sensitivity::encode).decoder(Sensitivity::decode).consumerMainThread(Sensitivity::handle).add();
CHANNEL.messageBuilder(Speech.class, 1, NetworkDirection.PLAY_TO_CLIENT)
.encoder(Speech::encode).decoder(Speech::decode).consumerMainThread(Speech::handle).add();
}
public static void send(ServerPlayer player, boolean enabled) {
CHANNEL.send(PacketDistributor.PLAYER.with(() -> player), new Sensitivity(enabled));
}
// VOZ: no transmite rutas, servicios ni URLs; únicamente un OGG pequeño en memoria.
public static void sendSpeech(ServerPlayer player, ParasiteEntity body, byte[] audio) {
var server = player.getServer();
if (server == null || !HorrorModule.mayTarget(server, player)
|| player.level() != body.level() || !body.isAlive() || player.distanceToSqr(body) > 48 * 48) return;
CHANNEL.send(PacketDistributor.PLAYER.with(() -> player),
new Speech(body.getUUID(), body.getX(), body.getEyeY(), body.getZ(), audio));
}
public static boolean validSpeechBytes(byte[] audio) {
if (audio == null || audio.length < 36 || audio.length > MAX_SPEECH_BYTES
|| audio[0] != 'O' || audio[1] != 'g' || audio[2] != 'g' || audio[3] != 'S' || audio[4] != 0) return false;
int firstPacket = 27 + Byte.toUnsignedInt(audio[26]);
if (firstPacket + 30 > audio.length || audio[firstPacket] != 1) return false;
byte[] vorbis = { 'v', 'o', 'r', 'b', 'i', 's' };
for (int index = 0; index < vorbis.length; index++) if (audio[firstPacket + 1 + index] != vorbis[index]) return false;
int channels = Byte.toUnsignedInt(audio[firstPacket + 11]);
int rate = Byte.toUnsignedInt(audio[firstPacket + 12]) | Byte.toUnsignedInt(audio[firstPacket + 13]) << 8
| Byte.toUnsignedInt(audio[firstPacket + 14]) << 16 | Byte.toUnsignedInt(audio[firstPacket + 15]) << 24;
return (channels == 1 || channels == 2) && rate >= 8000 && rate <= 48000;
}
public record Speech(UUID body, double x, double y, double z, byte[] audio) {
public Speech {
Objects.requireNonNull(body);
if (!Double.isFinite(x) || !Double.isFinite(y) || !Double.isFinite(z)
|| Math.abs(x) > 30_000_000 || Math.abs(z) > 30_000_000 || Math.abs(y) > 4096
|| !validSpeechBytes(audio)) throw new IllegalArgumentException("Invalid parasite speech packet");
audio = Arrays.copyOf(audio, audio.length);
}
@Override public byte[] audio() { return Arrays.copyOf(audio, audio.length); }
private void encode(FriendlyByteBuf buffer) {
buffer.writeUUID(body);
buffer.writeDouble(x); buffer.writeDouble(y); buffer.writeDouble(z);
buffer.writeByteArray(audio);
}
private static Speech decode(FriendlyByteBuf buffer) {
return new Speech(buffer.readUUID(), buffer.readDouble(), buffer.readDouble(), buffer.readDouble(),
buffer.readByteArray(MAX_SPEECH_BYTES));
}
private void handle(Supplier<NetworkEvent.Context> context) {
CLIENT_SPEECH.accept(this);
context.get().setPacketHandled(true);
}
}
public record Sensitivity(boolean enabled) {
private void encode(FriendlyByteBuf buffer) { buffer.writeBoolean(enabled); }
private static Sensitivity decode(FriendlyByteBuf buffer) { return new Sensitivity(buffer.readBoolean()); }
private void handle(Supplier<NetworkEvent.Context> context) {
CLIENT_RECEIVER.accept(enabled); context.get().setPacketHandled(true);
}
}
}