Renderer/src/main/java/electrosphere/net/client/ClientNetworking.java
2024-07-13 16:00:48 -04:00

222 lines
7.8 KiB
Java

package electrosphere.net.client;
import electrosphere.engine.Globals;
import electrosphere.engine.Main;
import electrosphere.logger.LoggerInterface;
import electrosphere.net.client.protocol.ClientProtocol;
import electrosphere.net.parser.net.message.NetworkMessage;
import electrosphere.net.parser.net.message.ServerMessage;
import electrosphere.net.parser.net.message.NetworkMessage.MessageType;
import electrosphere.net.parser.net.message.ServerMessage.ServerMessageType;
import electrosphere.net.parser.net.raw.NetworkParser;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* Client networking thread
*/
public class ClientNetworking implements Runnable{
String address;
int port;
boolean local = false;
public Socket socket;
// CryptoInputStream inputStream;
// CryptoOutputStream outputStream;
InputStream inputStream;
OutputStream outputStream;
boolean initialized;
NetworkParser parser;
ClientProtocol clientProtocol = new ClientProtocol();
static final int MAX_CONNECTION_ATTEMPTS = 10;
//set to true to also get ping and pong messages in debug logging
boolean echoPings = false;
//thresholds for when to send pings and to determine when we've disconnected
static final long SEND_PING_THRESHOLD = 3000;
static final long PING_DISCONNECT_THRESHOLD = 20000;
//times for calculating ping-pong
long lastPingTime = 0;
long lastPongTime = 0;
//debugging stuff
String netMonitorHandle = null;
public ClientNetworking(String address, int port){
this.address = address;
this.port = port;
}
public ClientNetworking(InputStream clientInputStream, OutputStream clientOutputStream){
this.local = true;
this.inputStream = clientInputStream;
this.outputStream = clientOutputStream;
}
@Override
public void run(){
initialized = false;
// final SecretKeySpec key = new SecretKeySpec(("1234567890123456").getBytes(),"AES");
// final Properties properties = new Properties();
// final RSAKeyGenParameterSpec spec = new RSAKeyGenParameterSpec(4096, BigInteger.probablePrime(4000, new Random()));
// try {
// inputStream = new CryptoInputStream("AES/ECB/PKCS5Padding",properties,socket.getInputStream(),key,spec);
// } catch (IOException ex) {
// ex.printStackTrace();
// }
// try {
// outputStream = new CryptoOutputStream("AES/ECB/PKCS5Padding",properties,socket.getOutputStream(),key,spec);
// } catch (IOException ex) {
// ex.printStackTrace();
// }
//Used to copy messages from network parser to NetMonitor
List<NetworkMessage> netMonitorCache = new LinkedList<NetworkMessage>();
if(!this.local) {
//attempt connection
int connectionAttempts = 0;
boolean connected = false;
while(!connected){
try {
this.socket = new Socket(address,port);
connected = true;
} catch (IOException ex) {
LoggerInterface.loggerNetworking.WARNING("Client failed to connect!");
}
if(!connected){
try {
TimeUnit.MILLISECONDS.sleep(50);
} catch (InterruptedException e) {}
connectionAttempts++;
}
if(connectionAttempts > MAX_CONNECTION_ATTEMPTS){
LoggerInterface.loggerNetworking.ERROR("Max client connection attempts!", new Exception());
}
}
if(connected && Globals.netMonitor != null){
this.netMonitorHandle = Globals.netMonitor.registerConnection();
}
//grab input/output streams
try {
inputStream = socket.getInputStream();
outputStream = socket.getOutputStream();
} catch (IOException ex) {
LoggerInterface.loggerNetworking.ERROR("Error on client socket", ex);
}
}
//create parser
parser = new NetworkParser(inputStream,outputStream);
//start parsing messages
initialized = true;
while(Main.isRunning()){
//attempt poll incoming messages
parser.readMessagesIn();
//outgoing messages
parser.pushMessagesOut();
//ping logic
long currentTime = System.currentTimeMillis();
//basically if we haven't sent a ping in a while, send one
if(currentTime - lastPingTime > SEND_PING_THRESHOLD){
queueOutgoingMessage(ServerMessage.constructPingMessage());
lastPingTime = currentTime;
if(lastPongTime == 0){
lastPongTime = lastPingTime;
}
}
if(lastPingTime - lastPongTime > PING_DISCONNECT_THRESHOLD){
//disconnected from the server
LoggerInterface.loggerNetworking.WARNING("Disconnected from server");
//close socket
if(socket != null && socket.isConnected()){
try {
socket.close();
} catch (IOException e) {
LoggerInterface.loggerNetworking.ERROR("Error closing socket", e);
}
}
//TODO: kick us back to the main menu
break;
}
}
}
static final int MAX_MESSAGES_PARSED = 1000;
public void parseMessages(){
if(initialized){
while(parser.hasIncomingMessaage()){
NetworkMessage message = parser.popIncomingMessage();
//net monitor
if(Globals.netMonitor != null){
Globals.netMonitor.logMessage(netMonitorHandle, message, true);
}
//print network message
printMessage(message);
//do something
Globals.profiler.beginCpuSample("ClientProtocol.handleMessage");
clientProtocol.handleMessage(message);
Globals.profiler.endCpuSample();
}
}
}
/**
* Print out the network message type, this only prints ping and pong if echoPings is true
*/
void printMessage(NetworkMessage message){
//only print ping and pong if echoPings is true
if(message.getType() == MessageType.SERVER_MESSAGE){
if((((ServerMessage)message).getMessageSubtype()) == ServerMessageType.PING ||
(((ServerMessage)message).getMessageSubtype()) == ServerMessageType.PONG
){
if(this.echoPings == true){
LoggerInterface.loggerNetworking.DEBUG("[CLIENT] New message " + message.getType());
}
} else {
LoggerInterface.loggerNetworking.DEBUG("[CLIENT] New message " + message.getType());
}
} else {
LoggerInterface.loggerNetworking.DEBUG("[CLIENT] New message " + message.getType());
}
}
public void queueOutgoingMessage(NetworkMessage message){
//net monitor stuff
if(Globals.netMonitor != null){
Globals.netMonitor.logMessage(netMonitorHandle, message, false);
}
//actually queue
parser.addOutgoingMessage(message);
}
public ClientProtocol getClientProtocol(){
return clientProtocol;
}
public void markReceivedPongMessage(){
lastPongTime = System.currentTimeMillis();
}
}