public class IdFactory { private static Logger _log = Logger.getLogger(IdFactory.class.getName()); private static final int STACK_SIZE_INCREMENT = 8196; private AtomicInteger freeIdCount; private AtomicInteger nextFreeId; private static int newFreeID; private static String[] id_updates = { "UPDATE items SET owner_id = ? WHERE owner_id = ?", "UPDATE items SET object_id = ? WHERE object_id = ?", "UPDATE character_quests SET char_id = ? WHERE char_id = ?", "UPDATE character_recipebook SET char_id = ? WHERE char_id = ?", "UPDATE character_shortcuts SET char_obj_id = ? WHERE char_obj_id = ?", "UPDATE character_shortcuts SET shortcut_id = ? WHERE shortcut_id = ? AND type = 1", // items "UPDATE character_macroses SET char_obj_id = ? WHERE char_obj_id = ?", "UPDATE character_skills SET char_obj_id = ? WHERE char_obj_id = ?", "UPDATE characters SET obj_Id = ? WHERE obj_Id = ?", "UPDATE characters SET clanid = ? WHERE clanid = ?", "UPDATE clan_data SET clan_id = ? WHERE clan_id = ?", "UPDATE clan_data SET ally_id = ? WHERE ally_id = ?", "UPDATE clan_data SET leader_id = ? WHERE leader_id = ?", "UPDATE pets SET item_obj_id = ? WHERE item_obj_id = ?", }; private static String[] id_checks = { "SELECT object_id FROM items WHERE object_id >= ? AND object_id < ?", "SELECT owner_id FROM items WHERE object_id >= ? AND object_id < ?", "SELECT char_id FROM character_quests WHERE char_id >= ? AND char_id < ?", "SELECT char_id FROM character_recipebook WHERE char_id >= ? AND char_id < ?", "SELECT char_obj_id FROM character_shortcuts WHERE char_obj_id >= ? AND char_obj_id < ?", "SELECT char_obj_id FROM character_macroses WHERE char_obj_id >= ? AND char_obj_id < ?", "SELECT char_obj_id FROM character_skills WHERE char_obj_id >= ? AND char_obj_id < ?", "SELECT obj_Id FROM characters WHERE obj_Id >= ? AND obj_Id < ?", "SELECT clanid FROM characters WHERE clanid >= ? AND clanid < ?", "SELECT clan_id FROM clan_data WHERE clan_id >= ? AND clan_id < ?", "SELECT ally_id FROM clan_data WHERE ally_id >= ? AND ally_id < ?", "SELECT leader_id FROM clan_data WHERE leader_id >= ? AND leader_id < ?", "SELECT item_obj_id FROM pets WHERE item_obj_id >= ? AND item_obj_id < ?", }; private int _curOID; private int _tempOID; private int[] _freeOIDs; private int _freeSize; private Stack _freeOIDStack = new Stack(); private boolean initialized; private static int FIRST_OID = 0x10000000; private static int LAST_OID = 0x7FFFFFFF; private static IdFactory _instance = new IdFactory(); private IdFactory() { if (Config.COMPACTION) { _curOID = FIRST_OID; _tempOID = FIRST_OID; _freeOIDs = new int[STACK_SIZE_INCREMENT]; _freeSize = 0; java.sql.Connection con = null; try { con = L2DatabaseFactory.getInstance().getConnection(); //con.createStatement().execute("drop table if exists tmp_obj_id"); //create a temporary table Statement s = con.createStatement(); try { s.executeUpdate("drop table temporaryObjectTable"); } catch (SQLException e) { // TODO Auto-generated catch block //e.printStackTrace(); } s.executeUpdate("create table temporaryObjectTable" + " (object_id int NOT NULL PRIMARY KEY)"); s.executeUpdate("insert into temporaryObjectTable (object_id)" + " select obj_id from characters"); s.executeUpdate("insert into temporaryObjectTable (object_id)" + " select object_id from items"); s.executeUpdate("insert into temporaryObjectTable (object_id)" + " select clan_id from clan_data"); ResultSet result = s.executeQuery("select count(object_id) from temporaryObjectTable ORDER BY object_id"); result.next(); int size = result.getInt(1); int[] tmp_obj_ids = new int[size]; // System.out.println("tmp table size: " + tmp_obj_ids.length); result.close(); result = s.executeQuery("select object_id from temporaryObjectTable ORDER BY object_id"); int idx = 0; while (result.next()) { tmp_obj_ids[idx++] = result.getInt(1); } _curOID = tmp_obj_ids[size-1]; System.out.println("Max Id = " + _curOID); result.close(); s.close(); int N = tmp_obj_ids.length; for (idx = 0; idx < N; idx++) { //long time1 = System.currentTimeMillis(); N = insertUntil(tmp_obj_ids, idx, N, con); // if (System.currentTimeMillis() - time1 > 0) // System.out.println("T1: " + (System.currentTimeMillis() - time1)); } _curOID++; _log.config("IdFactory: Next usable Object ID is: " + _curOID); initialized = true; } catch (Exception e1) { e1.printStackTrace(); _log.severe("ID Factory could not be initialized correctly:" + e1); } finally { try { con.close(); } catch (Exception e) {} } } else { initialize(); _log.info("IDFactory: "+ freeIds.size() + " id's avaliable."); } //setFreeIds(); // _log.info("IDFactory: "+ freeIds.size() + " id's avaliable."); java.sql.Connection con2 = null; try { con2 = L2DatabaseFactory.getInstance().getConnection(); Statement s2 = con2.createStatement(); s2.executeUpdate("update characters set online=0"); _log.info("Updated characters online status."); } catch (SQLException e) { } finally { try { con2.close(); } catch (Exception e) {} } } private IdFactory(int i) {} /** * @param con * @return * @throws SQLException */ private int[] extractUsedObjectIDTable(java.sql.Connection con) throws SQLException { // create a temporary table Statement s = con.createStatement(); s.executeUpdate("drop table if exists tmp_obj_id"); s.executeUpdate("create table tmp_obj_id (object_id int NOT NULL PRIMARY KEY)"); s.executeUpdate("insert into tmp_obj_id (object_id) select obj_id from characters"); s.executeUpdate("insert into tmp_obj_id (object_id) select clan_id from clan_data"); s.executeUpdate("update items i left join tmp_obj_id t on (i.object_id = t.object_id) set i.object_id = i.object_id + 200000000 where i.object_id IS NOT NULL AND t.object_id IS NOT NULL;"); s.executeUpdate("insert into tmp_obj_id (object_id) select object_id from items"); ResultSet result = s.executeQuery("select count(object_id) from tmp_obj_id ORDER BY object_id"); result.next(); int[] tmp_obj_ids = new int[result.getInt(1)]; // System.out.println("tmp table size: " + tmp_obj_ids.length); result.close(); result = s.executeQuery("select object_id from tmp_obj_id ORDER BY object_id"); int idx = 0; while (result.next()) { tmp_obj_ids[idx++] = result.getInt(1); } result.close(); s.close(); return tmp_obj_ids; } public static void main(String[] args) { System.out.println("Commencing Database Reindexing."); new IdFactory(1).reindex(); System.out.println("Finished Reindexing Database."); } public void reindex() { newFreeID = FIRST_OID; java.sql.Connection con = null; try { con = L2DatabaseFactory.getInstance().getConnection(); // create a temporary table int[] objectIds = extractUsedObjectIDTable(con); ArrayList objectIDList = new ArrayList(); for (Integer oId: objectIds) { objectIDList.add(oId); } reindexCharacterIds(con, objectIDList); reindexPetItemIds(con, objectIDList); } catch (Exception e1) { _log.severe("ID Factory could not be initialized correctly (2):" + e1); e1.printStackTrace(); } finally { try { con.close(); } catch (Exception e) {} } } /** * @param con * @param objectIDList */ private static void reindexPetItemIds(Connection con, ArrayList objectIDList) throws SQLException { System.out.println("Reindexing Pet Items."); Statement s = con.createStatement(); ResultSet result = s.executeQuery("select count(item_obj_id) from pets"); result.next(); int[] characterIds = new int[result.getInt(1)]; result.close(); result = s.executeQuery("select item_obj_id from pets ORDER BY item_obj_id"); int idx = 0; while (result.next()) { characterIds[idx++] = result.getInt(1); } result.close(); int[] chars = {1,13}; for (Integer oldOID : characterIds) { //System.out.println("Reindexing Character ID: "+oldOID+"."); for (Integer updateId : chars) { String update = id_updates[updateId]; newFreeID = getNextUnusedID(objectIDList, newFreeID); PreparedStatement ps = con.prepareStatement(update); ps.setInt(1, oldOID); ps.setInt(2, newFreeID); ps.execute(); ps.close(); objectIDList.remove(oldOID); } System.out.println("Reindexed Pet Item from Old ID: "+oldOID+"\tto New ID: "+newFreeID+"."); } System.out.println("Finished Reindexing Pet Items."); } /** * @param newFreeID * @param con * @param objectIds * @throws SQLException */ private static void reindexCharacterIds(java.sql.Connection con, ArrayList objectIDList) throws SQLException { System.out.println("Reindexing Characters."); Statement s = con.createStatement(); ResultSet result = s.executeQuery("select count(obj_id) from characters ORDER BY obj_id"); result.next(); int[] characterIds = new int[result.getInt(1)]; result.close(); result = s.executeQuery("select obj_id from characters ORDER BY obj_id"); int idx = 0; while (result.next()) { characterIds[idx++] = result.getInt(1); } result.close(); int[] chars = {0,2,3,4,6,7,8,12}; for (Integer oldOID : characterIds) { //System.out.println("Reindexing Character ID: "+oldOID+"."); for (Integer updateId : chars) { String update = id_updates[updateId]; newFreeID = getNextUnusedID(objectIDList, newFreeID); PreparedStatement ps = con.prepareStatement(update); ps.setInt(1, oldOID); ps.setInt(2, newFreeID); ps.execute(); ps.close(); objectIDList.remove(oldOID); } System.out.println("Reindexed Character from Old ID: "+oldOID+"\tto New ID: "+newFreeID+"."); } System.out.println("Finished Reindexing Characters."); } private static int getNextUnusedID(ArrayList objectIDList, int startIndex) { int id = startIndex+1; while (id <= LAST_OID) { if (!objectIDList.contains(id)) { return id; } id++; } return -1; } public boolean isInitialized() { return initialized; } private int insertUntil(int[] tmp_obj_ids, int idx, int N, java.sql.Connection con) throws SQLException { int id = tmp_obj_ids[idx]; if (id == _tempOID) { _tempOID++; return N; } /*if (id == _curOID) { _curOID++; return N; }*/ // check these IDs not present in DB for (String check : id_checks) { PreparedStatement ps = con.prepareStatement(check); ps.setInt(1, _tempOID); //ps.setInt(1, _curOID); ps.setInt(2, id); ResultSet rs = ps.executeQuery(); while (rs.next()) { int badId = rs.getInt(1); _log.severe("Bad ID " + badId + " in DB found by: " + check); throw new RuntimeException(); } rs.close(); ps.close(); } //int hole = id - _curOID; int hole = id - _tempOID; if (hole > N - idx) hole = N - idx; for (int i = 1; i <= hole; i++) { //long time1 = System.currentTimeMillis(); id = tmp_obj_ids[N - i]; //System.out.println("Compacting DB object ID=" + id + " into " + (_curOID)); /* for (String update : id_updates) { PreparedStatement ps = con.prepareStatement(update); ps.setInt(1, _tempOID); //ps.setInt(1, _curOID); ps.setInt(2, id); ps.execute(); ps.close(); }*/ System.out.println("Free ID added " + (_tempOID)); _freeOIDStack.push(_tempOID); _tempOID++; //_curOID++; // System.out.println("T2: " + (System.currentTimeMillis() - time1)); } //if (hole < N - idx) _curOID++; if (hole < N - idx) _tempOID++; // return N - hole; return N - hole; } public static IdFactory getInstance() { return _instance; } public synchronized int getNextId() { /*if (Config.COMPACTION) { if (_freeSize == 0) return _curOID++; else return _freeOIDs[--_freeSize]; } else {*/ int id; if (!_freeOIDStack.empty()) id = _freeOIDStack.pop(); else { id = _curOID; _curOID = _curOID+1; } System.out.println("Next id = " + id); return id; /* int newID = nextFreeId.get(); int nextFree = freeIds.nextClearBit(newID); if (nextFree < 0) nextFree = freeIds.nextClearBit(0); if (nextFree < 0) throw new NullPointerException("Ran out of valid Id's."); nextFreeId.set(nextFree); return newID + FIRST_OID;*/ // } } /** * return a used Object ID back to the pool * @param object ID */ public synchronized void releaseId(int id) { /* if (Config.COMPACTION) { if (_freeSize >= _freeOIDs.length) { int[] tmp = new int[_freeSize + STACK_SIZE_INCREMENT]; System.arraycopy(_freeOIDs, 0, tmp, 0, _freeSize); _freeOIDs = tmp; } _freeOIDs[_freeSize++] = id; } else if (id >= FIRST_OID) { freeIds.clear(id-FIRST_OID); freeIdCount.incrementAndGet(); }*/ _freeOIDStack.push(id); System.out.println("Released id: " + id); } public int size() { if (!Config.COMPACTION) { return freeIdCount.get(); } return _freeSize + LAST_OID - FIRST_OID; } private BitSet freeIds = null; public void initialize() { try { freeIds = new BitSet(LAST_OID - FIRST_OID); freeIdCount = new AtomicInteger(freeIds.size()); nextFreeId = new AtomicInteger(0); freeIds.set(0); //for (int objectID = FIRST_OID; objectID <= LAST_OID; objectID++) //{ // freeIds.s(objectID); //} initialized = true; } catch (Exception e) { initialized = false; e.printStackTrace(); } } /** * @param id */ public synchronized void setObjectIDAsUsed(int id) { if (id >= FIRST_OID && !Config.COMPACTION) { freeIds.set(id-FIRST_OID); freeIdCount.decrementAndGet(); } } }