forked from MagicBane/Server
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
324 lines
11 KiB
324 lines
11 KiB
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . |
|
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· |
|
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ |
|
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ |
|
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ |
|
// Magicbane Emulator Project © 2013 - 2022 |
|
// www.magicbane.com |
|
|
|
|
|
package engine.db.archive; |
|
|
|
import com.zaxxer.hikari.HikariConfig; |
|
import com.zaxxer.hikari.HikariDataSource; |
|
import engine.gameManager.ConfigManager; |
|
import engine.util.Hasher; |
|
import org.pmw.tinylog.Logger; |
|
|
|
import java.sql.Connection; |
|
import java.sql.PreparedStatement; |
|
import java.sql.ResultSet; |
|
import java.sql.SQLException; |
|
import java.util.concurrent.LinkedBlockingQueue; |
|
|
|
import static engine.Enum.DataRecordType; |
|
|
|
public class DataWarehouse implements Runnable { |
|
|
|
public static final Hasher hasher = new Hasher("Cthulhu Owns Joo"); |
|
private static final LinkedBlockingQueue<DataRecord> recordQueue = new LinkedBlockingQueue<>(); |
|
public static HikariDataSource connectionPool = null; |
|
public static HikariDataSource remoteConnectionPool = null; |
|
|
|
public DataWarehouse() { |
|
|
|
Logger.info("Configuring local Database Connection Pool..."); |
|
|
|
configureConnectionPool(); |
|
|
|
// If WarehousePush is disabled |
|
// then early exit |
|
|
|
if ( ConfigManager.MB_WORLD_WAREHOUSE_PUSH.getValue().equalsIgnoreCase("false")) { |
|
Logger.info("Warehouse Remote Connection disabled along with push"); |
|
return; |
|
} |
|
|
|
Logger.info( "Configuring remote Database Connection Pool..."); |
|
configureRemoteConnectionPool(); |
|
|
|
} |
|
|
|
public static void bootStrap() { |
|
Thread warehousingThread; |
|
warehousingThread = new Thread(new DataWarehouse()); |
|
|
|
warehousingThread.setName("DataWarehouse"); |
|
warehousingThread.setPriority(Thread.NORM_PRIORITY - 1); |
|
warehousingThread.start(); |
|
} |
|
|
|
public static void pushToWarehouse(DataRecord dataRecord) { |
|
|
|
DataWarehouse.recordQueue.add(dataRecord); |
|
} |
|
|
|
public static void writeHash(DataRecordType recordType, int uuid) { |
|
|
|
// Member variable declaration |
|
|
|
Connection connection = null; |
|
PreparedStatement statement = null; |
|
String queryString; |
|
String hashString; |
|
|
|
try { |
|
connection = DataWarehouse.connectionPool.getConnection(); |
|
} catch (SQLException e) { |
|
e.printStackTrace(); |
|
} |
|
|
|
if (connection == null) { |
|
Logger.error("Null connection when writing zone hash."); |
|
return; |
|
} |
|
|
|
// Build query string |
|
|
|
switch (recordType) { |
|
case CHARACTER: |
|
queryString = "UPDATE `obj_character` SET hash = ? WHERE `UID` = ?"; |
|
break; |
|
case GUILD: |
|
queryString = "UPDATE `obj_guild` SET hash = ? WHERE `UID` = ?"; |
|
break; |
|
case ZONE: |
|
queryString = "UPDATE `obj_zone` SET hash = ? WHERE `UID` = ?"; |
|
break; |
|
case CITY: |
|
queryString = "UPDATE `obj_city` SET hash = ? WHERE `UID` = ?"; |
|
break; |
|
case REALM: |
|
queryString = "UPDATE `obj_realm` SET hash = ? WHERE `realmID` = ?"; |
|
break; |
|
default: |
|
queryString = null; |
|
break; |
|
} |
|
|
|
hashString = hasher.encrypt(uuid); |
|
|
|
// Write this record to the warehouse |
|
|
|
try { |
|
|
|
statement = connection.prepareStatement(queryString); |
|
|
|
statement.setString(1, hashString); |
|
statement.setLong(2, uuid); |
|
statement.execute(); |
|
} catch (SQLException e) { |
|
Logger.error("Error writing hash for uuid" + uuid + " of type " + recordType.name() + ' ' + e.toString()); |
|
e.printStackTrace(); |
|
} finally { |
|
if (connection != null) { |
|
try { |
|
connection.close(); |
|
} catch (SQLException e) { |
|
e.printStackTrace(); |
|
} |
|
} |
|
} |
|
} |
|
|
|
public static boolean recordExists(DataRecordType recordType, int uuid) { |
|
|
|
// Member variable declaration |
|
|
|
Connection connection = null; |
|
PreparedStatement statement = null; |
|
String queryString; |
|
ResultSet resultSet; |
|
|
|
try { |
|
connection = DataWarehouse.connectionPool.getConnection(); |
|
} catch (SQLException e) { |
|
e.printStackTrace(); |
|
} |
|
|
|
if (connection == null) { |
|
Logger.error("Null connection during char record lookup"); |
|
return true; // False positive here, so as not to try and write the record twice. |
|
// will refactor out once we write hashes to object tables |
|
} |
|
|
|
// Build query string |
|
|
|
switch (recordType) { |
|
case CHARACTER: |
|
queryString = "SELECT COUNT(*) from warehouse_characterhistory where char_id = ?"; |
|
break; |
|
case GUILD: |
|
queryString = "SELECT COUNT(*) from warehouse_guildhistory where guild_id = ?"; |
|
break; |
|
case CITY: |
|
queryString = "SELECT COUNT(*) from warehouse_cityhistory where city_id = ?"; |
|
break; |
|
case REALM: |
|
queryString = "SELECT COUNT(*) from warehouse_realmhistory where realm_id = ?"; |
|
break; |
|
case BANE: |
|
queryString = "SELECT COUNT(*) from warehouse_banehistory where city_id = ? AND `resolution` = 'PENDING'"; |
|
break; |
|
case ZONE: // Does not really exist but enum acts as a proxy for hash lookup |
|
case MINE: // Does not really exist but enum acts as a proxy for hash lookup |
|
default: |
|
queryString = null; |
|
break; |
|
} |
|
|
|
try { |
|
statement = connection.prepareStatement(queryString); |
|
statement.setString(1, DataWarehouse.hasher.encrypt(uuid)); |
|
resultSet = statement.executeQuery(); |
|
|
|
while (resultSet.next()) { |
|
return resultSet.getInt("COUNT(*)") > 0; |
|
} |
|
|
|
} catch (SQLException e) { |
|
Logger.error("Error in record lookup for " + recordType.name() + " of uuid:" + uuid + e.toString()); |
|
e.printStackTrace(); |
|
} finally { |
|
if (connection != null) { |
|
try { |
|
connection.close(); |
|
} catch (SQLException e) { |
|
e.printStackTrace(); |
|
} |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
public void run() { |
|
|
|
// Working variable set |
|
|
|
DataRecord dataRecord; |
|
PvpRecord pvpRecord; |
|
GuildRecord guildRecord; |
|
CharacterRecord characterRecord; |
|
CityRecord cityRecord; |
|
BaneRecord baneRecord; |
|
RealmRecord realmRecord; |
|
MineRecord mineRecord; |
|
|
|
Logger.info( "DataWarehouse is running."); |
|
|
|
while (true) { |
|
|
|
dataRecord = null; |
|
pvpRecord = null; |
|
guildRecord = null; |
|
characterRecord = null; |
|
cityRecord = null; |
|
baneRecord = null; |
|
realmRecord = null; |
|
mineRecord = null; |
|
|
|
try { |
|
dataRecord = recordQueue.take(); |
|
} catch (InterruptedException e) { |
|
e.printStackTrace(); |
|
} |
|
|
|
// Write record to appropriate warehousing table |
|
|
|
if (dataRecord != null) { |
|
|
|
switch (dataRecord.recordType) { |
|
case PVP: |
|
pvpRecord = (PvpRecord) dataRecord; |
|
pvpRecord.write(); |
|
pvpRecord.release(); |
|
break; |
|
case CHARACTER: |
|
characterRecord = (CharacterRecord) dataRecord; |
|
characterRecord.write(); |
|
characterRecord.release(); |
|
break; |
|
case GUILD: |
|
guildRecord = (GuildRecord) dataRecord; |
|
guildRecord.write(); |
|
guildRecord.release(); |
|
break; |
|
case CITY: |
|
cityRecord = (CityRecord) dataRecord; |
|
cityRecord.write(); |
|
cityRecord.release(); |
|
break; |
|
case BANE: |
|
baneRecord = (BaneRecord) dataRecord; |
|
baneRecord.write(); |
|
baneRecord.release(); |
|
break; |
|
case REALM: |
|
realmRecord = (RealmRecord) dataRecord; |
|
realmRecord.write(); |
|
realmRecord.release(); |
|
break; |
|
case MINE: |
|
mineRecord = (MineRecord) dataRecord; |
|
mineRecord.write(); |
|
mineRecord.release(); |
|
break; |
|
default: |
|
Logger.error( "Unhandled record type"); |
|
break; |
|
|
|
} // end switch |
|
} |
|
} |
|
} |
|
|
|
private static void configureConnectionPool() { |
|
|
|
HikariConfig config = new HikariConfig(); |
|
|
|
config.setMaximumPoolSize(10); |
|
|
|
config.setJdbcUrl("jdbc:mysql://" + ConfigManager.MB_DATABASE_ADDRESS.getValue() + |
|
":" + ConfigManager.MB_DATABASE_PORT.getValue() + "/" + |
|
ConfigManager.MB_DATABASE_NAME.getValue()); |
|
config.setUsername(ConfigManager.MB_DATABASE_USER.getValue()); |
|
config.setPassword( ConfigManager.MB_DATABASE_PASS.getValue()); |
|
config.addDataSourceProperty("characterEncoding", "utf8"); |
|
config.addDataSourceProperty("cachePrepStmts", "true"); |
|
config.addDataSourceProperty("prepStmtCacheSize", "250"); |
|
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); |
|
|
|
connectionPool = new HikariDataSource(config); // setup the connection pool |
|
|
|
Logger.info("Local warehouse database connection configured"); |
|
} |
|
|
|
private static void configureRemoteConnectionPool() { |
|
|
|
HikariConfig config = new HikariConfig(); |
|
|
|
config.setMaximumPoolSize(1); // Only the server talks to remote, so yeah. |
|
config.setJdbcUrl(ConfigManager.MB_WAREHOUSE_ADDR.getValue()); |
|
config.setUsername(ConfigManager.MB_WAREHOUSE_USER.getValue()); |
|
config.setPassword(ConfigManager.MB_WAREHOUSE_PASS.getValue()); |
|
config.addDataSourceProperty("characterEncoding", "utf8"); |
|
config.addDataSourceProperty("cachePrepStmts", "true"); |
|
config.addDataSourceProperty("prepStmtCacheSize", "250"); |
|
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); |
|
|
|
remoteConnectionPool = new HikariDataSource(config); // setup the connection pool |
|
|
|
Logger.info("remote warehouse connection configured"); |
|
} |
|
|
|
}
|
|
|