Typing indicator + Metrics
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user