Typing indicator + Metrics

This commit is contained in:
2023-12-24 02:46:27 +02:00
parent ac58009119
commit a477a87c3d
20 changed files with 1201 additions and 316 deletions
@@ -8,8 +8,23 @@ import java.util.stream.Collectors;
public class PacketHandler {
private final Map<Class<? extends Packet>, Packet> packetClassMap = new HashMap<>();
private final Map<Class<? extends Packet>, Set<PacketListener<? extends Packet>>> listeners = new ConcurrentHashMap<>();
public void clearPacketMap() {
packetClassMap.clear();
}
public void addPacketToMap(Packet packet) {
packetClassMap.put(packet.getClass(), packet);
}
public void addAllPacketsToMap(Collection<Packet> packets) {
packetClassMap.putAll(packets.stream()
.collect(Collectors.toMap(Packet::getClass, o -> o)));
}
public <T extends Packet> void addListener(Class<T> type, PacketListener<T> listener) {
if (!listeners.containsKey(type)) {
listeners.put(type, new HashSet<>());
@@ -39,35 +54,25 @@ public class PacketHandler {
}
public boolean runPacket(Packet packet) {
return switch (packet.getType()) {
case TopicCreatedPacket.TYPE -> runPacket(TopicCreatedPacket.class, (TopicCreatedPacket) packet);
case TopicUpdatePacket.TYPE -> runPacket(TopicUpdatePacket.class, (TopicUpdatePacket) packet);
case TopicListDataPacket.TYPE -> runPacket(TopicListDataPacket.class, (TopicListDataPacket) packet);
case MessageCreatedPacket.TYPE -> runPacket(MessageCreatedPacket.class, (MessageCreatedPacket) packet);
case MessageSystemPacket.TYPE -> runPacket(MessageSystemPacket.class, (MessageSystemPacket) packet);
default -> false;
};
return runPacket((Class<Packet>)packet.getClass(), packet);
}
public <T extends Packet> Set<PacketListener<T>> getListeners(Class<T> packetClass) {
if (!listeners.containsKey(packetClass))
return null;
return listeners.get(packetClass).stream().map(listener -> {
return (PacketListener<T>) listener;
}).collect(Collectors.toSet());
return listeners.get(packetClass).stream()
.map(listener -> (PacketListener<T>) listener)
.collect(Collectors.toSet());
}
public List<Short> getListeningTypes() {
return new ArrayList<>() {
{
add(TopicCreatedPacket.TYPE);
add(TopicUpdatePacket.TYPE);
add(TopicListDataPacket.TYPE);
add(MessageCreatedPacket.TYPE);
add(MessageSystemPacket.TYPE);
}
};
return listeners.entrySet().stream()
.filter(classSetEntry -> !classSetEntry.getValue().isEmpty())
.map(Map.Entry::getKey)
.map(packetClassMap::get)
.map(Packet::getType)
.collect(Collectors.toList());
}
}
@@ -4,10 +4,7 @@ import dev.wiing.gossip.lib.packets.*;
import java.io.*;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
public class PacketManager {
@@ -38,9 +35,14 @@ public class PacketManager {
add(new MessageListDataPacket());
add(new MessageFetchPacket());
add(new MessageDataPacket());
add(new TypingPingPacket());
add(new TypingListUpdatePacket());
}
};
private final PacketMetrics packetMetrics = new PacketMetrics();
private final Map<Short, Packet> packetMap = new HashMap<>();
public void addPacket(Packet packet) {
@@ -58,6 +60,14 @@ public class PacketManager {
}
}
public Map<Short, Packet> getPacketMap() {
return Collections.unmodifiableMap(packetMap);
}
public PacketMetrics getPacketMetrics() {
return packetMetrics;
}
public Packet readPacket(InputStream stream) throws IOException {
ByteBuffer buffer = ByteBuffer.wrap(stream.readNBytes(6));
@@ -68,9 +78,14 @@ public class PacketManager {
Packet packet = packetMap.getOrDefault(type, null);
if (packet == null) return null;
if (packet == null) {
packetMetrics.recordUnrecognisedPacket();
return null;
}
return packet.readBytes(ByteBuffer.wrap(stream.readNBytes(size)), size);
Packet result = packet.readBytes(ByteBuffer.wrap(stream.readNBytes(size)), size);
packetMetrics.recordReceivedPacket(result);
return result;
}
public void writePacket(BufferedOutputStream stream, Packet packet) throws IOException {
@@ -82,6 +97,8 @@ public class PacketManager {
packet.writeBytes(buffer);
stream.write(buffer.array());
packetMetrics.recordSentPacket(packet);
}
public void writePacket(OutputStream stream, Packet packet) throws IOException {
@@ -0,0 +1,74 @@
package dev.wiing.gossip.lib;
import dev.wiing.gossip.lib.packets.Packet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class PacketMetrics {
private final Map<Class<Packet>, List<Packet>> sendHistory = new ConcurrentHashMap<>();
private int totalPacketsSentCount = 0;
private long totalPacketsSentBytes = 0;
private final Map<Class<Packet>, List<Packet>> receiveHistory = new ConcurrentHashMap<>();
private int totalPacketsReceivedCount = 0;
private long totalPacketsReceivedBytes = 0;
private int totalPacketsUnrecognised = 0;
public PacketMetrics() {
}
public void recordSentPacket(Packet packet) {
if (!sendHistory.containsKey(packet.getClass())) {
sendHistory.put((Class<Packet>) packet.getClass(), Collections.synchronizedList(new ArrayList<>()));
}
sendHistory.get(packet.getClass()).add(packet);
++totalPacketsSentCount;
totalPacketsSentBytes += packet.getTotalLength();
}
public void recordReceivedPacket(Packet packet) {
if (!receiveHistory.containsKey(packet.getClass())) {
receiveHistory.put((Class<Packet>) packet.getClass(), Collections.synchronizedList(new ArrayList<>()));
}
receiveHistory.get(packet.getClass()).add(packet);
++totalPacketsReceivedCount;
totalPacketsReceivedBytes += packet.getTotalLength();
}
public void recordUnrecognisedPacket() {
++totalPacketsUnrecognised;
}
public Map<Class<Packet>, List<Packet>> getSendHistory() {
return Collections.unmodifiableMap(sendHistory);
}
public int getTotalPacketsSentCount() {
return totalPacketsSentCount;
}
public long getTotalPacketsSentBytes() {
return totalPacketsSentBytes;
}
public Map<Class<Packet>, List<Packet>> getReceiveHistory() {
return Collections.unmodifiableMap(receiveHistory);
}
public int getTotalPacketsReceivedCount() {
return totalPacketsReceivedCount;
}
public long getTotalPacketsReceivedBytes() {
return totalPacketsReceivedBytes;
}
}
@@ -18,6 +18,8 @@ public class Topic {
private final List<Message> messages = Collections.synchronizedList(new ArrayList<>());
private final Map<Long, Message> messageByIDs = new ConcurrentHashMap<>();
private final Set<User> typingUsers = Collections.synchronizedSet(new HashSet<>());
private long messageTrackerID = 1;
public Topic(long id, String name, String description, User host, short color) {
@@ -101,6 +103,30 @@ public class Topic {
this.changeSupport.firePropertyChange("messageAdd", null, message);
}
public Set<User> getTypingUsersReadOnly() {
return Collections.unmodifiableSet(typingUsers);
}
public int getTypingUsersCount() {
return typingUsers.size();
}
public void addTypingUser(User user) {
if (typingUsers.contains(user)) return;
typingUsers.add(user);
this.changeSupport.firePropertyChange("typingAdd", null, user);
}
public void removeTypingUser(User user) {
if (!typingUsers.contains(user)) return;
typingUsers.remove(user);
this.changeSupport.firePropertyChange("typingRemove", null, user);
}
public Message getMessageByID(long messageID) {
return messageByIDs.getOrDefault(messageID, null);
}
@@ -0,0 +1,59 @@
package dev.wiing.gossip.lib.packets;
import dev.wiing.gossip.lib.data.ListData;
import dev.wiing.gossip.lib.data.LongData;
import java.nio.ByteBuffer;
import java.util.List;
public class TypingListUpdatePacket extends Packet {
public static final short TYPE = 0x42;
public static final int LENGTH = 0x000;
private long topicID;
private final ListData<LongData> typingMembers = new ListData<>(LongData::new);
public TypingListUpdatePacket() {
super(TYPE, LENGTH);
}
private void updateLength() {
setLength(8 + typingMembers.getLength());
}
@Override
public int getLength() {
updateLength();
return super.getLength();
}
public long getTopicID() {
return topicID;
}
public void setTopicID(long topicID) {
this.topicID = topicID;
}
public List<LongData> getTypingMembers() {
return typingMembers.getData();
}
@Override
public Packet readBytes(ByteBuffer buffer, int size) {
TypingListUpdatePacket packet = new TypingListUpdatePacket();
packet.setLength(size);
packet.topicID = buffer.getLong();
packet.typingMembers.setBytes(buffer);
return packet;
}
@Override
public void writeBytes(ByteBuffer buffer) {
buffer.putLong(topicID);
buffer.put(typingMembers.getBytes());
}
}
@@ -0,0 +1,52 @@
package dev.wiing.gossip.lib.packets;
import dev.wiing.gossip.lib.data.AuthSecret;
import java.nio.ByteBuffer;
public class TypingPingPacket extends AuthRequiredPacket {
public static final short TYPE = 0x41;
public static final int LENGTH = 0x029;
private long topicID;
private boolean typing;
public TypingPingPacket() {
super(TYPE, LENGTH);
}
public long getTopicID() {
return topicID;
}
public void setTopicID(long topicID) {
this.topicID = topicID;
}
public boolean isTyping() {
return typing;
}
public void setTyping(boolean typing) {
this.typing = typing;
}
@Override
public Packet readBytes(ByteBuffer buffer, int size) {
TypingPingPacket packet = new TypingPingPacket();
packet.setAuth(new AuthSecret(buffer));
packet.topicID = buffer.getLong();
packet.typing = buffer.get() > 0;
return packet;
}
@Override
public void writeBytes(ByteBuffer buffer) {
buffer.put(getAuth().getBytes());
buffer.putLong(topicID);
buffer.put(typing ? (byte)1 : 0);
}
}