package network;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import entities.Player;
import exceptions.MCConnectionException;
import packets.Dispatcher;
import packets.SPacket;
import packets.s2cpackets.S0x00;
import packets.s2cpackets.S0xFF;
/**
* Connection to a Minecraft server.
*
* Each connection can have a User set, but this is not always true.
* For example, during handshake the connection has no user.
*
* The class will call the dispatcher on incomming messages,
* and contains byte-send methods, to send bytes back to the client.
*
*/
public class MCConnection implements Runnable {
private Socket connection;
private Dispatcher dispatcher;
private ConnectionWriter w;
public ConnectionReader r;
/**
* When the user is authenticated, the value of the Player variable
* is set to the actual entity logged in.
*/
private Player player;
/**
* Initialize a new connection.
* @param connection
*/
public MCConnection(Socket connection, Dispatcher dispatcher) {
this.connection = connection;
this.dispatcher = dispatcher;
try {
r = new ConnectionReader(new DataInputStream(connection.getInputStream()));
w = new ConnectionWriter(new DataOutputStream(connection.getOutputStream()));
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Disconnect the current connection.
*
* Note that after disconnecting the MCConnection will
* also stop its thread.
*/
public void disconnect() {
try {
this.connection.close();
} catch (IOException e) {}
}
public void disconnect(String message) {
SPacket x = new S0xFF(message);
try {
this.send(x);
Thread.sleep(100);
}
catch (MCConnectionException e) {}
catch (InterruptedException e) {}
this.disconnect();
}
/**
* Main client-communication loop.
*/
@Override
public void run() {
try {
while(true) {
int msgcommand = r.read();
if(msgcommand == -1) {break;}
dispatcher.dispatch(msgcommand, this);
}
} catch (MCConnectionException e) {
System.out.println("Connection reset.");
}
dispatcher.dispatch(-1, this);
}
public synchronized void send(SPacket s) throws MCConnectionException {
this.w.send(s.getBytes());
}
/**
* Sets the player for this connection. This is issued after a succesful login.
* @param player
*/
public void setPlayer(Player player) {
//TODO: Assert that the player has not been set already
this.player = player;
//after a succesful login we can assume the player
// will be online for a while:
// we are going to start pinging the client a lot
// to ensure a nice connection!
new Thread(new PingThread(this)).start();
}
/**
* Returns the logged in player entity for this connection.
* @return The logged in player entity, or null if no player was logged in for this connection (yet).
*/
public Player getPlayer() {
return player;
}
}
/**
* Thread wich will ping a client for a undefined amount of time.
* Thread stops automagically and silently when a connection is
* closed or an error occurs.
*/
class PingThread implements Runnable {
private MCConnection conn;
public PingThread(MCConnection conn) {
this.conn = conn;
}
@Override
public void run() {
SPacket ping = new S0x00(1);
while(true) {
try {
Thread.sleep(25000);
conn.send(ping);
} catch (InterruptedException e) {
return;
} catch (MCConnectionException e) {
return;
}
}
}
}