233 lines
6.2 KiB
Java

package dev.wiing.gossip.client;
import dev.wiing.gossip.lib.PacketHandler;
import dev.wiing.gossip.lib.PacketManager;
import dev.wiing.gossip.lib.models.SecretUser;
import dev.wiing.gossip.lib.models.User;
import dev.wiing.gossip.lib.packets.*;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class Connection {
private static final Connection instance = new Connection();
private final PacketManager packetManager = new PacketManager();
private final PacketHandler packetHandler = new PacketHandler();
private Thread handlerThread;
private Socket socket;
private SecretUser self;
private final List<Packet> queuedPackets = Collections.synchronizedList(new LinkedList<>());
private Connection() {
packetManager.registerPackets();
}
public static Connection getInstance() {
return instance;
}
public PacketManager getPacketManager() {
return packetManager;
}
public PacketHandler getPacketHandler() {
return packetHandler;
}
public static class PacketHandlerRunnable implements Runnable {
private final Connection connection;
public PacketHandlerRunnable(Connection connection) {
this.connection = connection;
}
@Override
public void run() {
while (true) {
try {
Packet packet;
if (connection.getSocket().getInputStream().available() > 0 || connection.queuedPackets.isEmpty()) {
packet = connection.nextPacket(false);
} else {
packet = connection.findPacketOfTypes(connection.packetHandler.getListeningTypes());
}
if (packet == null) continue;
if (!connection.getPacketHandler().runPacket(packet)) {
connection.queuedPackets.add(packet);
}
} catch (SocketException e) {
break;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public void beginHandlingPackets() {
handlerThread = new Thread(new PacketHandlerRunnable(this));
handlerThread.start();
}
public Socket getSocket() {
return socket;
}
public void setSocket(Socket socket) {
this.socket = socket;
}
public void sendPacket(Packet packet) {
try {
getPacketManager().writePacket(getSocket().getOutputStream(), packet);
} catch (IOException e) {
e.printStackTrace();
}
}
public void sendPacketAuthenticated(AuthRequiredPacket packet) {
packet.setAuth(getSelf().getUserSecret());
sendPacket(packet);
}
public Packet nextPacket(boolean useQueue) throws SocketException {
if (useQueue && !queuedPackets.isEmpty()) {
return queuedPackets.remove(0);
}
try {
return getPacketManager().readPacket(getSocket().getInputStream());
} catch (SocketException e) {
throw e;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
public Packet nextPacket() throws SocketException {
return nextPacket(true);
}
public Packet nextPacket(short packetType) throws SocketException {
while (true) {
Packet packet = nextPacket(false);
if (packet.getType() != packetType) {
queuedPackets.add(packet);
continue;
}
return packet;
}
}
public Packet findPacketOfTypes(List<Short> types) {
Packet packet = null;
int i;
for (i = 0; i < queuedPackets.size(); i++) {
Packet queuedPacket = queuedPackets.get(i);
if (types.contains(queuedPacket.getType())) {
packet = queuedPacket;
break;
}
}
if (packet != null) {
queuedPackets.remove(i);
return packet;
}
return null;
}
public boolean findAck(short acknowledgement) throws IOException {
while (true) {
while (socket.getInputStream().available() > 0);
AckPacket ack = null;
int i;
for (i = 0; i < queuedPackets.size(); i++) {
Packet queuedPacket = queuedPackets.get(i);
if (queuedPacket.getType() == AckPacket.TYPE) {
ack = (AckPacket) queuedPacket;
if (ack.getAcknowledgement() == acknowledgement) {
break;
}
ack = null;
}
}
if (ack != null) {
queuedPackets.remove(i);
return true;
}
}
}
public SecretUser getSelf() {
return self;
}
public void setSelf(SecretUser self) {
this.self = self;
userCache.put(self.getUserID(), new User(
self.getUsername(),
self.getAvatarID(),
self.getUserID()
));
}
private final Map<Long, User> userCache = new ConcurrentHashMap<>();
public User getUser(long userID) {
User result;
if ((result = userCache.getOrDefault(userID, null)) != null) {
return result;
}
UserFetchPacket fetch = new UserFetchPacket();
fetch.setUserID(userID);
Connection.getInstance().sendPacket(fetch);
Packet resp;
try {
resp = Connection.getInstance().nextPacket(UserDataPacket.TYPE);
} catch (SocketException e) {
throw new RuntimeException(e);
}
if (resp.getType() == UserDataPacket.TYPE) {
UserDataPacket userData = (UserDataPacket)resp;
result = new User(
userData.getUsername(),
userData.getAvatarID(),
userData.getUserID()
);
userCache.put(result.getUserID(), result);
}
return result;
}
}