Added ability to add and remove channels.
This commit is contained in:
parent
b21137a767
commit
f159708fa2
|
@ -12,6 +12,7 @@ import nl.andrewl.concord_client.event.EventManager;
|
|||
import nl.andrewl.concord_client.event.handlers.ChannelMovedHandler;
|
||||
import nl.andrewl.concord_client.event.handlers.ChannelUsersResponseHandler;
|
||||
import nl.andrewl.concord_client.event.handlers.ChatHistoryResponseHandler;
|
||||
import nl.andrewl.concord_client.event.handlers.ServerMetaDataHandler;
|
||||
import nl.andrewl.concord_client.gui.MainWindow;
|
||||
import nl.andrewl.concord_client.model.ClientModel;
|
||||
import nl.andrewl.concord_core.msg.Message;
|
||||
|
@ -57,6 +58,7 @@ public class ConcordClient implements Runnable {
|
|||
this.eventManager.addHandler(ChannelUsersResponse.class, new ChannelUsersResponseHandler());
|
||||
this.eventManager.addHandler(ChatHistoryResponse.class, new ChatHistoryResponseHandler());
|
||||
this.eventManager.addHandler(Chat.class, (msg, client) -> client.getModel().getChatHistory().addChat(msg));
|
||||
this.eventManager.addHandler(ServerMetaData.class, new ServerMetaDataHandler());
|
||||
}
|
||||
|
||||
public void sendMessage(Message message) throws IOException {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package nl.andrewl.concord_client.event;
|
||||
|
||||
import nl.andrewl.concord_core.msg.types.ChannelUsersResponse;
|
||||
import nl.andrewl.concord_core.msg.types.ServerMetaData;
|
||||
import nl.andrewl.concord_core.msg.types.UserData;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
@ -8,5 +9,7 @@ import java.util.UUID;
|
|||
public interface ClientModelListener {
|
||||
default void channelMoved(UUID oldChannelId, UUID newChannelId) {}
|
||||
|
||||
default void usersUpdated(List<ChannelUsersResponse.UserData> users) {}
|
||||
default void usersUpdated(List<UserData> users) {}
|
||||
|
||||
default void serverMetaDataUpdated(ServerMetaData metaData) {}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package nl.andrewl.concord_client.event.handlers;
|
||||
|
||||
import nl.andrewl.concord_client.ConcordClient;
|
||||
import nl.andrewl.concord_client.event.MessageHandler;
|
||||
import nl.andrewl.concord_core.msg.types.ServerMetaData;
|
||||
|
||||
public class ServerMetaDataHandler implements MessageHandler<ServerMetaData> {
|
||||
@Override
|
||||
public void handle(ServerMetaData msg, ConcordClient client) {
|
||||
client.getModel().setServerMetaData(msg);
|
||||
}
|
||||
}
|
|
@ -4,7 +4,8 @@ import com.googlecode.lanterna.gui2.*;
|
|||
import lombok.Getter;
|
||||
import nl.andrewl.concord_client.ConcordClient;
|
||||
import nl.andrewl.concord_client.event.ClientModelListener;
|
||||
import nl.andrewl.concord_core.msg.types.ChannelUsersResponse;
|
||||
import nl.andrewl.concord_core.msg.types.ServerMetaData;
|
||||
import nl.andrewl.concord_core.msg.types.UserData;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
@ -22,11 +23,8 @@ public class ServerPanel extends Panel implements ClientModelListener {
|
|||
private final ChannelList channelList;
|
||||
private final UserList userList;
|
||||
|
||||
private final TextGUIThread guiThread;
|
||||
|
||||
public ServerPanel(ConcordClient client, Window window) {
|
||||
super(new BorderLayout());
|
||||
this.guiThread = window.getTextGUI().getGUIThread();
|
||||
this.channelChatBox = new ChannelChatBox(client, window);
|
||||
this.channelList = new ChannelList(client);
|
||||
this.channelList.setChannels();
|
||||
|
@ -55,9 +53,16 @@ public class ServerPanel extends Panel implements ClientModelListener {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void usersUpdated(List<ChannelUsersResponse.UserData> users) {
|
||||
this.guiThread.invokeLater(() -> {
|
||||
public void usersUpdated(List<UserData> users) {
|
||||
this.getTextGUI().getGUIThread().invokeLater(() -> {
|
||||
this.userList.updateUsers(users);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serverMetaDataUpdated(ServerMetaData metaData) {
|
||||
this.getTextGUI().getGUIThread().invokeLater(() -> {
|
||||
this.channelList.setChannels();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import com.googlecode.lanterna.gui2.LinearLayout;
|
|||
import com.googlecode.lanterna.gui2.Panel;
|
||||
import nl.andrewl.concord_client.ConcordClient;
|
||||
import nl.andrewl.concord_core.msg.types.ChannelUsersResponse;
|
||||
import nl.andrewl.concord_core.msg.types.UserData;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@ -17,7 +18,7 @@ public class UserList extends Panel {
|
|||
this.client = client;
|
||||
}
|
||||
|
||||
public void updateUsers(List<ChannelUsersResponse.UserData> usersResponse) {
|
||||
public void updateUsers(List<UserData> usersResponse) {
|
||||
this.removeAllComponents();
|
||||
for (var user : usersResponse) {
|
||||
Button b = new Button(user.getName(), () -> {
|
||||
|
|
|
@ -2,8 +2,8 @@ package nl.andrewl.concord_client.model;
|
|||
|
||||
import lombok.Getter;
|
||||
import nl.andrewl.concord_client.event.ClientModelListener;
|
||||
import nl.andrewl.concord_core.msg.types.ChannelUsersResponse;
|
||||
import nl.andrewl.concord_core.msg.types.ServerMetaData;
|
||||
import nl.andrewl.concord_core.msg.types.UserData;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -17,7 +17,7 @@ public class ClientModel {
|
|||
private ServerMetaData serverMetaData;
|
||||
|
||||
private UUID currentChannelId;
|
||||
private List<ChannelUsersResponse.UserData> knownUsers;
|
||||
private List<UserData> knownUsers;
|
||||
private final ChatHistory chatHistory;
|
||||
|
||||
private final List<ClientModelListener> modelListeners;
|
||||
|
@ -38,11 +38,16 @@ public class ClientModel {
|
|||
this.modelListeners.forEach(listener -> listener.channelMoved(oldId, newChannelId));
|
||||
}
|
||||
|
||||
public void setKnownUsers(List<ChannelUsersResponse.UserData> users) {
|
||||
public void setKnownUsers(List<UserData> users) {
|
||||
this.knownUsers = users;
|
||||
this.modelListeners.forEach(listener -> listener.usersUpdated(this.knownUsers));
|
||||
}
|
||||
|
||||
public void setServerMetaData(ServerMetaData metaData) {
|
||||
this.serverMetaData = metaData;
|
||||
this.modelListeners.forEach(listener -> listener.serverMetaDataUpdated(metaData));
|
||||
}
|
||||
|
||||
public void addListener(ClientModelListener listener) {
|
||||
this.modelListeners.add(listener);
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ public class Serializer {
|
|||
registerType(5, ChatHistoryResponse.class);
|
||||
registerType(6, ChannelUsersRequest.class);
|
||||
registerType(7, ChannelUsersResponse.class);
|
||||
registerType(8, ServerMetaData.class);
|
||||
}
|
||||
|
||||
private static void registerType(int id, Class<? extends Message> clazz) {
|
||||
|
|
|
@ -9,7 +9,6 @@ import java.io.DataInputStream;
|
|||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static nl.andrewl.concord_core.msg.MessageUtils.*;
|
||||
|
||||
|
@ -37,29 +36,4 @@ public class ChannelUsersResponse implements Message {
|
|||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class UserData implements Message {
|
||||
private UUID id;
|
||||
private String name;
|
||||
|
||||
@Override
|
||||
public int getByteCount() {
|
||||
return UUID_BYTES + getByteSize(this.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutputStream o) throws IOException {
|
||||
writeUUID(this.id, o);
|
||||
writeString(this.name, o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInputStream i) throws IOException {
|
||||
this.id = readUUID(i);
|
||||
this.name = readString(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
package nl.andrewl.concord_core.msg.types;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import nl.andrewl.concord_core.msg.Message;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
|
||||
import static nl.andrewl.concord_core.msg.MessageUtils.*;
|
||||
import static nl.andrewl.concord_core.msg.MessageUtils.readString;
|
||||
|
||||
/**
|
||||
* Standard set of user data that is used mainly as a component of other more
|
||||
* complex messages.
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class UserData implements Message {
|
||||
private UUID id;
|
||||
private String name;
|
||||
|
||||
@Override
|
||||
public int getByteCount() {
|
||||
return UUID_BYTES + getByteSize(this.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutputStream o) throws IOException {
|
||||
writeUUID(this.id, o);
|
||||
writeString(this.name, o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInputStream i) throws IOException {
|
||||
this.id = readUUID(i);
|
||||
this.name = readString(i);
|
||||
}
|
||||
}
|
|
@ -4,6 +4,9 @@ import lombok.Getter;
|
|||
import nl.andrewl.concord_core.msg.Message;
|
||||
import nl.andrewl.concord_core.msg.Serializer;
|
||||
import nl.andrewl.concord_core.msg.types.ChannelUsersResponse;
|
||||
import nl.andrewl.concord_core.msg.types.UserData;
|
||||
import org.dizitart.no2.IndexOptions;
|
||||
import org.dizitart.no2.IndexType;
|
||||
import org.dizitart.no2.NitriteCollection;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
@ -31,6 +34,29 @@ public class Channel {
|
|||
this.name = name;
|
||||
this.connectedClients = ConcurrentHashMap.newKeySet();
|
||||
this.messageCollection = messageCollection;
|
||||
this.initCollection();
|
||||
}
|
||||
|
||||
private void initCollection() {
|
||||
if (!this.messageCollection.hasIndex("timestamp")) {
|
||||
System.out.println("Adding index on \"timestamp\" field to collection " + this.messageCollection.getName());
|
||||
this.messageCollection.createIndex("timestamp", IndexOptions.indexOptions(IndexType.NonUnique));
|
||||
}
|
||||
if (!this.messageCollection.hasIndex("senderNickname")) {
|
||||
System.out.println("Adding index on \"senderNickname\" field to collection " + this.messageCollection.getName());
|
||||
this.messageCollection.createIndex("senderNickname", IndexOptions.indexOptions(IndexType.Fulltext));
|
||||
}
|
||||
if (!this.messageCollection.hasIndex("message")) {
|
||||
System.out.println("Adding index on \"message\" field to collection " + this.messageCollection.getName());
|
||||
this.messageCollection.createIndex("message", IndexOptions.indexOptions(IndexType.Fulltext));
|
||||
}
|
||||
var fields = List.of("timestamp", "senderNickname", "message");
|
||||
for (var index : this.messageCollection.listIndices()) {
|
||||
if (!fields.contains(index.getField())) {
|
||||
System.out.println("Dropping unknown index " + index.getField() + " from collection " + index.getCollectionName());
|
||||
this.messageCollection.dropIndex(index.getField());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addClient(ClientThread clientThread) {
|
||||
|
@ -66,12 +92,12 @@ public class Channel {
|
|||
}
|
||||
}
|
||||
|
||||
public List<ChannelUsersResponse.UserData> getUserData() {
|
||||
List<ChannelUsersResponse.UserData> users = new ArrayList<>();
|
||||
public List<UserData> getUserData() {
|
||||
List<UserData> users = new ArrayList<>(this.connectedClients.size());
|
||||
for (var clientThread : this.getConnectedClients()) {
|
||||
users.add(new ChannelUsersResponse.UserData(clientThread.getClientId(), clientThread.getClientNickname()));
|
||||
users.add(clientThread.toData());
|
||||
}
|
||||
users.sort(Comparator.comparing(ChannelUsersResponse.UserData::getName));
|
||||
users.sort(Comparator.comparing(UserData::getName));
|
||||
return users;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import lombok.Setter;
|
|||
import nl.andrewl.concord_core.msg.Message;
|
||||
import nl.andrewl.concord_core.msg.Serializer;
|
||||
import nl.andrewl.concord_core.msg.types.Identification;
|
||||
import nl.andrewl.concord_core.msg.types.UserData;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
|
@ -122,6 +123,10 @@ public class ClientThread extends Thread {
|
|||
return false;
|
||||
}
|
||||
|
||||
public UserData toData() {
|
||||
return new UserData(this.clientId, this.clientNickname);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.clientNickname + " (" + this.clientId + ")";
|
||||
|
|
|
@ -1,19 +1,24 @@
|
|||
package nl.andrewl.concord_server;
|
||||
|
||||
import lombok.Getter;
|
||||
import nl.andrewl.concord_core.msg.Message;
|
||||
import nl.andrewl.concord_core.msg.Serializer;
|
||||
import nl.andrewl.concord_core.msg.types.Identification;
|
||||
import nl.andrewl.concord_core.msg.types.ServerMetaData;
|
||||
import nl.andrewl.concord_core.msg.types.ServerWelcome;
|
||||
import nl.andrewl.concord_core.msg.types.UserData;
|
||||
import nl.andrewl.concord_server.cli.ServerCli;
|
||||
import nl.andrewl.concord_server.config.ServerConfig;
|
||||
import org.dizitart.no2.IndexOptions;
|
||||
import org.dizitart.no2.IndexType;
|
||||
import org.dizitart.no2.Nitrite;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
@ -52,33 +57,14 @@ public class ConcordServer implements Runnable {
|
|||
this.executorService = Executors.newCachedThreadPool();
|
||||
this.eventManager = new EventManager(this);
|
||||
this.channelManager = new ChannelManager(this);
|
||||
for (var channelConfig : config.channels()) {
|
||||
for (var channelConfig : config.getChannels()) {
|
||||
this.channelManager.addChannel(new Channel(
|
||||
this,
|
||||
UUID.fromString(channelConfig.id()),
|
||||
channelConfig.name(),
|
||||
this.db.getCollection("channel-" + channelConfig.id())
|
||||
UUID.fromString(channelConfig.getId()),
|
||||
channelConfig.getName(),
|
||||
this.db.getCollection("channel-" + channelConfig.getId())
|
||||
));
|
||||
}
|
||||
this.updateDatabase();
|
||||
}
|
||||
|
||||
private void updateDatabase() {
|
||||
for (var channel : this.channelManager.getChannels()) {
|
||||
var col = channel.getMessageCollection();
|
||||
if (!col.hasIndex("timestamp")) {
|
||||
System.out.println("Adding timestamp index to collection for channel " + channel.getName());
|
||||
col.createIndex("timestamp", IndexOptions.indexOptions(IndexType.NonUnique));
|
||||
}
|
||||
if (!col.hasIndex("senderNickname")) {
|
||||
System.out.println("Adding senderNickname index to collection for channel " + channel.getName());
|
||||
col.createIndex("senderNickname", IndexOptions.indexOptions(IndexType.Fulltext));
|
||||
}
|
||||
if (!col.hasIndex("message")) {
|
||||
System.out.println("Adding message index to collection for channel " + channel.getName());
|
||||
col.createIndex("message", IndexOptions.indexOptions(IndexType.Fulltext));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -96,17 +82,9 @@ public class ConcordServer implements Runnable {
|
|||
this.clients.put(id, clientThread);
|
||||
clientThread.setClientId(id);
|
||||
clientThread.setClientNickname(identification.getNickname());
|
||||
// Send a welcome reply containing all the initial server info the client needs.
|
||||
ServerMetaData metaData = new ServerMetaData(
|
||||
this.config.name(),
|
||||
this.channelManager.getChannels().stream()
|
||||
.map(channel -> new ServerMetaData.ChannelData(channel.getId(), channel.getName()))
|
||||
.sorted(Comparator.comparing(ServerMetaData.ChannelData::getName))
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
// Immediately add the client to the default channel and send the initial welcome message.
|
||||
var defaultChannel = this.channelManager.getChannelByName("general").orElseThrow();
|
||||
clientThread.sendToClient(new ServerWelcome(id, defaultChannel.getId(), metaData));
|
||||
clientThread.sendToClient(new ServerWelcome(id, defaultChannel.getId(), this.getMetaData()));
|
||||
// It is important that we send the welcome message first. The client expects this as the initial response to their identification message.
|
||||
defaultChannel.addClient(clientThread);
|
||||
clientThread.setCurrentChannel(defaultChannel);
|
||||
|
@ -127,14 +105,52 @@ public class ConcordServer implements Runnable {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
public List<UserData> getClients() {
|
||||
return this.clients.values().stream()
|
||||
.sorted(Comparator.comparing(ClientThread::getClientNickname))
|
||||
.map(ClientThread::toData)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public ServerMetaData getMetaData() {
|
||||
return new ServerMetaData(
|
||||
this.config.getName(),
|
||||
this.channelManager.getChannels().stream()
|
||||
.map(channel -> new ServerMetaData.ChannelData(channel.getId(), channel.getName()))
|
||||
.sorted(Comparator.comparing(ServerMetaData.ChannelData::getName))
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to every connected client.
|
||||
* @param message The message to send.
|
||||
*/
|
||||
public void broadcast(Message message) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(message.getByteCount());
|
||||
try {
|
||||
Serializer.writeMessage(message, baos);
|
||||
byte[] data = baos.toByteArray();
|
||||
for (var client : this.clients.values()) {
|
||||
client.sendToClient(data);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
this.running = true;
|
||||
ServerSocket serverSocket;
|
||||
try {
|
||||
serverSocket = new ServerSocket(this.config.port());
|
||||
serverSocket = new ServerSocket(this.config.getPort());
|
||||
StringBuilder startupMessage = new StringBuilder();
|
||||
startupMessage.append("Opened server on port ").append(config.port()).append("\n");
|
||||
startupMessage.append("Opened server on port ").append(config.getPort()).append("\n");
|
||||
for (var channel : this.channelManager.getChannels()) {
|
||||
startupMessage.append("\tChannel \"").append(channel).append('\n');
|
||||
}
|
||||
|
@ -151,6 +167,7 @@ public class ConcordServer implements Runnable {
|
|||
|
||||
public static void main(String[] args) {
|
||||
var server = new ConcordServer();
|
||||
server.run();
|
||||
new Thread(server).start();
|
||||
new ServerCli(server).run();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
package nl.andrewl.concord_server.cli;
|
||||
|
||||
import nl.andrewl.concord_server.ConcordServer;
|
||||
import nl.andrewl.concord_server.cli.command.AddChannelCommand;
|
||||
import nl.andrewl.concord_server.cli.command.ListClientsCommand;
|
||||
import nl.andrewl.concord_server.cli.command.RemoveChannelCommand;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ServerCli implements Runnable {
|
||||
private final ConcordServer server;
|
||||
private final Map<String, ServerCliCommand> commands;
|
||||
|
||||
public ServerCli(ConcordServer server) {
|
||||
this.server = server;
|
||||
this.commands = new HashMap<>();
|
||||
|
||||
this.commands.put("list-clients", new ListClientsCommand());
|
||||
this.commands.put("add-channel", new AddChannelCommand());
|
||||
this.commands.put("remove-channel", new RemoveChannelCommand());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
|
||||
String line;
|
||||
try {
|
||||
while (this.server.isRunning() && (line = reader.readLine()) != null) {
|
||||
if (!line.isBlank()) {
|
||||
String[] words = line.split("\\s+");
|
||||
String command = words[0];
|
||||
String[] args = Arrays.copyOfRange(words, 1, words.length);
|
||||
var cliCommand = this.commands.get(command.trim().toLowerCase());
|
||||
if (cliCommand != null) {
|
||||
try {
|
||||
cliCommand.handle(this.server, args);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
System.err.println("Unknown command.");
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package nl.andrewl.concord_server.cli;
|
||||
|
||||
import nl.andrewl.concord_server.ConcordServer;
|
||||
|
||||
public interface ServerCliCommand {
|
||||
void handle(ConcordServer server, String[] args) throws Exception;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package nl.andrewl.concord_server.cli.command;
|
||||
|
||||
import nl.andrewl.concord_server.Channel;
|
||||
import nl.andrewl.concord_server.ConcordServer;
|
||||
import nl.andrewl.concord_server.cli.ServerCliCommand;
|
||||
import nl.andrewl.concord_server.config.ServerConfig;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class AddChannelCommand implements ServerCliCommand {
|
||||
@Override
|
||||
public void handle(ConcordServer server, String[] args) throws Exception {
|
||||
if (args.length < 1) {
|
||||
System.err.println("Missing required name argument.");
|
||||
}
|
||||
String name = args[0].trim().toLowerCase().replaceAll("\\s+", "-");
|
||||
if (name.isBlank()) {
|
||||
System.err.println("Cannot create channel with blank name.");
|
||||
}
|
||||
if (server.getChannelManager().getChannelByName(name).isPresent()) {
|
||||
System.err.println("Channel with that name already exists.");
|
||||
}
|
||||
String description = null;
|
||||
if (args.length > 1) {
|
||||
description = args[1].trim();
|
||||
}
|
||||
UUID id = server.getIdProvider().newId();
|
||||
var channelConfig = new ServerConfig.ChannelConfig(id.toString(), name, description);
|
||||
server.getConfig().getChannels().add(channelConfig);
|
||||
server.getConfig().save();
|
||||
|
||||
var col = server.getDb().getCollection("channel-" + id);
|
||||
server.getChannelManager().addChannel(new Channel(server, id, name, col));
|
||||
server.broadcast(server.getMetaData());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package nl.andrewl.concord_server.cli.command;
|
||||
|
||||
import nl.andrewl.concord_server.ConcordServer;
|
||||
import nl.andrewl.concord_server.cli.ServerCliCommand;
|
||||
|
||||
public class ListClientsCommand implements ServerCliCommand {
|
||||
@Override
|
||||
public void handle(ConcordServer server, String[] args) throws Exception {
|
||||
var users = server.getClients();
|
||||
if (users.isEmpty()) {
|
||||
System.out.println("There are no connected clients.");
|
||||
} else {
|
||||
StringBuilder sb = new StringBuilder("Online Users:\n");
|
||||
for (var userData : users) {
|
||||
sb.append("\t").append(userData.getName()).append(" (").append(userData.getId()).append(")\n");
|
||||
}
|
||||
System.out.print(sb);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package nl.andrewl.concord_server.cli.command;
|
||||
|
||||
import nl.andrewl.concord_server.Channel;
|
||||
import nl.andrewl.concord_server.ConcordServer;
|
||||
import nl.andrewl.concord_server.cli.ServerCliCommand;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class RemoveChannelCommand implements ServerCliCommand {
|
||||
@Override
|
||||
public void handle(ConcordServer server, String[] args) throws Exception {
|
||||
if (args.length != 1) {
|
||||
System.err.println("Missing required channel name.");
|
||||
return;
|
||||
}
|
||||
String name = args[0].trim().toLowerCase();
|
||||
Optional<Channel> optionalChannel = server.getChannelManager().getChannelByName(name);
|
||||
if (optionalChannel.isEmpty()) {
|
||||
System.err.println("No channel with that name exists.");
|
||||
return;
|
||||
}
|
||||
Channel channelToRemove = optionalChannel.get();
|
||||
Channel alternative = null;
|
||||
for (var c : server.getChannelManager().getChannels()) {
|
||||
if (!c.equals(channelToRemove)) {
|
||||
alternative = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (alternative == null) {
|
||||
System.err.println("No alternative channel could be found. A server must always have at least one channel.");
|
||||
return;
|
||||
}
|
||||
for (var client : channelToRemove.getConnectedClients()) {
|
||||
server.getChannelManager().moveToChannel(client, alternative);
|
||||
}
|
||||
server.getChannelManager().removeChannel(channelToRemove);
|
||||
server.getDb().getContext().dropCollection(channelToRemove.getMessageCollection().getName());
|
||||
server.broadcast(server.getMetaData());
|
||||
}
|
||||
}
|
|
@ -1,31 +1,43 @@
|
|||
package nl.andrewl.concord_server.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.java.Log;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import nl.andrewl.concord_server.IdProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
public record ServerConfig(
|
||||
String name,
|
||||
int port,
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class ServerConfig {
|
||||
private String name;
|
||||
private int port;
|
||||
private int chatHistoryMaxCount;
|
||||
private int chatHistoryDefaultCount;
|
||||
private int maxMessageLength;
|
||||
private List<ChannelConfig> channels;
|
||||
|
||||
// Global Channel configuration
|
||||
int chatHistoryMaxCount,
|
||||
int chatHistoryDefaultCount,
|
||||
int maxMessageLength,
|
||||
/**
|
||||
* The path at which this config is stored.
|
||||
*/
|
||||
@JsonIgnore
|
||||
private transient Path filePath;
|
||||
|
||||
ChannelConfig[] channels
|
||||
) {
|
||||
|
||||
public static record ChannelConfig (
|
||||
String id,
|
||||
String name,
|
||||
String description
|
||||
) {}
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static final class ChannelConfig {
|
||||
private String id;
|
||||
private String name;
|
||||
private String description;
|
||||
}
|
||||
|
||||
public static ServerConfig loadOrCreate(Path filePath, IdProvider idProvider) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
|
@ -37,9 +49,8 @@ public record ServerConfig(
|
|||
100,
|
||||
50,
|
||||
8192,
|
||||
new ServerConfig.ChannelConfig[]{
|
||||
new ServerConfig.ChannelConfig(idProvider.newId().toString(), "general", "Default channel for general discussion.")
|
||||
}
|
||||
List.of(new ChannelConfig(idProvider.newId().toString(), "general", "Default channel for general discussion.")),
|
||||
filePath
|
||||
);
|
||||
try (var out = Files.newOutputStream(filePath)) {
|
||||
mapper.writerWithDefaultPrettyPrinter().writeValue(out, config);
|
||||
|
@ -50,6 +61,7 @@ public record ServerConfig(
|
|||
} else {
|
||||
try {
|
||||
config = mapper.readValue(Files.newInputStream(filePath), ServerConfig.class);
|
||||
config.setFilePath(filePath);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
|
@ -57,4 +69,11 @@ public record ServerConfig(
|
|||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
public void save() throws IOException {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
try (var out = Files.newOutputStream(filePath)) {
|
||||
mapper.writerWithDefaultPrettyPrinter().writeValue(out, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@ public class ChatHistoryRequestHandler implements MessageHandler<ChatHistoryRequ
|
|||
if (optionalChannel.isPresent()) {
|
||||
var channel = optionalChannel.get();
|
||||
var params = msg.getQueryAsMap();
|
||||
Long count = this.getOrDefault(params, "count", (long) server.getConfig().chatHistoryDefaultCount());
|
||||
if (count > server.getConfig().chatHistoryMaxCount()) {
|
||||
Long count = this.getOrDefault(params, "count", (long) server.getConfig().getChatHistoryDefaultCount());
|
||||
if (count > server.getConfig().getChatHistoryMaxCount()) {
|
||||
return;
|
||||
}
|
||||
Long from = this.getOrDefault(params, "from", null);
|
||||
|
|
Loading…
Reference in New Issue