Anzeigen: Beitritt/Verlassen als Text und Signalton, Discord-Status, TS-Thema, Sprecher in der Actionbar (abschaltbar)

This commit is contained in:
MineTec committed 2026-10-18 16:13:55 +02:00
1 parent 3c0e3ed17a
commit b335933678
19 files changed
+583 -37

No files matched your search

@@ -10,7 +10,9 @@ import java.util.UUID;
public sealed interface Message {
/** Plugin → Bridge, jede Sekunde: Herzschlag und verbundene Spieler pro Raum. */
record Hello(int version, Map<String, Integer> members) implements Message {}
record Hello(int version, Map<String, List<Member>> members) implements Message {}
record Member(UUID id, String name) {}
/** Bridge → Plugin, Antwort auf jedes Hello: die Räume und wer auf den anderen Seiten ist. */
record Rooms(List<RoomInfo> rooms) implements Message {}
@@ -22,4 +24,10 @@ public sealed interface Message {
* die Spieler-UUID, beim Bridge → Plugin eine feste UUID pro externem Sprecher.
*/
record Audio(String room, UUID source, byte[] opus) implements Message {}
/** Bridge → Plugin: Chat-Hinweis an alle in der Gruppe, z. B. wer dazugekommen ist. */
record Notice(String room, String text) implements Message {}
/** Bridge → Plugin: wer aus Discord/TeamSpeak gerade spricht (für die Actionbar). */
record Speakers(String room, List<String> names) implements Message {}
}
@@ -14,13 +14,15 @@ import java.util.UUID;
public final class Protocol {
/** Bei inkompatiblen Änderungen am Format hochzählen. */
public static final int VERSION = 1;
public static final int VERSION = 2;
public static final int DEFAULT_PORT = 24470;
public static final int MAX_DATAGRAM = 2048;
private static final byte HELLO = 1;
private static final byte ROOMS = 2;
private static final byte AUDIO = 3;
private static final byte NOTICE = 4;
private static final byte SPEAKERS = 5;
private Protocol() {}
@@ -32,9 +34,13 @@ public final class Protocol {
out.writeByte(HELLO);
out.writeInt(hello.version());
out.writeShort(hello.members().size());
for (Map.Entry<String, Integer> e : hello.members().entrySet()) {
for (Map.Entry<String, List<Message.Member>> e : hello.members().entrySet()) {
out.writeUTF(e.getKey());
out.writeShort(e.getValue());
out.writeShort(e.getValue().size());
for (Message.Member member : e.getValue()) {
writeUuid(out, member.id());
out.writeUTF(member.name());
}
}
}
case Message.Rooms rooms -> {
@@ -55,6 +61,17 @@ public final class Protocol {
writeUuid(out, audio.source());
out.write(audio.opus());
}
case Message.Notice notice -> {
out.writeByte(NOTICE);
out.writeUTF(notice.room());
out.writeUTF(notice.text());
}
case Message.Speakers speakers -> {
out.writeByte(SPEAKERS);
out.writeUTF(speakers.room());
out.writeShort(speakers.names().size());
for (String name : speakers.names()) out.writeUTF(name);
}
}
} catch (IOException e) {
throw new UncheckedIOException(e);
@@ -69,8 +86,14 @@ public final class Protocol {
case HELLO -> {
int version = in.readInt();
int count = in.readUnsignedShort();
Map<String, Integer> members = new LinkedHashMap<>();
for (int i = 0; i < count; i++) members.put(in.readUTF(), in.readUnsignedShort());
Map<String, List<Message.Member>> members = new LinkedHashMap<>();
for (int i = 0; i < count; i++) {
String room = in.readUTF();
int size = in.readUnsignedShort();
List<Message.Member> list = new ArrayList<>(size);
for (int j = 0; j < size; j++) list.add(new Message.Member(readUuid(in), in.readUTF()));
members.put(room, list);
}
yield new Message.Hello(version, members);
}
case ROOMS -> {
@@ -83,6 +106,14 @@ public final class Protocol {
yield new Message.Rooms(rooms);
}
case AUDIO -> new Message.Audio(in.readUTF(), readUuid(in), in.readAllBytes());
case NOTICE -> new Message.Notice(in.readUTF(), in.readUTF());
case SPEAKERS -> {
String room = in.readUTF();
int size = in.readUnsignedShort();
List<String> names = new ArrayList<>(size);
for (int i = 0; i < size; i++) names.add(in.readUTF());
yield new Message.Speakers(room, names);
}
default -> throw new IOException("Unbekannter Nachrichtentyp " + type);
};
}
@@ -18,7 +18,8 @@ class ProtocolTest {
@Test
void hello() throws IOException {
Message.Hello hello = new Message.Hello(Protocol.VERSION, Map.of("crosstalk-1", 3));
Message.Hello hello = new Message.Hello(Protocol.VERSION,
Map.of("crosstalk-1", List.of(new Message.Member(UUID.randomUUID(), "Steve"))));
assertEquals(hello, roundTrip(hello));
}
@@ -39,6 +40,14 @@ class ProtocolTest {
assertArrayEquals(opus, audio.opus());
}
@Test
void noticeAndSpeakers() throws IOException {
Message.Notice notice = new Message.Notice("crosstalk-1", "Anna (Discord) ist dazugekommen.");
assertEquals(notice, roundTrip(notice));
Message.Speakers speakers = new Message.Speakers("crosstalk-1", List.of("Anna (Discord)", "Bob (TeamSpeak)"));
assertEquals(speakers, roundTrip(speakers));
}
@Test
void unknownType() {
assertThrows(IOException.class, () -> Protocol.decode(new byte[]{42}, 0, 1));