package org.ggp.base.util.presence; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.Charset; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.TreeSet; import org.ggp.base.util.observer.Event; import org.ggp.base.util.observer.Observer; import org.ggp.base.util.observer.Subject; import external.JSON.JSONArray; import external.JSON.JSONException; import external.JSON.JSONObject; public class PlayerPresenceManager implements Subject { private Map<String,PlayerPresence> monitoredPlayers; public class PlayerPresenceChanged extends Event {} public class PlayerPresenceAdded extends Event {} public class PlayerPresenceRemoved extends Event {} public static boolean isDifferent(String a, String b) { return !Objects.equals(a, b); } public static final int INFO_PING_PERIOD_IN_SECONDS = 1; class PresenceMonitor extends Thread { @Override public void run() { while(true) { try { Thread.sleep(INFO_PING_PERIOD_IN_SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } Set<String> keys = new HashSet<String>(monitoredPlayers.keySet()); for (String key : keys) { PlayerPresence presence = monitoredPlayers.get(key); if (presence == null) continue; if (presence.getStatusAge() > INFO_PING_PERIOD_IN_SECONDS*1000) { String old_name = presence.getName(); String old_status = presence.getStatus(); presence.updateInfo(); String new_name = presence.getName(); String new_status = presence.getStatus(); if (isDifferent(old_status, new_status)) { notifyObservers(new PlayerPresenceChanged()); } else if (isDifferent(old_name, new_name)) { notifyObservers(new PlayerPresenceChanged()); } } } } } } public PlayerPresenceManager() { monitoredPlayers = new HashMap<String,PlayerPresence>(); loadPlayersJSON(); if (monitoredPlayers.isEmpty()) { try { // When starting from a blank slate, add some initial players to the // monitoring list just so that it's clear how it works. addPlayer("127.0.0.1:9147"); addPlayer("127.0.0.1:9148"); } catch (InvalidHostportException e) { ; } } new PresenceMonitor().start(); } @SuppressWarnings("serial") public class InvalidHostportException extends Exception {} private PlayerPresence addPlayerSilently(String hostport) throws InvalidHostportException { try { if (!monitoredPlayers.containsKey(hostport)) { String host = hostport.split(":")[0]; int port = Integer.parseInt(hostport.split(":")[1]); PlayerPresence presence = new PlayerPresence(host, port); monitoredPlayers.put(hostport, presence); return presence; } else { return monitoredPlayers.get(hostport); } } catch (ArrayIndexOutOfBoundsException e) { throw new InvalidHostportException(); } catch (NumberFormatException e) { throw new InvalidHostportException(); } } public PlayerPresence addPlayer(String hostport) throws InvalidHostportException { PlayerPresence presence = addPlayerSilently(hostport); notifyObservers(new PlayerPresenceAdded()); savePlayersJSON(); return presence; } public void removePlayer(String hostport) { monitoredPlayers.remove(hostport); notifyObservers(new PlayerPresenceRemoved()); savePlayersJSON(); } public PlayerPresence getPresence(String hostport) { return monitoredPlayers.get(hostport); } public Set<String> getSortedPlayerNames() { return new TreeSet<String>(monitoredPlayers.keySet()); } private Set<Observer> observers = new HashSet<Observer>(); @Override public void addObserver(Observer observer) { observers.add(observer); } @Override public void notifyObservers(Event event) { for (Observer observer : observers) { observer.observe(event); } } private static final String playerListFilename = ".ggpserver-playerlist.json"; private void savePlayersJSON() { try { JSONObject playerListJSON = new JSONObject(); playerListJSON.put("hostports", monitoredPlayers.keySet()); File file = new File(System.getProperty("user.home"), playerListFilename); if (!file.exists()) { file.createNewFile(); } FileWriter fw = new FileWriter(file); BufferedWriter bw = new BufferedWriter(fw); bw.write(playerListJSON.toString()); bw.close(); } catch (IOException ie) { ie.printStackTrace(); } catch (JSONException e) { e.printStackTrace(); } } private void loadPlayersJSON() { try { String line; StringBuilder pdata = new StringBuilder(); File file = new File(System.getProperty("user.home"), playerListFilename); if (!file.exists()) { return; } BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8"))); try { while ((line = br.readLine()) != null) { pdata.append(line); } } finally { br.close(); } JSONObject playerListJSON = new JSONObject(pdata.toString()); if (playerListJSON.has("hostports")) { JSONArray theHostports = playerListJSON.getJSONArray("hostports"); for (int i = 0; i < theHostports.length(); i++) { try { addPlayerSilently(theHostports.get(i).toString()); } catch (InvalidHostportException e) { e.printStackTrace(); } } } } catch (IOException ie) { ie.printStackTrace(); } catch (JSONException e) { e.printStackTrace(); } } }