|
|
|
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
|
|
|
|
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
|
|
|
|
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
|
|
|
|
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
|
|
|
|
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
|
|
|
|
// Magicbane Emulator Project © 2013 - 2022
|
|
|
|
// www.magicbane.com
|
|
|
|
|
|
|
|
package engine.gameManager;
|
|
|
|
|
|
|
|
import com.zaxxer.hikari.HikariConfig;
|
|
|
|
import com.zaxxer.hikari.HikariDataSource;
|
|
|
|
import engine.Enum;
|
|
|
|
import engine.Enum.GameObjectType;
|
|
|
|
import engine.db.handlers.*;
|
|
|
|
import engine.objects.*;
|
|
|
|
import engine.server.MBServerStatics;
|
|
|
|
import engine.util.Hasher;
|
|
|
|
import org.pmw.tinylog.Logger;
|
|
|
|
|
|
|
|
import java.sql.Connection;
|
|
|
|
import java.sql.PreparedStatement;
|
|
|
|
import java.sql.SQLException;
|
|
|
|
import java.util.EnumMap;
|
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
|
|
|
|
public enum DbManager {
|
|
|
|
DBMANAGER;
|
|
|
|
|
|
|
|
private static HikariDataSource connectionPool = null;
|
|
|
|
|
|
|
|
public static Hasher hasher;
|
|
|
|
|
|
|
|
//Local Object Caching
|
|
|
|
|
|
|
|
private static final EnumMap<GameObjectType, ConcurrentHashMap<Integer, AbstractGameObject>> objectCache = new EnumMap<>(GameObjectType.class);
|
|
|
|
|
|
|
|
public static AbstractGameObject getObject(GameObjectType objectType, int objectUUID) {
|
|
|
|
|
|
|
|
AbstractGameObject outObject = null;
|
|
|
|
|
|
|
|
switch (objectType) {
|
|
|
|
case PlayerCharacter:
|
|
|
|
outObject = PlayerCharacter.getPlayerCharacter(objectUUID);
|
|
|
|
break;
|
|
|
|
case NPC:
|
|
|
|
outObject = NPC.getNPC(objectUUID);
|
|
|
|
break;
|
|
|
|
case Mob:
|
|
|
|
outObject = Mob.getFromCache(objectUUID);
|
|
|
|
break;
|
|
|
|
case Building:
|
|
|
|
outObject = BuildingManager.getBuilding(objectUUID);
|
|
|
|
break;
|
|
|
|
case Guild:
|
|
|
|
outObject = Guild.getGuild(objectUUID);
|
|
|
|
break;
|
|
|
|
case Item:
|
|
|
|
outObject = Item.getFromCache(objectUUID);
|
|
|
|
break;
|
|
|
|
case MobLoot:
|
|
|
|
outObject = MobLoot.getFromCache(objectUUID);
|
|
|
|
break;
|
|
|
|
case City:
|
|
|
|
outObject = City.getCity(objectUUID);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
Logger.error("Attempt to retrieve nonexistant " + objectType +
|
|
|
|
" from object cache." );
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return outObject;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static boolean inCache(GameObjectType gameObjectType, int uuid) {
|
|
|
|
|
|
|
|
if (objectCache.get(gameObjectType) == null)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return (objectCache.get(gameObjectType).containsKey(uuid));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public static AbstractGameObject getFromCache(GameObjectType gameObjectType, int uuid) {
|
|
|
|
|
|
|
|
if (objectCache.get(gameObjectType) == null)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
return objectCache.get(gameObjectType).get(uuid);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void removeFromCache(GameObjectType gameObjectType, int uuid) {
|
|
|
|
|
|
|
|
AbstractGameObject abstractGameObject;
|
|
|
|
|
|
|
|
if (objectCache.get(gameObjectType) == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
abstractGameObject = objectCache.get(gameObjectType).get(uuid);
|
|
|
|
|
|
|
|
if (abstractGameObject == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
removeFromCache(abstractGameObject);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void removeFromCache(AbstractGameObject abstractGameObject) {
|
|
|
|
|
|
|
|
if (abstractGameObject == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (objectCache.get(abstractGameObject.getObjectType()) == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Remove object from game cache
|
|
|
|
|
|
|
|
objectCache.get(abstractGameObject.getObjectType()).remove(abstractGameObject.getObjectUUID());
|
|
|
|
|
|
|
|
// Release bounds as we're dispensing with this object.
|
|
|
|
|
|
|
|
if (abstractGameObject instanceof AbstractWorldObject) {
|
|
|
|
AbstractWorldObject abstractWorldObject = (AbstractWorldObject)abstractGameObject;
|
|
|
|
|
|
|
|
if (abstractWorldObject.getBounds() != null) {
|
|
|
|
abstractWorldObject.getBounds().release();
|
|
|
|
abstractWorldObject.setBounds(null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public static boolean addToCache(AbstractGameObject gameObject) {
|
|
|
|
|
|
|
|
boolean isWorldServer = ConfigManager.serverType.equals(Enum.ServerType.WORLDSERVER);
|
|
|
|
|
|
|
|
if (!isWorldServer) {
|
|
|
|
if (MBServerStatics.SKIP_CACHE_LOGIN)
|
|
|
|
return true;
|
|
|
|
if (MBServerStatics.SKIP_CACHE_LOGIN_PLAYER
|
|
|
|
&& (gameObject.getObjectType() == GameObjectType.PlayerCharacter))
|
|
|
|
return true;
|
|
|
|
if (MBServerStatics.SKIP_CACHE_LOGIN_ITEM &&
|
|
|
|
(gameObject.getObjectType() == GameObjectType.Item))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// First time this object type has been cached. Create the hashmap.
|
|
|
|
|
|
|
|
if (objectCache.get(gameObject.getObjectType()) == null) {
|
|
|
|
|
|
|
|
int initialCapacity;
|
|
|
|
|
|
|
|
// Provide initial sizing hints
|
|
|
|
|
|
|
|
switch (gameObject.getObjectType()) {
|
|
|
|
case Building:
|
|
|
|
initialCapacity = 46900;
|
|
|
|
break;
|
|
|
|
case Mob:
|
|
|
|
initialCapacity = 11700;
|
|
|
|
break;
|
|
|
|
case NPC:
|
|
|
|
initialCapacity = 900;
|
|
|
|
break;
|
|
|
|
case Zone:
|
|
|
|
initialCapacity = 1070;
|
|
|
|
break;
|
|
|
|
case Account:
|
|
|
|
initialCapacity = 10000;
|
|
|
|
break;
|
|
|
|
case Guild:
|
|
|
|
initialCapacity = 100;
|
|
|
|
break;
|
|
|
|
case ItemContainer:
|
|
|
|
initialCapacity = 100;
|
|
|
|
break;
|
|
|
|
case Item:
|
|
|
|
initialCapacity = 1000;
|
|
|
|
break;
|
|
|
|
case MobLoot:
|
|
|
|
initialCapacity = 10000;
|
|
|
|
break;
|
|
|
|
case PlayerCharacter:
|
|
|
|
initialCapacity = 100;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
initialCapacity = 100; // Lookup api default should be ok for small maps
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
objectCache.put(gameObject.getObjectType(), new ConcurrentHashMap<>(initialCapacity));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add the object to the cache. This will overwrite the current map entry.
|
|
|
|
|
|
|
|
objectCache.get(gameObject.getObjectType()).put(gameObject.getObjectUUID(), gameObject);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static java.util.Collection<AbstractGameObject> getList(GameObjectType gameObjectType) {
|
|
|
|
|
|
|
|
if (objectCache.get(gameObjectType) == null)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
return objectCache.get(gameObjectType).values();
|
|
|
|
}
|
|
|
|
|
|
|
|
public static PreparedStatement prepareStatement(String sql) throws SQLException {
|
|
|
|
return getConnection().prepareStatement(sql, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Omg refactor this out, somebody!
|
|
|
|
|
|
|
|
public static ConcurrentHashMap<Integer, AbstractGameObject> getMap(
|
|
|
|
GameObjectType gameObjectType) {
|
|
|
|
|
|
|
|
if (objectCache.get(gameObjectType) == null)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
return objectCache.get(gameObjectType);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void printCacheCount(PlayerCharacter pc) {
|
|
|
|
ChatManager.chatSystemInfo(pc, "Cache Lists");
|
|
|
|
|
|
|
|
for (GameObjectType gameObjectType : GameObjectType.values()) {
|
|
|
|
|
|
|
|
if (objectCache.get(gameObjectType) == null)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
String ret = gameObjectType.name() + ": " + objectCache.get(gameObjectType).size();
|
|
|
|
ChatManager.chatSystemInfo(pc, ret + '\n');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the conn
|
|
|
|
*/
|
|
|
|
//XXX I think we have a severe resource leak here! No one is putting the connections back!
|
|
|
|
public static Connection getConnection() {
|
|
|
|
try {
|
|
|
|
return DbManager.connectionPool.getConnection();
|
|
|
|
} catch (SQLException e) {
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static final dbAccountHandler AccountQueries = new dbAccountHandler();
|
|
|
|
public static final dbBaneHandler BaneQueries = new dbBaneHandler();
|
|
|
|
public static final dbBaseClassHandler BaseClassQueries = new dbBaseClassHandler();
|
|
|
|
public static final dbBuildingHandler BuildingQueries = new dbBuildingHandler();
|
|
|
|
public static final dbBuildingLocationHandler BuildingLocationQueries = new dbBuildingLocationHandler();
|
|
|
|
public static final dbCharacterPowerHandler CharacterPowerQueries = new dbCharacterPowerHandler();
|
|
|
|
public static final dbCharacterRuneHandler CharacterRuneQueries = new dbCharacterRuneHandler();
|
|
|
|
public static final dbCharacterSkillHandler CharacterSkillQueries = new dbCharacterSkillHandler();
|
|
|
|
public static final dbCityHandler CityQueries = new dbCityHandler();
|
|
|
|
public static final dbContractHandler ContractQueries = new dbContractHandler();
|
|
|
|
public static final dbWarehouseHandler WarehouseQueries = new dbWarehouseHandler();
|
|
|
|
public static final dbCSSessionHandler CSSessionQueries = new dbCSSessionHandler();
|
|
|
|
public static final dbEnchantmentHandler EnchantmentQueries = new dbEnchantmentHandler();
|
|
|
|
public static final dbEffectsResourceCostHandler EffectsResourceCostsQueries = new dbEffectsResourceCostHandler();
|
|
|
|
public static final dbGuildHandler GuildQueries = new dbGuildHandler();
|
|
|
|
public static final dbItemHandler ItemQueries = new dbItemHandler();
|
|
|
|
public static final dbItemBaseHandler ItemBaseQueries = new dbItemBaseHandler();
|
|
|
|
public static final dbKitHandler KitQueries = new dbKitHandler();
|
|
|
|
public static final dbLootTableHandler LootQueries = new dbLootTableHandler();
|
|
|
|
public static final dbMenuHandler MenuQueries = new dbMenuHandler();
|
|
|
|
public static final dbMineHandler MineQueries = new dbMineHandler();
|
|
|
|
public static final dbMobHandler MobQueries = new dbMobHandler();
|
|
|
|
public static final dbMobBaseHandler MobBaseQueries = new dbMobBaseHandler();
|
|
|
|
public static final dbNPCHandler NPCQueries = new dbNPCHandler();
|
|
|
|
public static final dbPlayerCharacterHandler PlayerCharacterQueries = new dbPlayerCharacterHandler();
|
|
|
|
public static final dbPromotionClassHandler PromotionQueries = new dbPromotionClassHandler();
|
|
|
|
public static final dbRaceHandler RaceQueries = new dbRaceHandler();
|
|
|
|
public static final dbResistHandler ResistQueries = new dbResistHandler();
|
|
|
|
public static final dbRuneBaseAttributeHandler RuneBaseAttributeQueries = new dbRuneBaseAttributeHandler();
|
|
|
|
public static final dbRuneBaseEffectHandler RuneBaseEffectQueries = new dbRuneBaseEffectHandler();
|
|
|
|
public static final dbRuneBaseHandler RuneBaseQueries = new dbRuneBaseHandler();
|
|
|
|
public static final dbSkillBaseHandler SkillsBaseQueries = new dbSkillBaseHandler();
|
|
|
|
public static final dbSkillReqHandler SkillReqQueries = new dbSkillReqHandler();
|
|
|
|
public static final dbVendorDialogHandler VendorDialogQueries = new dbVendorDialogHandler();
|
|
|
|
public static final dbZoneHandler ZoneQueries = new dbZoneHandler();
|
|
|
|
public static final dbRealmHandler RealmQueries = new dbRealmHandler();
|
|
|
|
public static final dbBlueprintHandler BlueprintQueries = new dbBlueprintHandler();
|
|
|
|
public static final dbBoonHandler BoonQueries = new dbBoonHandler();
|
|
|
|
public static final dbShrineHandler ShrineQueries = new dbShrineHandler();
|
|
|
|
public static final dbHeightMapHandler HeightMapQueries = new dbHeightMapHandler();
|
|
|
|
|
|
|
|
public static final dbRunegateHandler RunegateQueries = new dbRunegateHandler();
|
|
|
|
|
|
|
|
public static void configureConnectionPool() {
|
|
|
|
|
|
|
|
HikariConfig config = new HikariConfig();
|
|
|
|
|
|
|
|
int connectionCount = (Runtime.getRuntime().availableProcessors() * 2) + 1;
|
|
|
|
config.setMaximumPoolSize(connectionCount);
|
|
|
|
|
|
|
|
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("minimumIdle", "5");
|
|
|
|
config.addDataSourceProperty("maxLifetime", "3600000");
|
|
|
|
|
|
|
|
config.addDataSourceProperty("characterEncoding", "utf8");
|
|
|
|
|
|
|
|
config.addDataSourceProperty("useServerPrepStmts", "true");
|
|
|
|
config.addDataSourceProperty("cachePrepStmts", "true");
|
|
|
|
config.addDataSourceProperty("prepStmtCacheSize", "500");
|
|
|
|
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
|
|
|
|
|
|
|
|
config.addDataSourceProperty("leakDetectionThreshold", "5000");
|
|
|
|
config.addDataSourceProperty("cacheServerConfiguration", "true");
|
|
|
|
|
|
|
|
connectionPool = new HikariDataSource(config); // setup the connection pool
|
|
|
|
|
|
|
|
Logger.info("Database pool has " + connectionCount + " max connections");
|
|
|
|
}
|
|
|
|
}
|