package de.maxgb.minecraft.second_screen;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import org.java_websocket.WebSocket;
import org.json.JSONObject;
import de.maxgb.minecraft.second_screen.actions.ActionManager;
import de.maxgb.minecraft.second_screen.actions.ActionManager.ActionResultListener;
import de.maxgb.minecraft.second_screen.data.UserManager;
import de.maxgb.minecraft.second_screen.info_listener.ChatListener;
import de.maxgb.minecraft.second_screen.info_listener.PlayerInfoListener;
import de.maxgb.minecraft.second_screen.info_listener.PlayerInventoryListener;
import de.maxgb.minecraft.second_screen.info_listener.ServerInfoListener;
import de.maxgb.minecraft.second_screen.info_listener.WorldInfoListener;
import de.maxgb.minecraft.second_screen.shared.ClientVersion;
import de.maxgb.minecraft.second_screen.shared.PROTOKOLL;
import de.maxgb.minecraft.second_screen.util.Constants;
import de.maxgb.minecraft.second_screen.util.ForceUpdateEvent;
import de.maxgb.minecraft.second_screen.util.Logger;
import de.maxgb.minecraft.second_screen.util.User;
/**
* Handles the communication with a specific client
* @author Max
*
*/
public class WebSocketHandler implements ActionResultListener {
private WebSocket socket;
public final InetSocketAddress address;
private static String TAG = "WebSocketHandler-";
private ArrayList<StandardListener> listeners;
private User user;
/**
* Indicates if the Handler will be removed. Should only be modified by {@link #markForRemoval()}
*/
private boolean gettingRemoved;
public boolean isGettingRemoved() {
return gettingRemoved;
}
public WebSocketHandler(WebSocket socket) {
gettingRemoved=false;
address = socket.getRemoteSocketAddress();
this.socket = socket;
TAG += WebSocketListener.getNewHandlerID();
listeners = new ArrayList<StandardListener>();
}
public void close() {
for(StandardListener l:listeners){
l.onUnregister();
}
socket.close();
this.markForRemoval();
Logger.d(TAG, "Closing this listener "+address);
}
public void forceUpdate(ForceUpdateEvent e) {
for (StandardListener l : listeners) {
if (l.getClass().equals(e.listener)) {
send(l.tick(true));
}
}
}
private void onActionMessage(String action, String params) {
Logger.d(TAG, "Received action result message");
// Check is user object is available and so if the user is authentified
if (user == null) {
send(PROTOKOLL.ERROR + "-" + "Login required. [" + PROTOKOLL.ACTION_COMMAND_BEGIN + action + "-" + params
+ "]");
Logger.w(TAG, "Cannot execute action before login. ");
return;
}
JSONObject p = new JSONObject(params);
if (!ActionManager.doAction(action, p, user, this)) {
send(PROTOKOLL.ERROR + "-" + "Action not found. [" + PROTOKOLL.ACTION_COMMAND_BEGIN + action + "-" + params
+ "]");
}
}
@Override
public void onActionResult(String command, JSONObject r) {
send(PROTOKOLL.ACTION_RESULT_BEGIN + command + "-" + r.toString());
}
/**
* Handles the connect message
*/
private void onConnectMessage() {
Logger.d(TAG, "Received connect message");
JSONObject result = new JSONObject();
result.put("versionid", Constants.FEATURE_VERSION);
result.put("minecraftversion", Constants.MINECRAFT_VERSION);
result.put("login_required", Configs.auth_required);
send(PROTOKOLL.CONNECT_RESULT + "-" + result.toString());
}
/**
* Handles the disconnect message
*/
private void onDisconnectMessage() {
Logger.d(TAG, "Received disconnect message");
close();
}
/**
* Handles the login message
*
* @param params
* The parameter Json string
*/
private void onLoginMessage(String params) {
Logger.d(TAG, "Received login message");
JSONObject data = new JSONObject(params);
if (!data.has("username")) {
Logger.w(TAG, "Login message is missing username.");
JSONObject result = new JSONObject();
result.put("success", 0);
result.put("error", "Username is missing");
send(PROTOKOLL.LOGIN_RESULT + "-" + result.toString());
}
String username = data.getString("username");
if (Configs.auth_required) {
if (!data.has("password")) {
Logger.w(TAG, "Login message is missing password. It is set to required in the config options");
JSONObject result = new JSONObject();
result.put("success", 0);
result.put("error", "Password required");
send(PROTOKOLL.LOGIN_RESULT + "-" + result.toString());
return;
} else if (!UserManager.auth(username, data.getInt("password"))) {
Logger.w(TAG, "Authentification failed. Username or password is wrong");
JSONObject result = new JSONObject();
result.put("success", 0);
result.put("error", "Username or password wrong");
send(PROTOKOLL.LOGIN_RESULT + "-" + result.toString());
return;
}
}
user = UserManager.getUser(username);
JSONObject result = new JSONObject();
result.put("success", 1);
if (data.has("clientid") && data.has("clientversion")) {
String id = data.getString("clientid");
int v = data.getInt("clientversion");
Logger.i(TAG, "Clientinfo: " + id + " Version: " + v);
result.put("clientupdate", ClientVersion.isUpdateAvailable(id, v));
result.put("clientupdatenecessary", ClientVersion.isUpdateNecessary(id, v));
user.setClient(new ClientVersion.ClientInfo(id, v));
} else {
Logger.w(TAG, "Login message is missing client information.");
}
send(PROTOKOLL.LOGIN_RESULT + "-" + result.toString());
Logger.i(TAG, "Sucessfully logged in user " + username);
}
public void onMessage(String msg) {
try {
Logger.i(TAG, "Received Message: " + msg);// TODO Remove
if (msg.trim().startsWith(PROTOKOLL.REGISTER_COMMAND_BEGIN)) {
try {
String listener = msg.replace(PROTOKOLL.REGISTER_COMMAND_BEGIN, "").trim();
onRegisterMessage(listener);
} catch (Exception e) {
Logger.e(TAG, "Failed to parse listener from register command", e);
send(PROTOKOLL.ERROR + "-" + "Failed to register listener. [" + msg + "]");
}
} else if (msg.trim().startsWith(PROTOKOLL.UNREGISTER_COMMAND_BEGIN)) {
try {
String listener = msg.replace(PROTOKOLL.UNREGISTER_COMMAND_BEGIN, "").trim();
onUnregisterMessage(listener);
} catch (Exception e) {
Logger.e(TAG, "Failed to parse listener from unregister command", e);
send(PROTOKOLL.ERROR + "-" + "Failed to unregister listener. [" + msg + "]");
}
} else if (msg.trim().startsWith(PROTOKOLL.ACTION_COMMAND_BEGIN)) {
try {
String s = msg.replace(PROTOKOLL.ACTION_COMMAND_BEGIN, "");
String action = s.substring(0, s.indexOf('-'));
String params = s.substring(s.indexOf('-') + 1);
onActionMessage(action, params);
} catch (Exception e) {
Logger.e(TAG, "Failed processing action command", e);
send(PROTOKOLL.ERROR + "-" + "Failed processing action command. [" + msg + "]");
}
} else if (msg.trim().startsWith(PROTOKOLL.CONNECT)) {
onConnectMessage();
} else if (msg.trim().startsWith(PROTOKOLL.LOGIN)) {
String params = msg.substring(PROTOKOLL.LOGIN.length() + 1);
onLoginMessage(params);
}
else if (msg.trim().startsWith(PROTOKOLL.DISCONNECT)) {
onDisconnectMessage();
} else {
String m="";
for(int i=0;i<msg.length();i++){
m+="'"+msg.charAt(i)+"',";
}
Logger.d(TAG, "Did not find any matching command in the protokoll for "+m);
send(PROTOKOLL.UNKNOWN + " [" + msg + "]");
}
} catch (Exception e) {
Logger.e(TAG, "Failed to process message", e);
send(PROTOKOLL.ERROR + "-" + "Failed to process message. [" + msg + "]");
}
finally{
Logger.d(TAG, "Processed message");
}
}
/**
* Handles the register listener message
*
* @param l
* The string representation of the listener which should be
* registered
*/
private void onRegisterMessage(final String l) {
Logger.d(TAG, "Received register message");
// Check is user object is available and so if the user is authentified
if (user == null) {
send(PROTOKOLL.ERROR + "-" + "Login required. [" + PROTOKOLL.REGISTER_COMMAND_BEGIN + l + "]");
Logger.w(TAG, "Cannot register a listener before login.");
return;
}
if (l.startsWith(PROTOKOLL.S_PLAYERINFO_LISTENER)) {
listeners.add(new PlayerInfoListener(user));
} else if (l.startsWith(PROTOKOLL.PLAYER_INVENTORY_LISTENER)) {
listeners.add(new PlayerInventoryListener(user));
} else if (l.startsWith(PROTOKOLL.SERVER_INFO_LISTENER)) {
listeners.add(new ServerInfoListener(user));
} else if (l.startsWith(PROTOKOLL.WORLD_INFO_LISTENER)) {
listeners.add(new WorldInfoListener(user));
} else if (l.startsWith(PROTOKOLL.CHAT_LISTENER)) {
listeners.add(new ChatListener(user));
}
}
/**
* Handles the unregister listener messages
*
* @param l
* The string representation of the listener, which should be
* unregistered
*/
private void onUnregisterMessage(String l) {
Logger.d(TAG, "Received unregister message");
int listenercount = listeners.size();
if (l.startsWith(PROTOKOLL.S_PLAYERINFO_LISTENER)) {
for (int i = 0; i < listeners.size(); i++) {
StandardListener sl = listeners.get(i);
if (sl instanceof PlayerInfoListener) {
listeners.get(i).onUnregister();
listeners.remove(i);
}
}
} else if (l.startsWith(PROTOKOLL.PLAYER_INVENTORY_LISTENER)) {
for (int i = 0; i < listeners.size(); i++) {
StandardListener sl = listeners.get(i);
if (sl instanceof PlayerInventoryListener) {
listeners.get(i).onUnregister();
listeners.remove(i);
}
}
} else if (l.startsWith(PROTOKOLL.SERVER_INFO_LISTENER)) {
for (int i = 0; i < listeners.size(); i++) {
if (listeners.get(i) instanceof ServerInfoListener) {
listeners.get(i).onUnregister();
listeners.remove(i);
}
}
} else if (l.startsWith(PROTOKOLL.WORLD_INFO_LISTENER)) {
for (int i = 0; i < listeners.size(); i++) {
if (listeners.get(i) instanceof WorldInfoListener) {
listeners.get(i).onUnregister();
listeners.remove(i);
}
}
} else if (l.startsWith(PROTOKOLL.CHAT_LISTENER)) {
for (int i = 0; i < listeners.size(); i++) {
if (listeners.get(i) instanceof ChatListener) {
listeners.get(i).onUnregister();
listeners.remove(i);
break;
}
}
} else if (l.startsWith(PROTOKOLL.ALL_LISTENERS)) {
for(StandardListener li:listeners){
li.onUnregister();
}
listeners = new ArrayList<StandardListener>();
System.gc();
}
Logger.i(TAG, "Removed " + (listenercount - listeners.size()) + " listeners");
}
private void send(String msg) {
if (msg == null || isGettingRemoved()) {
return;
}
Logger.d(TAG, "Sending "+msg);
socket.send(msg);
// TODO make async test etc
}
public void tick() {
for (StandardListener l : listeners) {
send(l.tick(false));
}
}
/**
* Marks the method ready for removal
*/
public void markForRemoval(){
Logger.d(TAG, "Marking ready for removal");
this.gettingRemoved=true;
}
}