package me.rkfg.ns2gather.server; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentHashMap; import me.rkfg.ns2gather.domain.Player; import me.rkfg.ns2gather.dto.HiveStatsDTO; import me.rkfg.ns2gather.dto.PlayerDTO; import me.rkfg.ns2gather.dto.Side; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpUriRequest; import org.hibernate.NonUniqueResultException; import org.hibernate.Session; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import ru.ppsrk.gwt.client.ClientAuthException; import ru.ppsrk.gwt.client.LogicException; import ru.ppsrk.gwt.server.HibernateCallback; import ru.ppsrk.gwt.server.HibernateUtil; import ru.ppsrk.gwt.server.LogicExceptionFormatted; import com.google.gwt.user.client.rpc.AsyncCallback; public class GatherPlayersManager implements AutoCloseable { public enum TeamStatType { EQUALITY, NOFREE } Timer playersCleanupTimer = new Timer("Players cleanup", true); ConcurrentHashMap<Long, GatherPlayers> gatherToPlayers = new ConcurrentHashMap<>(); ConcurrentHashMap<Long, PlayerDTO> steamIdPlayer = new ConcurrentHashMap<>(); ConcurrentHashMap<Long, String> kicked = new ConcurrentHashMap<>(); public void addKicked(Long steamId, String reason) { kicked.put(steamId, reason); } public String getKickedReason(Long steamId) { return kicked.get(steamId); } public void removeKickedId(Long steamId) { kicked.remove(steamId); } private CleanupCallback cleanupCallback; public interface CleanupCallback { public void playerRemoved(Long gatherId, Long steamId) throws LogicException, ClientAuthException; } public class GatherPlayers { ConcurrentHashMap<Long, PlayerDTO> gatherPlayers = new ConcurrentHashMap<>(); ConcurrentHashMap<Long, PlayerDTO> gatherParticipants = new ConcurrentHashMap<>(); List<Long> comms = new ArrayList<Long>(); public void putPlayer(PlayerDTO playerDTO) { gatherPlayers.put(playerDTO.getId(), playerDTO); } public PlayerDTO getPlayer(Long steamId) { return gatherPlayers.get(steamId); } public Collection<PlayerDTO> getPlayers() { return gatherPlayers.values(); } public int playerCount() { return gatherPlayers.size(); } public Long getCommId(int i) { return comms.get(i); } public void setComms(List<Long> comms) { this.comms = comms; } public void pickSide(final Long steamId, final Side side) throws LogicException, ClientAuthException { if (!getCommId(0).equals(steamId)) { throw new LogicException("Вам нельзя голосовать за сторону."); } Side commSide = side; for (int i = 0; i < 2; i++) { Long commId = getCommId(i); PlayerDTO comm = getParticipant(commId); if (comm == null) { throw LogicExceptionFormatted.format("Не найден командир в списке участников: %d", commId); } comm.setSide(commSide); commSide = commSide == Side.MARINES ? Side.ALIENS : Side.MARINES; } } public void pickPlayer(Long steamId, Long participantSteamId) throws LogicException { boolean equalPlayersInTeams = getTeamsStat(TeamStatType.EQUALITY); if (equalPlayersInTeams && !steamId.equals(comms.get(1)) || !equalPlayersInTeams && !steamId.equals(comms.get(0))) { throw new LogicException("Вы не можете сейчас выбрать игрока."); } PlayerDTO comm = getParticipant(steamId); if (comm == null) { throw LogicExceptionFormatted.format("Командир %d не найден среди участников.", steamId); } Side side = comm.getSide(); PlayerDTO participant = getParticipant(participantSteamId); if (participant == null) { throw LogicExceptionFormatted.format("Игрок %d не найден среди участников.", participantSteamId); } if (participant.getSide() == Side.MERC) { throw new LogicException("Вы не можете взять мерка в свою команду."); } participant.setLastPing(System.currentTimeMillis()); participant.setSide(side); } public boolean getTeamsStat(TeamStatType type) { int aliens = 0; int marines = 0; int free = 0; for (PlayerDTO playerDTO : gatherParticipants.values()) { switch (playerDTO.getSide()) { case ALIENS: aliens++; break; case MARINES: marines++; break; case NONE: free++; break; case MERC: // don't count merc(s) break; default: break; } } switch (type) { case EQUALITY: return aliens == marines; case NOFREE: return free == 0; default: return false; } } // clone players to fix them for the current gather public void playersToParticipants() { gatherParticipants = new ConcurrentHashMap<>(); PlayerDTO merc = null; boolean needMerc = false; Collection<PlayerDTO> players = getPlayers(); if (players.size() % 2 == 1) { needMerc = true; } for (PlayerDTO playerDTO : players) { PlayerDTO participant = playerDTO.clone(); if (needMerc) { if (merc == null) { merc = participant; } else { if (merc.getLoginTimestamp() < participant.getLoginTimestamp() && !comms.contains(participant.getId())) { merc = participant; } } } gatherParticipants.put(playerDTO.getId(), participant); } if (needMerc && merc != null) { merc.setSide(Side.MERC); } } public Collection<PlayerDTO> getParticipants() { return gatherParticipants.values(); } public PlayerDTO getParticipant(Long steamId) { return gatherParticipants.get(steamId); } public void remove(PlayerDTO player) { gatherPlayers.remove(player.getId()); } public synchronized void getHiveStats(final Long steamId, final AsyncCallback<HiveStatsDTO> callback) { new Thread(new Runnable() { @Override public void run() { PlayerDTO playerDTO = getPlayer(steamId); Long realSteamId = steamId & 0xFFFFFFFFL; HttpClient client = NetworkUtils.getHTTPClient(); HttpGet request = new HttpGet("http://hive.naturalselection2.com/api/get/playerData/" + realSteamId); try { String resultJSON = NetworkUtils.readStream(client.execute(request).getEntity().getContent()); JSONObject rootObject = new JSONObject(resultJSON); HiveStatsDTO hiveStatsDTO = playerDTO.getHiveStats(); if (hiveStatsDTO == null) { hiveStatsDTO = new HiveStatsDTO(); } hiveStatsDTO.setHoursPlayed(rootObject.getLong("playTime") / 3600); hiveStatsDTO.setSkill(rootObject.getDouble("skill")); playerDTO.setHiveStats(hiveStatsDTO); callback.onSuccess(hiveStatsDTO); } catch (IOException e) { callback.onFailure(e); } catch (JSONException e) { callback.onFailure(e); } } }, "Hive request for " + steamId).start(); } } public GatherPlayersManager(CleanupCallback cleanupCallback) { runPlayersCleanup(); this.cleanupCallback = cleanupCallback; } public void addPlayerToGather(Long gatherId, PlayerDTO playerDTO) { GatherPlayers gatherPlayers = getPlayersByGather(gatherId); playerDTO.setLoginTimestamp(System.currentTimeMillis()); gatherPlayers.putPlayer(playerDTO); addPlayerBySteamId(playerDTO); } public PlayerDTO getPlayerByGatherSteamId(Long gatherId, Long steamId) { return getPlayersByGather(gatherId).getPlayer(steamId); } public GatherPlayers getPlayersByGather(Long gatherId) { GatherPlayers result = gatherToPlayers.get(gatherId); if (result == null) { result = new GatherPlayers(); gatherToPlayers.put(gatherId, result); } return result; } public PlayerDTO getPlayerBySteamId(Long steamId) { return steamIdPlayer.get(steamId); } public void addPlayerBySteamId(PlayerDTO player) { steamIdPlayer.put(player.getId(), player); } public Set<Long> getGathers() { return gatherToPlayers.keySet(); } public PlayerDTO lookupPlayerBySteamId(Long steamId) throws LogicException, ClientAuthException { String name; HttpClient client = NetworkUtils.getHTTPClient(); HttpUriRequest request = new HttpGet(String.format( "http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=%s&steamids=%s", Settings.STEAM_API_KEY, steamId)); try { HttpResponse response = client.execute(request); JSONObject jsonRootObject = new JSONObject(NetworkUtils.readStream(response.getEntity().getContent())); JSONObject jsonResponse = jsonRootObject.optJSONObject("response"); if (jsonResponse == null) { throw new LogicException("no response"); } JSONArray jsonPlayers = jsonResponse.optJSONArray("players"); if (jsonPlayers == null || jsonPlayers.length() == 0) { throw new LogicException("no players"); } JSONObject jsonPlayer = jsonPlayers.optJSONObject(0); if (jsonPlayer == null) { throw new LogicException("no player"); } name = jsonPlayer.optString("personaname"); if (name == null) { throw new LogicException("no persona name"); } String profileUrl = jsonPlayer.optString("profileurl"); if (profileUrl == null) { throw new LogicException("no profile url"); } PlayerDTO player = new PlayerDTO(steamId, name, profileUrl, System.currentTimeMillis()); completeInfo(player); addPlayerBySteamId(player); return player; } catch (IOException | IllegalStateException e) { throw new LogicException("can't get player data"); } catch (JSONException e) { throw new LogicException("invalid player data"); } } private void completeInfo(final PlayerDTO playerDTO) throws LogicException, ClientAuthException { HibernateUtil.exec(new HibernateCallback<Void>() { @Override public Void run(Session session) throws LogicException, ClientAuthException { try { Player playerEnt = (Player) session.createQuery("from Player p where p.steamId = :sid") .setLong("sid", playerDTO.getId()).uniqueResult(); if (playerEnt != null) { playerDTO.setNick(playerEnt.getNick()); } } catch (NonUniqueResultException e) { throw LogicExceptionFormatted.format("Дубликация информации об игроке %d", playerDTO.getId()); } return null; } }); } private void runPlayersCleanup() { playersCleanupTimer.schedule(new TimerTask() { @Override public void run() { for (Long gatherId : getGathers()) { GatherPlayers gatherPlayers = getPlayersByGather(gatherId); for (PlayerDTO player : gatherPlayers.getPlayers()) { if (System.currentTimeMillis() - player.getLastPing() > Settings.PLAYER_PING_TIMEOUT) { try { cleanupCallback.playerRemoved(gatherId, player.getId()); } catch (LogicException | ClientAuthException e) { e.printStackTrace(); } } } } } }, 5000, 5000); } @Override public void close() throws Exception { playersCleanupTimer.cancel(); } public void removePlayerFromGather(Long gatherId, Long steamId) { getPlayersByGather(gatherId).remove(getPlayerBySteamId(steamId)); } }