package org.gametrack.bf42.game; import java.net.*; import java.util.*; import org.gametrack.bf42.db.*; import org.gametrack.util.*; /** *
Title: BFTracker
*Description: Tracks online Battlefield 1942 games, outputs game records
*Copyright: Copyright (c) 2003
*Company: bf1942.gametrack.org
* @author Brian Cairns * @version 1.0 */ // keeps track of all running games public class GameManager { static int BF42 = 0; static int BFV = 1; static boolean dumpOnce = false; // debug public static int dupes = 0; // duplicate names static HashMap trackers = new HashMap(); // maps SocketAddresses to GameTrackers static HashMap idLookup = new HashMap(); // map server IDs to GameTrackers static HashMap[] playerIDs = { new HashMap(), new HashMap()}; // map of ints to PlayerID objects, which can contain any number of sessions static HashMap[] playerKeys = { new HashMap(), new HashMap()}; // map of key strings to PlayerID objects // static TreeSet[] rankedSessions = { new TreeSet(), new TreeSet() }; // private static TreeSet rankedSessions = new TreeSet( new PlayerRatingsComp() ); static final IntegerKey idKey = new IntegerKey( 0 ); public static int colAudit() { return trackers.size() + idLookup.size() + playerIDs[0].size() + playerIDs[1].size() + playerKeys[0].size() + playerKeys[1].size(); } public static String summary() { return "GameMan: Trackers=" + trackers.size() + ", sidLookup=" + idLookup.size() + ", pIDs[0]=" + playerIDs[0].size() + ", pIDs[1]=" + playerIDs[1].size() + ", pKeys[0]=" + playerKeys[0].size() + ", pKeys[1]=" + playerKeys[1].size(); } /** * Return a player ID for a particular key string * @param gameIndex * @param playerKey * @return */ public static PlayerIdentity getIdentity( int gameIndex, String playerKey ) { return( PlayerIdentity )playerKeys[gameIndex].get( playerKey ); } /** * Add this session to the GameManager pool of online players * @param session * @return */ public static PlayerIdentity addSession( PlayerSession session ) { PlayerIdentity identity = null; int gameIndex = DatabaseManager.getGameIndex( session.game ); // DBMan is 1 based for game index, want 0-based for an array index if( gameIndex < 0 ) Log.debug( 4, "GameMan.add(): GameIndex < 0! " + session.game ); else { /* if( playerIDs[gameIndex].containsKey( idKey.set( session.ID ) ) ) { // found in ID# lookup dupes++; identity = ( PlayerIdentity )playerIDs[gameIndex].get( idKey ); identity.addSession( session ); Log.log( "GameMan.addSession()[ID]: Duplicate name: '" + session + "' {" + session.keyhash + "} Instances: " + identity.count ); } else */ if( ( identity = ( PlayerIdentity )playerKeys[gameIndex].get( session.key ) ) != null ) { // found in key lookup dupes++; identity.addSession( session ); Log.log( "GameMan.addSession()[Key]: Duplicate name: '" + session + "' {" + session.keyhash + "} Instances: " + identity.count ); } else { // new identity identity = new PlayerIdentity( session ); playerKeys[gameIndex].put( session.key, identity ); } /** @todo was the problem with this the threshold between unranked and ranked? (a person becoming ranked while online) */ // if( session.isRanked() ) // synchronized( rankedSessions ) // { // rankedSessions[gameIndex].add( session ); // Log.debug( 6, "GameMan.addSes: ranked[" + rankedSessions[gameIndex].size() + "]: " + session.getID() ); // } } return identity; } public static void obtainedInfo( PlayerSession session ) { int gameIndex = DatabaseManager.getGameIndex( session.game ); if( gameIndex < 0 ) Log.err( "GameMan.obtainedID(" + session + "): Unknown game - " + session.game ); else { // verify that a key exists (otherwise they've gone offline) if( playerKeys[gameIndex].containsKey( session.key ) ) { // create ID# lookup playerIDs[gameIndex].put( new IntegerKey( session.getInfo().ID ), session.playerIdentity ); } else Log.log( "GameMan.obtainedInfo - Online player key not found for session: " + session ); } } public static void removeSession( PlayerSession session ) { int gameIndex = DatabaseManager.getGameIndex( session.game ); if( gameIndex < 0 ) Log.debug( 4, "GameID < 0. GameID: " + session.game ); else if( gameIndex > 1 ) Log.err( "GameID > 1!!! GameID=" + session.game ); else { /* if( session.isRanked() ) synchronized( rankedSessions ) { if( rankedSessions[gameIndex].remove( session ) ) { Log.debug( 6, "GameMan: removed rankedSessions[" + rankedSessions[gameIndex].size() + "] - " + session.getID() ); } else { Log.err( "GameManager.removeSession() - Could not find session in rankedSessions[" + rankedSessions[gameIndex].size() + "], scanning! (Memory leak?) " + session.getID() ); PlayerSession s2; // do a manual scan for( Iterator iter = rankedSessions[gameIndex].iterator(); iter.hasNext(); ) { s2 = ( PlayerSession )iter.next(); if( dumpOnce ) Log.log( s2.toString() ); if( s2 != null ) { if( session.name.equals( s2.name ) ) { Log.err( "Found name match, removing: " + s2 ); iter.remove(); } } } if( dumpOnce ) dumpOnce = false; } } //*/ PlayerIdentity id = ( PlayerIdentity )playerKeys[gameIndex].get( session.key ); if( id == null ) { Log.err( "GameMan.removeSession(): No PlayerID found for: '" + session + "'" ); // Debug.backtrace( 2 ); } else { id.removeSession( session ); if( id.count > 0 ) { Log.log( "Dupe '" + session + "' offline, looking for replacement. count=" + id.count + ", multiple.size()=" + id.multiple.size() ); dupes--; ArrayList listTemp = new ArrayList(); Iterator iter = id.multiple.values().iterator(); if( iter.hasNext() ) { PlayerSession replacement = ( PlayerSession )iter.next(); listTemp.add( replacement ); DatabaseManager.queuePlayersOnline( listTemp ); Log.log( "Found dupe replacement, setting " + replacement + " online" ); } else { Log.err( "Could not find dupe replacement for " + session + "!" ); playerIDs[gameIndex].remove( idKey.set( session.ID ) ); playerKeys[gameIndex].remove( session.key ); } } else { playerIDs[gameIndex].remove( idKey.set( session.ID ) ); playerKeys[gameIndex].remove( session.key ); } } } } public static PlayerSession getPlayerSession( int gameIndex, int pid ) { PlayerIdentity id = ( PlayerIdentity )playerIDs[gameIndex].get( idKey.set( pid ) ); if( id == null ) return null; // no ID online for this pid return id.getOne(); } public static List getTopOnlinePlayers( int gameIndex, int count, int skip ) { /* ArrayList list = new ArrayList( count ); synchronized( rankedSessions ) { if( count > 100 ) count = 100; for( Iterator iter = rankedSessions[gameIndex].iterator(); iter.hasNext() && count > 0; ) { if( skip > 0 ) { iter.next(); skip--; } else { list.add( iter.next() ); count--; } } } return list; //*/ return null; } /** * This is expensive, don't use often! * @return total number of players */ public static int getTotalPlayerCount() { int count = 0; for( Iterator iter = trackers.values().iterator(); iter.hasNext(); ) { count += ( ( GameTracker )iter.next() ).getNumPlayers(); } return count; } public static GameTracker[] getAllTrackers() { GameTracker[] array = new GameTracker[trackers.size()]; trackers.values().toArray( array ); return array; } /** * Tracker is shutting down, inform all trackers to close sessions */ public static void trackerShutdown() { for( Iterator iter = trackers.values().iterator(); iter.hasNext(); ) { ( ( GameTracker )iter.next() ).trackerShutdown(); } trackers.clear(); } public static int getServers() { return trackers.size(); } public static GameTracker getTracker( int id ) { return( GameTracker )idLookup.get( idKey.set( id ) ); } public static GameTracker addServer( Server server ) { GameTracker tracker = new GameTracker( server ); trackers.put( server.address, tracker ); idLookup.put( idKey.set( server.serverID ), tracker ); return tracker; } /** * Get the current gametracker object asssociated with this socket * (Inlined code instead of calling getTracker() for slight speed boost) * @param sa the socket address * @return the current GameState */ public static GameTracker getGame( SocketAddress sa ) { return( GameTracker )trackers.get( sa ); } public static void notResponding( SocketAddress sa ) { GameTracker tracker = getGame( sa ); if( tracker != null ) { tracker.serverNotResponding(); } } // comparator for players, by rating public static class PlayerRatingsComp implements Comparator { public int compare( Object o1, Object o2 ) { PlayerSession s1 = ( PlayerSession )o1; PlayerSession s2 = ( PlayerSession )o2; if( s2 == null ) return -1; if( s1 == null ) return 1; if( s1.equals( s2 ) ) return 0; if( s1.isRanked() ) { if( !s2.isRanked() ) return -1; // both are ranked return( int ) ( s2.getInfo().rating - s1.getInfo().rating ); } else { if( s2.isRanked() ) return 1; // neither is ranked return 0; } } } }