Access restrictions, errors & properties
This commit is contained in:
parent
a477a87c3d
commit
3c7845f439
@ -84,8 +84,13 @@ public class Connection {
|
||||
}
|
||||
|
||||
connection.updateConnectionStats();
|
||||
|
||||
} catch (PacketException e) {
|
||||
e.printStackTrace();
|
||||
|
||||
} catch (SocketException e) {
|
||||
break;
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@ -212,9 +217,19 @@ public class Connection {
|
||||
}
|
||||
|
||||
try {
|
||||
return getPacketManager().readPacket(getSocket().getInputStream());
|
||||
Packet packet = getPacketManager().readPacket(getSocket().getInputStream());
|
||||
|
||||
if (packet.getType() == ErrorPacket.TYPE) {
|
||||
ErrorPacket errorPacket = (ErrorPacket) packet;
|
||||
|
||||
throw new PacketException(packetManager.getPacketMap().get(errorPacket.getErrorType()), errorPacket.getReason());
|
||||
}
|
||||
|
||||
return packet;
|
||||
|
||||
} catch (SocketException e) {
|
||||
throw e;
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
@ -231,6 +246,7 @@ public class Connection {
|
||||
|
||||
if (packet.getType() != packetType) {
|
||||
queuedPackets.add(packet);
|
||||
updateConnectionStats();
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
package dev.wiing.gossip.client;
|
||||
|
||||
import dev.wiing.gossip.lib.packets.Packet;
|
||||
|
||||
public class PacketException extends RuntimeException {
|
||||
public PacketException(Packet packet, String message) {
|
||||
super("Packet of type=" + packet.getType() + " (" + packet.getClass().getSimpleName() + ") caused an error: " + message);
|
||||
}
|
||||
}
|
||||
@ -13,6 +13,7 @@ import dev.wiing.gossip.lib.packets.MessagePushPacket;
|
||||
import dev.wiing.gossip.lib.packets.TopicJoinPacket;
|
||||
import dev.wiing.gossip.lib.packets.TypingPingPacket;
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.Parent;
|
||||
@ -109,6 +110,13 @@ public class MainChatController {
|
||||
});
|
||||
|
||||
updateTypingMembers();
|
||||
|
||||
txtCompose.getParagraphs().addListener((ListChangeListener<? super CharSequence>) c -> {
|
||||
int height = 8 + Math.min(c.getList().size(), 5) * 17;
|
||||
|
||||
txtCompose.setMinHeight(height);
|
||||
txtCompose.setPrefHeight(height);
|
||||
});
|
||||
}
|
||||
|
||||
private void setTopicName(String name) {
|
||||
@ -256,7 +264,12 @@ public class MainChatController {
|
||||
}
|
||||
|
||||
public void updateTypingMembers() {
|
||||
if (topic.getTypingUsersCount() == 0) {
|
||||
int typingUsersCount = topic.getTypingUsersCount();
|
||||
|
||||
if (topic.getTypingUsersReadOnly().stream().anyMatch(user -> user.getUserID() == Connection.getInstance().getSelf().getUserID()))
|
||||
--typingUsersCount;
|
||||
|
||||
if (typingUsersCount == 0) {
|
||||
lblTyping.setText("");
|
||||
lblTyping.setMinHeight(0);
|
||||
lblTyping.setMaxHeight(0);
|
||||
@ -266,7 +279,7 @@ public class MainChatController {
|
||||
lblTyping.setMinHeight(Region.USE_COMPUTED_SIZE);
|
||||
lblTyping.setMaxHeight(Region.USE_COMPUTED_SIZE);
|
||||
|
||||
if (topic.getTypingUsersCount() > 3) {
|
||||
if (typingUsersCount > 3) {
|
||||
lblTyping.setText("Several babblers are writing...");
|
||||
return;
|
||||
}
|
||||
@ -274,11 +287,14 @@ public class MainChatController {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (User user : topic.getTypingUsersReadOnly()) {
|
||||
if (user.getUserID() == Connection.getInstance().getSelf().getUserID())
|
||||
continue;
|
||||
|
||||
if (!sb.isEmpty()) sb.append(", ");
|
||||
sb.append(user.getUsername());
|
||||
}
|
||||
|
||||
sb.append(topic.getTypingUsersCount() == 1 ? " is" : " are");
|
||||
sb.append(typingUsersCount == 1 ? " is" : " are");
|
||||
|
||||
sb.append(" writing...");
|
||||
|
||||
@ -289,7 +305,4 @@ public class MainChatController {
|
||||
return Utils.createInstance("views/chat-view.fxml");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO: better mutliline input
|
||||
// 25 + 17 per extra line
|
||||
}
|
||||
@ -31,6 +31,7 @@ public class MainController implements Initializable {
|
||||
Topic activeTopic = null;
|
||||
Parent activeTopicElement = null;
|
||||
private final Map<Long, Pair<Parent, MainChatController>> topicMap = new ConcurrentHashMap<>();
|
||||
private final Map<Long, Thread> topicUpdateThreadMap = new ConcurrentHashMap<>();
|
||||
|
||||
@FXML
|
||||
private VBox vboxRoot;
|
||||
@ -121,6 +122,10 @@ public class MainController implements Initializable {
|
||||
for (LongData userID : packet.getUsersJoined()) {
|
||||
User userJoined = Connection.getInstance().getUser(userID.getValue());
|
||||
topic.addUser(userJoined);
|
||||
|
||||
if (userJoined.getUserID() == Connection.getInstance().getSelf().getUserID()) {
|
||||
pullTopicMessages(topic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,7 +144,7 @@ public class MainController implements Initializable {
|
||||
|
||||
User author = Connection.getInstance().getUser(packet.getAuthorID());
|
||||
|
||||
UserMessage userMessage = new UserMessage(packet.getMessageID(), author, topic, packet.getContents());
|
||||
UserMessage userMessage = new UserMessage(packet.getMessageID(), author, topic, packet.getContents(), packet.getCreatedTimestamp());
|
||||
|
||||
topic.addMessage(userMessage);
|
||||
});
|
||||
@ -154,7 +159,7 @@ public class MainController implements Initializable {
|
||||
targetUser = Connection.getInstance().getUser(packet.getUserID());
|
||||
}
|
||||
|
||||
SystemMessage systemMessage = new SystemMessage(packet.getMessageID(), topic, packet.getSystemType(), targetUser, packet.getContent());
|
||||
SystemMessage systemMessage = new SystemMessage(packet.getMessageID(), topic, packet.getSystemType(), targetUser, packet.getContent(), packet.getCreatedTimestamp());
|
||||
|
||||
topic.addMessage(systemMessage);
|
||||
});
|
||||
@ -178,10 +183,12 @@ public class MainController implements Initializable {
|
||||
}
|
||||
|
||||
private void fetchTopicMessage(Topic topic, long messageID) {
|
||||
if (topic.hasMessageByID(messageID)) return;
|
||||
|
||||
MessageFetchPacket messageFetchPacket = new MessageFetchPacket();
|
||||
messageFetchPacket.setMessageID(messageID);
|
||||
messageFetchPacket.setTopicID(topic.getId());
|
||||
Connection.getInstance().sendPacket(messageFetchPacket);
|
||||
Connection.getInstance().sendPacketAuthenticated(messageFetchPacket);
|
||||
|
||||
try {
|
||||
Packet messageFetchResult = Connection.getInstance().nextPacket(MessageDataPacket.TYPE);
|
||||
@ -195,7 +202,8 @@ public class MainController implements Initializable {
|
||||
messageData.getMessageID(),
|
||||
Connection.getInstance().getUser(messageData.getUserAuthorID()),
|
||||
Connection.getInstance().getTopic(messageData.getTopicID()),
|
||||
messageData.getUserContents()
|
||||
messageData.getUserContents(),
|
||||
messageData.getCreatedTimestamp()
|
||||
);
|
||||
}
|
||||
else if (messageData.isSystemMessage()) {
|
||||
@ -204,7 +212,8 @@ public class MainController implements Initializable {
|
||||
Connection.getInstance().getTopic(messageData.getTopicID()),
|
||||
messageData.getSystemType(),
|
||||
Connection.getInstance().getUser(messageData.getSystemUserID()),
|
||||
messageData.getSystemContents()
|
||||
messageData.getSystemContents(),
|
||||
messageData.getCreatedTimestamp()
|
||||
);
|
||||
}
|
||||
|
||||
@ -232,10 +241,6 @@ public class MainController implements Initializable {
|
||||
AnchorPane.setRightAnchor(contentPair.first(), 0.0);
|
||||
contentPair.first().setVisible(false);
|
||||
|
||||
MessageListFetchPacket messageListFetchPacket = new MessageListFetchPacket();
|
||||
messageListFetchPacket.setTopicID(topic.getId());
|
||||
Connection.getInstance().sendPacket(messageListFetchPacket);
|
||||
|
||||
Platform.runLater(() -> {
|
||||
paneContent.getChildren().add(contentPair.first());
|
||||
|
||||
@ -246,6 +251,16 @@ public class MainController implements Initializable {
|
||||
setActiveTopic(topic, pair.first());
|
||||
});
|
||||
|
||||
pullTopicMessages(topic);
|
||||
}
|
||||
|
||||
private void pullTopicMessages(Topic topic) {
|
||||
if (!topic.hasUser(Connection.getInstance().getSelf())) return;
|
||||
|
||||
MessageListFetchPacket messageListFetchPacket = new MessageListFetchPacket();
|
||||
messageListFetchPacket.setTopicID(topic.getId());
|
||||
Connection.getInstance().sendPacketAuthenticated(messageListFetchPacket);
|
||||
|
||||
try {
|
||||
Packet messageListFetchResult = Connection.getInstance().nextPacket(MessageListDataPacket.TYPE);
|
||||
if (messageListFetchResult == null) return;
|
||||
@ -259,6 +274,22 @@ public class MainController implements Initializable {
|
||||
} catch (SocketException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
long topicID = topic.getId();
|
||||
|
||||
Thread thread = new Thread(() -> {
|
||||
while (topicMap.containsKey(topicID)) {
|
||||
topic.updateAllRelativeTimes();
|
||||
|
||||
try {
|
||||
Thread.sleep(30_000);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
|
||||
topicUpdateThreadMap.put(topicID, thread);
|
||||
}
|
||||
|
||||
public void setActiveTopic(Topic topic, Parent element) {
|
||||
|
||||
@ -6,6 +6,7 @@ import dev.wiing.gossip.client.generic.Pair;
|
||||
import dev.wiing.gossip.client.utils.Utils;
|
||||
import dev.wiing.gossip.lib.models.UserMessage;
|
||||
import dev.wiing.gossip.lib.models.User;
|
||||
import javafx.application.Platform;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.Parent;
|
||||
@ -42,6 +43,7 @@ public class MessageItemController implements Initializable {
|
||||
@Override
|
||||
public void initialize(URL location, ResourceBundle resources) {
|
||||
vboxMessages.getChildren().clear();
|
||||
lblTimeAgo.setText("");
|
||||
}
|
||||
|
||||
public User getAuthor() {
|
||||
@ -70,6 +72,10 @@ public class MessageItemController implements Initializable {
|
||||
this.lblTag.getStyleClass().add("secondary");
|
||||
}
|
||||
|
||||
if (userMessages.isEmpty()) {
|
||||
userMessage.attachRelativeTime(data -> Platform.runLater(() -> lblTimeAgo.setText(data)));
|
||||
}
|
||||
|
||||
userMessages.add(userMessage);
|
||||
|
||||
Label label = new Label(userMessage.getContents());
|
||||
|
||||
@ -3,6 +3,7 @@ package dev.wiing.gossip.client.controllers.item;
|
||||
import dev.wiing.gossip.client.generic.Pair;
|
||||
import dev.wiing.gossip.client.utils.Utils;
|
||||
import dev.wiing.gossip.lib.models.SystemMessage;
|
||||
import javafx.application.Platform;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.Parent;
|
||||
@ -32,6 +33,9 @@ public class SystemMessageItemController {
|
||||
public void setSystemMessage(SystemMessage systemMessage) {
|
||||
this.systemMessage = systemMessage;
|
||||
|
||||
lblTimeAgo.setText("");
|
||||
systemMessage.attachRelativeTime(data -> Platform.runLater(() -> this.lblTimeAgo.setText(data)));
|
||||
|
||||
switch (this.systemMessage.getType()) {
|
||||
case SystemMessage.SystemType.USER_JOIN:
|
||||
removeNode(lblPrefix);
|
||||
|
||||
@ -88,7 +88,7 @@
|
||||
<String fx:value="list" />
|
||||
</styleClass>
|
||||
<children>
|
||||
<TextArea fx:id="txtCompose" maxHeight="1.7976931348623157E308" minHeight="1.0" onKeyPressed="#onKeyPressed" onKeyTyped="#onKeyTyped" prefHeight="25.0" prefRowCount="1" promptText="Compose..." styleClass="transparent" wrapText="true" HBox.hgrow="ALWAYS" />
|
||||
<TextArea fx:id="txtCompose" maxHeight="1.7976931348623157E308" minHeight="25.0" onKeyPressed="#onKeyPressed" onKeyTyped="#onKeyTyped" prefHeight="25.0" prefRowCount="1" promptText="Compose..." styleClass="transparent" wrapText="true" HBox.hgrow="ALWAYS" />
|
||||
<ImageView fitHeight="20.0" fitWidth="20.0" onMouseClicked="#onSend" opacity="0.5" pickOnBounds="true" preserveRatio="true">
|
||||
<image>
|
||||
<Image url="@../icons/icon-send-2.png" />
|
||||
|
||||
@ -12,6 +12,7 @@ public class PacketManager {
|
||||
{
|
||||
add(new TestPacket());
|
||||
add(new AckPacket());
|
||||
add(new ErrorPacket());
|
||||
|
||||
add(new RegisterRequestPacket());
|
||||
add(new RegisterCredentialsPacket());
|
||||
|
||||
@ -1,16 +1,25 @@
|
||||
package dev.wiing.gossip.lib.models;
|
||||
|
||||
import dev.wiing.gossip.lib.models.ext.DynamicProperty;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.FormatStyle;
|
||||
|
||||
public class Message {
|
||||
private long id;
|
||||
private final Topic topic;
|
||||
private final LocalDateTime postTime;
|
||||
|
||||
private final DynamicProperty<String> relativeTime;
|
||||
|
||||
public Message(long id, Topic topic, LocalDateTime postTime) {
|
||||
this.id = id;
|
||||
this.topic = topic;
|
||||
this.postTime = postTime;
|
||||
|
||||
relativeTime = new DynamicProperty<>("Just now");
|
||||
}
|
||||
|
||||
public Message(long id, Topic topic) {
|
||||
@ -32,4 +41,32 @@ public class Message {
|
||||
public LocalDateTime getPostTime() {
|
||||
return postTime;
|
||||
}
|
||||
|
||||
public void attachRelativeTime(DynamicProperty.ICallback<String> callback) {
|
||||
relativeTime.attach(callback);
|
||||
}
|
||||
|
||||
public void recalculateRelativeTime(LocalDateTime now) {
|
||||
Duration duration = Duration.between(postTime, now);
|
||||
|
||||
if (duration.toDays() > 7) {
|
||||
relativeTime.setValue(postTime.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.SHORT)));
|
||||
|
||||
} else if (duration.toDays() > 0) {
|
||||
setRelativeTime("day", duration.toDays());
|
||||
|
||||
} else if (duration.toHours() > 0) {
|
||||
setRelativeTime("hour", duration.toHours());
|
||||
|
||||
} else if (duration.toMinutes() > 0) {
|
||||
setRelativeTime("minute", duration.toMinutes());
|
||||
|
||||
} else {
|
||||
relativeTime.setValue("Just now");
|
||||
}
|
||||
}
|
||||
|
||||
private void setRelativeTime(String unit, long value) {
|
||||
relativeTime.setValue(value + " " + unit + (value > 1 ? "s" : "") + " ago");
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ package dev.wiing.gossip.lib.models;
|
||||
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.beans.PropertyChangeSupport;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@ -97,10 +98,14 @@ public class Topic {
|
||||
}
|
||||
|
||||
public void addMessage(Message message) {
|
||||
messages.add(message);
|
||||
messageByIDs.put(message.getId(), message);
|
||||
synchronized (messageByIDs) {
|
||||
if (messageByIDs.containsKey(message.getId())) return;
|
||||
|
||||
this.changeSupport.firePropertyChange("messageAdd", null, message);
|
||||
messageByIDs.put(message.getId(), message);
|
||||
messages.add(message);
|
||||
|
||||
this.changeSupport.firePropertyChange("messageAdd", null, message);
|
||||
}
|
||||
}
|
||||
|
||||
public Set<User> getTypingUsersReadOnly() {
|
||||
@ -131,6 +136,10 @@ public class Topic {
|
||||
return messageByIDs.getOrDefault(messageID, null);
|
||||
}
|
||||
|
||||
public boolean hasMessageByID(long messageID) {
|
||||
return messageByIDs.containsKey(messageID);
|
||||
}
|
||||
|
||||
public long getNextMessageID() {
|
||||
return messageTrackerID++;
|
||||
}
|
||||
@ -142,4 +151,13 @@ public class Topic {
|
||||
public void removeChangeListener(PropertyChangeListener listener) {
|
||||
this.changeSupport.removePropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
public void updateAllRelativeTimes() {
|
||||
synchronized (messages) {
|
||||
for (Message message : messages) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
message.recalculateRelativeTime(now);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package dev.wiing.gossip.lib.models;
|
||||
|
||||
import dev.wiing.gossip.lib.models.ext.DynamicProperty;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class UserMessage extends Message {
|
||||
|
||||
@ -0,0 +1,52 @@
|
||||
package dev.wiing.gossip.lib.models.ext;
|
||||
|
||||
import java.beans.PropertyChangeSupport;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class DynamicProperty<T> {
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ICallback<T> {
|
||||
void run(T data);
|
||||
}
|
||||
|
||||
private T value;
|
||||
|
||||
private final Set<ICallback<T>> callbacks = Collections.synchronizedSet(new HashSet<>());
|
||||
|
||||
public DynamicProperty(T defaultValue) {
|
||||
this.value = defaultValue;
|
||||
}
|
||||
|
||||
public void setValue(T value) {
|
||||
if (this.value.equals(value)) return;
|
||||
|
||||
this.value = value;
|
||||
|
||||
runCallbacks();
|
||||
}
|
||||
|
||||
public T getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void attach(ICallback<T> callback) {
|
||||
callbacks.add(callback);
|
||||
|
||||
callback.run(value);
|
||||
}
|
||||
|
||||
public void detach(ICallback<T> callback) {
|
||||
callbacks.remove(callback);
|
||||
}
|
||||
|
||||
private void runCallbacks() {
|
||||
synchronized (callbacks) {
|
||||
for (ICallback<T> callback : callbacks) {
|
||||
callback.run(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
package dev.wiing.gossip.lib.packets;
|
||||
|
||||
import dev.wiing.gossip.lib.data.StringData;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class ErrorPacket extends Packet {
|
||||
|
||||
public static short TYPE = 0x03;
|
||||
public static int LENGTH = 0x006;
|
||||
|
||||
private short errorType;
|
||||
private final StringData reason = new StringData();
|
||||
|
||||
public ErrorPacket() {
|
||||
super(TYPE, LENGTH);
|
||||
}
|
||||
|
||||
public short getErrorType() {
|
||||
return errorType;
|
||||
}
|
||||
|
||||
public void setErrorType(short errorType) {
|
||||
this.errorType = errorType;
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
return reason.getValue();
|
||||
}
|
||||
|
||||
public void setReason(String reason) {
|
||||
this.reason.setValue(reason);
|
||||
this.setLength(2 + this.reason.getLength());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Packet readBytes(ByteBuffer buffer, int size) {
|
||||
ErrorPacket packet = new ErrorPacket();
|
||||
packet.setLength(size);
|
||||
|
||||
packet.errorType = buffer.getShort();
|
||||
packet.reason.setBytes(buffer);
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBytes(ByteBuffer buffer) {
|
||||
buffer.putShort(errorType);
|
||||
buffer.put(reason.getBytes());
|
||||
}
|
||||
}
|
||||
@ -3,15 +3,19 @@ package dev.wiing.gossip.lib.packets;
|
||||
import dev.wiing.gossip.lib.data.StringData;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
|
||||
public class MessageCreatedPacket extends Packet {
|
||||
|
||||
public static final short TYPE = 0x32;
|
||||
public static final int LENGTH = 0x01C;
|
||||
public static final int LENGTH = 0x024;
|
||||
|
||||
private long messageID;
|
||||
private long authorID;
|
||||
private long topicID;
|
||||
private LocalDateTime createdTimestamp;
|
||||
private final StringData contents = new StringData();
|
||||
|
||||
public MessageCreatedPacket() {
|
||||
@ -42,13 +46,21 @@ public class MessageCreatedPacket extends Packet {
|
||||
this.topicID = topicID;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedTimestamp() {
|
||||
return createdTimestamp;
|
||||
}
|
||||
|
||||
public void setCreatedTimestamp(LocalDateTime createdTimestamp) {
|
||||
this.createdTimestamp = createdTimestamp;
|
||||
}
|
||||
|
||||
public String getContents() {
|
||||
return contents.getValue();
|
||||
}
|
||||
|
||||
public void setContents(String contents) {
|
||||
this.contents.setValue(contents);
|
||||
setLength(24 + this.contents.getLength());
|
||||
setLength(32 + this.contents.getLength());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -59,6 +71,7 @@ public class MessageCreatedPacket extends Packet {
|
||||
packet.messageID = buffer.getLong();
|
||||
packet.authorID = buffer.getLong();
|
||||
packet.topicID = buffer.getLong();
|
||||
packet.createdTimestamp = LocalDateTime.ofInstant(Instant.ofEpochMilli(buffer.getLong()), ZoneOffset.UTC);
|
||||
packet.contents.setBytes(buffer);
|
||||
|
||||
return packet;
|
||||
@ -69,6 +82,7 @@ public class MessageCreatedPacket extends Packet {
|
||||
buffer.putLong(messageID);
|
||||
buffer.putLong(authorID);
|
||||
buffer.putLong(topicID);
|
||||
buffer.putLong(createdTimestamp.toInstant(ZoneOffset.UTC).toEpochMilli());
|
||||
buffer.put(contents.getBytes());
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,10 @@ package dev.wiing.gossip.lib.packets;
|
||||
import dev.wiing.gossip.lib.data.StringData;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
|
||||
public class MessageDataPacket extends Packet {
|
||||
|
||||
@ -16,6 +20,7 @@ public class MessageDataPacket extends Packet {
|
||||
|
||||
private long messageID;
|
||||
private long topicID;
|
||||
private LocalDateTime createdTimestamp;
|
||||
|
||||
private byte messageType;
|
||||
|
||||
@ -33,7 +38,7 @@ public class MessageDataPacket extends Packet {
|
||||
}
|
||||
|
||||
private void updateLength() {
|
||||
setLength(17 +
|
||||
setLength(25 +
|
||||
(isUserMessage() ? (8 + userContents.getLength()) : 0) +
|
||||
(isSystemMessage() ? (16 + systemContents.getLength()) : 0));
|
||||
}
|
||||
@ -62,6 +67,14 @@ public class MessageDataPacket extends Packet {
|
||||
this.topicID = topicID;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedTimestamp() {
|
||||
return createdTimestamp;
|
||||
}
|
||||
|
||||
public void setCreatedTimestamp(LocalDateTime createdTimestamp) {
|
||||
this.createdTimestamp = createdTimestamp;
|
||||
}
|
||||
|
||||
public byte getMessageType() {
|
||||
return messageType;
|
||||
}
|
||||
@ -119,6 +132,7 @@ public class MessageDataPacket extends Packet {
|
||||
|
||||
packet.messageID = buffer.getLong();
|
||||
packet.topicID = buffer.getLong();
|
||||
packet.createdTimestamp = LocalDateTime.ofInstant(Instant.ofEpochMilli(buffer.getLong()), ZoneOffset.UTC);
|
||||
packet.messageType = buffer.get();
|
||||
|
||||
if (packet.isUserMessage()) {
|
||||
@ -137,6 +151,7 @@ public class MessageDataPacket extends Packet {
|
||||
public void writeBytes(ByteBuffer buffer) {
|
||||
buffer.putLong(messageID);
|
||||
buffer.putLong(topicID);
|
||||
buffer.putLong(createdTimestamp.toInstant(ZoneOffset.UTC).toEpochMilli());
|
||||
buffer.put(messageType);
|
||||
|
||||
if (isUserMessage()) {
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
package dev.wiing.gossip.lib.packets;
|
||||
|
||||
import dev.wiing.gossip.lib.data.AuthSecret;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class MessageFetchPacket extends Packet {
|
||||
public class MessageFetchPacket extends AuthRequiredPacket {
|
||||
|
||||
public static final short TYPE = 0x36;
|
||||
public static final int LENGTH = 0x010;
|
||||
public static final int LENGTH = 0x030;
|
||||
|
||||
private long topicID;
|
||||
private long messageID;
|
||||
@ -34,6 +36,7 @@ public class MessageFetchPacket extends Packet {
|
||||
public Packet readBytes(ByteBuffer buffer, int size) {
|
||||
MessageFetchPacket packet = new MessageFetchPacket();
|
||||
|
||||
packet.setAuth(new AuthSecret(buffer));
|
||||
packet.topicID = buffer.getLong();
|
||||
packet.messageID = buffer.getLong();
|
||||
|
||||
@ -42,6 +45,7 @@ public class MessageFetchPacket extends Packet {
|
||||
|
||||
@Override
|
||||
public void writeBytes(ByteBuffer buffer) {
|
||||
buffer.put(getAuth().getBytes());
|
||||
buffer.putLong(topicID);
|
||||
buffer.putLong(messageID);
|
||||
}
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
package dev.wiing.gossip.lib.packets;
|
||||
|
||||
import dev.wiing.gossip.lib.data.AuthSecret;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class MessageListFetchPacket extends Packet {
|
||||
public class MessageListFetchPacket extends AuthRequiredPacket {
|
||||
|
||||
public static final short TYPE = 0x34;
|
||||
public static final int LENGTH = 0x008;
|
||||
public static final int LENGTH = 0x028;
|
||||
|
||||
private long topicID;
|
||||
|
||||
@ -25,6 +27,7 @@ public class MessageListFetchPacket extends Packet {
|
||||
public Packet readBytes(ByteBuffer buffer, int size) {
|
||||
MessageListFetchPacket packet = new MessageListFetchPacket();
|
||||
|
||||
packet.setAuth(new AuthSecret(buffer));
|
||||
packet.topicID = buffer.getLong();
|
||||
|
||||
return packet;
|
||||
@ -32,6 +35,7 @@ public class MessageListFetchPacket extends Packet {
|
||||
|
||||
@Override
|
||||
public void writeBytes(ByteBuffer buffer) {
|
||||
buffer.put(getAuth().getBytes());
|
||||
buffer.putLong(topicID);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,15 +3,19 @@ package dev.wiing.gossip.lib.packets;
|
||||
import dev.wiing.gossip.lib.data.StringData;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Objects;
|
||||
|
||||
public class MessageSystemPacket extends Packet {
|
||||
|
||||
public static final short TYPE = 0x33;
|
||||
public static final int LENGTH = 0x01E;
|
||||
public static final int LENGTH = 0x026;
|
||||
|
||||
private long messageID;
|
||||
private long topicID;
|
||||
private LocalDateTime createdTimestamp;
|
||||
private short systemType = 0;
|
||||
private long userID = 0;
|
||||
private final StringData content = new StringData();
|
||||
@ -36,6 +40,14 @@ public class MessageSystemPacket extends Packet {
|
||||
this.topicID = topicID;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedTimestamp() {
|
||||
return createdTimestamp;
|
||||
}
|
||||
|
||||
public void setCreatedTimestamp(LocalDateTime createdTimestamp) {
|
||||
this.createdTimestamp = createdTimestamp;
|
||||
}
|
||||
|
||||
public short getSystemType() {
|
||||
return systemType;
|
||||
}
|
||||
@ -58,7 +70,7 @@ public class MessageSystemPacket extends Packet {
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content.setValue(content);
|
||||
setLength(26 + this.content.getLength());
|
||||
setLength(34 + this.content.getLength());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -68,6 +80,7 @@ public class MessageSystemPacket extends Packet {
|
||||
|
||||
packet.messageID = buffer.getLong();
|
||||
packet.topicID = buffer.getLong();
|
||||
packet.createdTimestamp = LocalDateTime.ofInstant(Instant.ofEpochMilli(buffer.getLong()), ZoneOffset.UTC);
|
||||
packet.systemType = buffer.getShort();
|
||||
packet.userID = buffer.getLong();
|
||||
packet.content.setBytes(buffer);
|
||||
@ -79,6 +92,7 @@ public class MessageSystemPacket extends Packet {
|
||||
public void writeBytes(ByteBuffer buffer) {
|
||||
buffer.putLong(messageID);
|
||||
buffer.putLong(topicID);
|
||||
buffer.putLong(createdTimestamp.toInstant(ZoneOffset.UTC).toEpochMilli());
|
||||
buffer.putShort(systemType);
|
||||
buffer.putLong(userID);
|
||||
buffer.put(content.getBytes());
|
||||
|
||||
@ -6,7 +6,7 @@ import java.nio.ByteBuffer;
|
||||
|
||||
public class RegisterCredentialsPacket extends Packet {
|
||||
|
||||
public static short TYPE = 0x04;
|
||||
public static short TYPE = 0x05;
|
||||
public static int LENGTH = 0x028;
|
||||
|
||||
private AuthSecret secret;
|
||||
|
||||
@ -6,7 +6,7 @@ import java.nio.ByteBuffer;
|
||||
|
||||
public class RegisterRequestPacket extends Packet {
|
||||
|
||||
public static final short TYPE = 0x03;
|
||||
public static final short TYPE = 0x04;
|
||||
public static final int SIZE = 0x0005;
|
||||
|
||||
private final StringData username = new StringData();
|
||||
|
||||
@ -5,4 +5,5 @@ module dev.wiing.gossip.lib {
|
||||
exports dev.wiing.gossip.lib.packets;
|
||||
exports dev.wiing.gossip.lib.data;
|
||||
exports dev.wiing.gossip.lib.models;
|
||||
exports dev.wiing.gossip.lib.models.ext;
|
||||
}
|
||||
@ -89,6 +89,54 @@ public record UserSocket(Socket socket) implements Runnable {
|
||||
Database.getInstance().disconnectUser(user);
|
||||
}
|
||||
|
||||
private void replyError(Packet source, String reason) {
|
||||
ErrorPacket packet = new ErrorPacket();
|
||||
packet.setErrorType(source.getType());
|
||||
packet.setReason(reason);
|
||||
|
||||
try {
|
||||
Globals.getPacketManager().replyPacket(source, packet);
|
||||
} catch (IOException e) {
|
||||
logger.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean errorNullTopic(Packet source, Topic topic) {
|
||||
if (topic == null) {
|
||||
replyError(source, "Topic does not exist");
|
||||
return true;
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean errorNullUser(Packet source, User user) {
|
||||
if (user == null) {
|
||||
replyError(source, "User does not exist");
|
||||
return true;
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean errorNullMessage(Packet source, Message message) {
|
||||
if (message == null) {
|
||||
replyError(source, "Message does not exist");
|
||||
return true;
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean errorUserNotInTopic(Packet source, Topic topic, User user) {
|
||||
if (!topic.hasUser(user)) {
|
||||
replyError(source, "User does not have access to Topic");
|
||||
return true;
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void onRegisterUser(RegisterRequestPacket packet) {
|
||||
SecretUser user = Database.getInstance().registerUser(packet.getUsername(), packet.getAvatarID(), packet.getSource());
|
||||
|
||||
@ -108,7 +156,7 @@ public record UserSocket(Socket socket) implements Runnable {
|
||||
private void onCreateTopic(TopicPushPacket packet) {
|
||||
Topic topic = Database.getInstance().createTopic(packet.getAuth(), packet.getTopicName(), packet.getTopicDescription());
|
||||
|
||||
if (topic == null) return;
|
||||
if (errorNullTopic(packet, topic)) return;
|
||||
|
||||
info("\"{}\" created topic \"{}\" (#{})", topic.getHost().getUsername().toUpperCase(), topic.getName().toUpperCase(), topic.getId());
|
||||
|
||||
@ -136,12 +184,12 @@ public record UserSocket(Socket socket) implements Runnable {
|
||||
Topic topic = Database.getInstance().getTopic(packet.getTopicID());
|
||||
User requester = Database.getInstance().getUserBySecret(packet.getAuth());
|
||||
|
||||
if (requester == null) return;
|
||||
if (topic == null) return;
|
||||
if (errorNullUser(packet, requester)) return;
|
||||
if (errorNullTopic(packet, topic)) return;
|
||||
|
||||
if (topic.hasUser(requester)) return;
|
||||
|
||||
info("\"{}\" joined topic \"{}\" (#{})", topic.getHost().getUsername().toUpperCase(), topic.getName().toUpperCase(), topic.getId());
|
||||
info("\"{}\" joined topic \"{}\" (#{})", requester.getUsername().toUpperCase(), topic.getName().toUpperCase(), topic.getId());
|
||||
topic.addUser(requester);
|
||||
|
||||
SystemMessage message = new SystemMessage(topic.getNextMessageID(), topic, SystemMessage.SystemType.USER_JOIN, requester, "");
|
||||
@ -161,7 +209,9 @@ public record UserSocket(Socket socket) implements Runnable {
|
||||
updatePacket.getUsersJoined().add(new LongData().setValue(requester.getUserID()));
|
||||
|
||||
MessageSystemPacket systemPacket = new MessageSystemPacket();
|
||||
systemPacket.setMessageID(message.getId());
|
||||
systemPacket.setTopicID(topic.getId());
|
||||
systemPacket.setCreatedTimestamp(message.getPostTime());
|
||||
systemPacket.setSystemType(message.getType());
|
||||
systemPacket.setUserID(message.getUser().getUserID());
|
||||
|
||||
@ -197,7 +247,7 @@ public record UserSocket(Socket socket) implements Runnable {
|
||||
private void onFetchTopic(TopicFetchPacket packet) {
|
||||
Topic topic = Database.getInstance().getTopic(packet.getTopicID());
|
||||
|
||||
if (topic == null) return;
|
||||
if (errorNullTopic(packet, topic)) return;
|
||||
|
||||
info("Requested topic #{}", topic.getId());
|
||||
|
||||
@ -221,7 +271,7 @@ public record UserSocket(Socket socket) implements Runnable {
|
||||
private void onFetchUser(UserFetchPacket packet) {
|
||||
User user = Database.getInstance().getUserByID(packet.getUserID());
|
||||
|
||||
if (user == null) return;
|
||||
if (errorNullUser(packet, user)) return;
|
||||
|
||||
info("Requested user #{}", user.getUserID());
|
||||
|
||||
@ -241,8 +291,8 @@ public record UserSocket(Socket socket) implements Runnable {
|
||||
Topic topic = Database.getInstance().getTopic(packet.getTopicID());
|
||||
User user = Database.getInstance().getUserBySecret(packet.getAuth());
|
||||
|
||||
if (user == null) return;
|
||||
if (topic == null) return;
|
||||
if (errorNullUser(packet, user)) return;
|
||||
if (errorNullTopic(packet, topic)) return;
|
||||
|
||||
UserMessage userMessage = new UserMessage(topic.getNextMessageID(), user, topic, packet.getMessage());
|
||||
|
||||
@ -260,8 +310,10 @@ public record UserSocket(Socket socket) implements Runnable {
|
||||
}
|
||||
|
||||
MessageCreatedPacket messageCreated = new MessageCreatedPacket();
|
||||
messageCreated.setMessageID(userMessage.getId());
|
||||
messageCreated.setAuthorID(user.getUserID());
|
||||
messageCreated.setTopicID(topic.getId());
|
||||
messageCreated.setCreatedTimestamp(userMessage.getPostTime());
|
||||
messageCreated.setContents(userMessage.getContents());
|
||||
|
||||
for (User babbler : topic.getUsersReadOnly().values()) {
|
||||
@ -279,8 +331,12 @@ public record UserSocket(Socket socket) implements Runnable {
|
||||
|
||||
private void onMessageListFetch(MessageListFetchPacket packet) {
|
||||
Topic topic = Database.getInstance().getTopic(packet.getTopicID());
|
||||
User requester = Database.getInstance().getUserBySecret(packet.getAuth());
|
||||
|
||||
if (topic == null) return;
|
||||
if (errorNullTopic(packet, topic)) return;
|
||||
if (errorNullUser(packet, requester)) return;
|
||||
|
||||
if (errorUserNotInTopic(packet, topic, requester)) return;
|
||||
|
||||
info("Requested Message list on #{}", topic.getId());
|
||||
|
||||
@ -299,18 +355,23 @@ public record UserSocket(Socket socket) implements Runnable {
|
||||
|
||||
private void onFetchMessage(MessageFetchPacket packet) {
|
||||
Topic topic = Database.getInstance().getTopic(packet.getTopicID());
|
||||
User requester = Database.getInstance().getUserBySecret(packet.getAuth());
|
||||
|
||||
if (topic == null) return;
|
||||
if (errorNullTopic(packet, topic)) return;
|
||||
if (errorNullUser(packet, requester)) return;
|
||||
|
||||
if (errorUserNotInTopic(packet, topic, requester)) return;
|
||||
|
||||
Message message = topic.getMessageByID(packet.getMessageID());
|
||||
|
||||
if (message == null) return;
|
||||
|
||||
if (errorNullMessage(packet, message)) return;
|
||||
|
||||
info("Requested message #{} on #{}", message.getId(), topic.getId());
|
||||
|
||||
MessageDataPacket resp = new MessageDataPacket();
|
||||
resp.setMessageID(message.getId());
|
||||
resp.setTopicID(message.getTopic().getId());
|
||||
resp.setCreatedTimestamp(message.getPostTime());
|
||||
|
||||
if (message instanceof UserMessage userMessage) {
|
||||
resp.setMessageType(MessageDataPacket.MessageType.USER);
|
||||
@ -335,11 +396,13 @@ public record UserSocket(Socket socket) implements Runnable {
|
||||
private void onTypingUser(TypingPingPacket packet) {
|
||||
User user = Database.getInstance().getUserBySecret(packet.getAuth());
|
||||
|
||||
if (user == null) return;
|
||||
if (errorNullUser(packet, user)) return;
|
||||
|
||||
TypingTopic topic = Database.getInstance().getTopic(packet.getTopicID());
|
||||
|
||||
if (topic == null || !topic.hasUser(user)) return;
|
||||
if (errorNullTopic(packet, topic)) return;
|
||||
|
||||
if (!topic.hasUser(user)) return;
|
||||
|
||||
if (packet.isTyping()) {
|
||||
topic.addTypingUser(user);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user