This commit is contained in:
mobius
2015-01-01 20:02:50 +00:00
parent eeae660458
commit a6a3718849
17894 changed files with 2818932 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,274 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jserver.gameserver.ThreadPoolManager;
import com.mchange.v2.c3p0.ComboPooledDataSource;
/**
* This class manages the database connections.
*/
public class L2DatabaseFactory
{
private static final Logger _log = Logger.getLogger(L2DatabaseFactory.class.getName());
private static L2DatabaseFactory _instance;
private static volatile ScheduledExecutorService _executor;
private ComboPooledDataSource _source;
/**
* Instantiates a new l2 database factory.
* @throws SQLException the SQL exception
*/
public L2DatabaseFactory() throws SQLException
{
try
{
if (Config.DATABASE_MAX_CONNECTIONS < 2)
{
Config.DATABASE_MAX_CONNECTIONS = 2;
_log.warning("A minimum of " + Config.DATABASE_MAX_CONNECTIONS + " db connections are required.");
}
_source = new ComboPooledDataSource();
_source.setAutoCommitOnClose(true);
_source.setInitialPoolSize(10);
_source.setMinPoolSize(10);
_source.setMaxPoolSize(Math.max(10, Config.DATABASE_MAX_CONNECTIONS));
_source.setAcquireRetryAttempts(0); // try to obtain connections indefinitely (0 = never quit)
_source.setAcquireRetryDelay(500); // 500 milliseconds wait before try to acquire connection again
_source.setCheckoutTimeout(0); // 0 = wait indefinitely for new connection
// if pool is exhausted
_source.setAcquireIncrement(5); // if pool is exhausted, get 5 more connections at a time
// cause there is a "long" delay on acquire connection
// so taking more than one connection at once will make connection pooling
// more effective.
// this "connection_test_table" is automatically created if not already there
_source.setAutomaticTestTable("connection_test_table");
_source.setTestConnectionOnCheckin(false);
// testing OnCheckin used with IdleConnectionTestPeriod is faster than testing on checkout
_source.setIdleConnectionTestPeriod(3600); // test idle connection every 60 sec
_source.setMaxIdleTime(Config.DATABASE_MAX_IDLE_TIME); // 0 = idle connections never expire
// *THANKS* to connection testing configured above
// but I prefer to disconnect all connections not used
// for more than 1 hour
// enables statement caching, there is a "semi-bug" in c3p0 0.9.0 but in 0.9.0.2 and later it's fixed
_source.setMaxStatementsPerConnection(100);
_source.setBreakAfterAcquireFailure(false); // never fail if any way possible
// setting this to true will make
// c3p0 "crash" and refuse to work
// till restart thus making acquire
// errors "FATAL" ... we don't want that
// it should be possible to recover
_source.setDriverClass(Config.DATABASE_DRIVER);
_source.setJdbcUrl(Config.DATABASE_URL);
_source.setUser(Config.DATABASE_LOGIN);
_source.setPassword(Config.DATABASE_PASSWORD);
/* Test the connection */
_source.getConnection().close();
if (Config.DEBUG)
{
_log.fine("Database Connection Working");
}
}
catch (SQLException x)
{
if (Config.DEBUG)
{
_log.fine("Database Connection FAILED");
}
// re-throw the exception
throw x;
}
catch (Exception e)
{
if (Config.DEBUG)
{
_log.fine("Database Connection FAILED");
}
throw new SQLException("Could not init DB connection:" + e.getMessage());
}
}
/**
* Shutdown.
*/
public void shutdown()
{
try
{
_source.close();
}
catch (Exception e)
{
_log.log(Level.INFO, "", e);
}
try
{
_source = null;
}
catch (Exception e)
{
_log.log(Level.INFO, "", e);
}
}
/**
* Gets the single instance of L2DatabaseFactory.
* @return single instance of L2DatabaseFactory
* @throws SQLException the SQL exception
*/
public static L2DatabaseFactory getInstance() throws SQLException
{
synchronized (L2DatabaseFactory.class)
{
if (_instance == null)
{
_instance = new L2DatabaseFactory();
}
}
return _instance;
}
/**
* Gets the connection.
* @return the connection
*/
public Connection getConnection()
{
Connection con = null;
while (con == null)
{
try
{
con = _source.getConnection();
if (Server.serverMode == Server.MODE_GAMESERVER)
{
ThreadPoolManager.getInstance().scheduleGeneral(new ConnectionCloser(con, new RuntimeException()), Config.CONNECTION_CLOSE_TIME);
}
else
{
getExecutor().schedule(new ConnectionCloser(con, new RuntimeException()), Config.CONNECTION_CLOSE_TIME, TimeUnit.MILLISECONDS);
}
}
catch (SQLException e)
{
_log.log(Level.WARNING, "L2DatabaseFactory: getConnection() failed, trying again " + e.getMessage(), e);
}
}
return con;
}
/**
* The Class ConnectionCloser.
*/
private static class ConnectionCloser implements Runnable
{
private static final Logger _log = Logger.getLogger(ConnectionCloser.class.getName());
/** The connection. */
private final Connection c;
/** The exception. */
private final RuntimeException exp;
/**
* Instantiates a new connection closer.
* @param con the con
* @param e the e
*/
public ConnectionCloser(Connection con, RuntimeException e)
{
c = con;
exp = e;
}
@Override
public void run()
{
try
{
if (!c.isClosed())
{
_log.log(Level.WARNING, "Unclosed connection! Trace: " + exp.getStackTrace()[1], exp);
}
}
catch (SQLException e)
{
_log.log(Level.WARNING, "", e);
}
}
}
/**
* Gets the executor.
* @return the executor
*/
private static ScheduledExecutorService getExecutor()
{
if (_executor == null)
{
synchronized (L2DatabaseFactory.class)
{
if (_executor == null)
{
_executor = Executors.newSingleThreadScheduledExecutor();
}
}
}
return _executor;
}
/**
* Gets the busy connection count.
* @return the busy connection count
* @throws SQLException the SQL exception
*/
public int getBusyConnectionCount() throws SQLException
{
return _source.getNumBusyConnectionsDefaultUser();
}
/**
* Gets the idle connection count.
* @return the idle connection count
* @throws SQLException the SQL exception
*/
public int getIdleConnectionCount() throws SQLException
{
return _source.getNumIdleConnectionsDefaultUser();
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver;
/**
* This class used to be the starter class, since LS/GS split, it only retains server mode
*/
public class Server
{
// constants for the server mode
private static final int MODE_NONE = 0;
public static final int MODE_GAMESERVER = 1;
public static final int MODE_LOGINSERVER = 2;
public static int serverMode = MODE_NONE;
}

View File

@@ -0,0 +1,149 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver;
import java.io.IOException;
import java.net.InetAddress;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bitlet.weupnp.GatewayDevice;
import org.bitlet.weupnp.GatewayDiscover;
import org.bitlet.weupnp.PortMappingEntry;
import org.xml.sax.SAXException;
/**
* @author UnAfraid
*/
public class UPnPService
{
private static final Logger _log = Logger.getLogger(UPnPService.class.getName());
private static final String PROTOCOL = "TCP";
private final GatewayDiscover _gatewayDiscover = new GatewayDiscover();
private GatewayDevice _activeGW;
protected UPnPService()
{
try
{
load();
}
catch (Exception e)
{
_log.log(Level.WARNING, getClass().getSimpleName() + ": error while initializing: ", e);
}
}
private void load() throws Exception
{
if (!Config.ENABLE_UPNP)
{
_log.log(Level.WARNING, "UPnP Service is disabled.");
return;
}
_log.log(Level.INFO, "Looking for UPnP Gateway Devices...");
final Map<InetAddress, GatewayDevice> gateways = _gatewayDiscover.discover();
if (gateways.isEmpty())
{
_log.log(Level.INFO, "No UPnP gateways found");
return;
}
// choose the first active gateway for the tests
_activeGW = _gatewayDiscover.getValidGateway();
if (_activeGW != null)
{
_log.log(Level.INFO, "Using UPnP gateway: " + _activeGW.getFriendlyName());
}
else
{
_log.log(Level.INFO, "No active UPnP gateway found");
return;
}
_log.log(Level.INFO, "Using local address: " + _activeGW.getLocalAddress().getHostAddress() + " External address: " + _activeGW.getExternalIPAddress());
if (Server.serverMode == Server.MODE_GAMESERVER)
{
addPortMapping(Config.PORT_GAME, "L2j Game Server");
}
else if (Server.serverMode == Server.MODE_LOGINSERVER)
{
addPortMapping(Config.PORT_LOGIN, "L2j Login Server");
}
}
public void removeAllPorts() throws Exception
{
if (_activeGW != null)
{
if (Server.serverMode == Server.MODE_GAMESERVER)
{
deletePortMapping(Config.PORT_GAME);
}
else if (Server.serverMode == Server.MODE_LOGINSERVER)
{
deletePortMapping(Config.PORT_LOGIN);
}
}
}
private void addPortMapping(int port, String description) throws IOException, SAXException
{
final PortMappingEntry portMapping = new PortMappingEntry();
final InetAddress localAddress = _activeGW.getLocalAddress();
// Attempt to re-map
if (_activeGW.getSpecificPortMappingEntry(port, PROTOCOL, portMapping))
{
_activeGW.deletePortMapping(port, PROTOCOL);
}
if (_activeGW.addPortMapping(port, port, localAddress.getHostAddress(), PROTOCOL, description))
{
_log.log(Level.INFO, "Mapping successfull on [" + localAddress.getHostAddress() + ":" + port + "]");
}
else
{
_log.log(Level.INFO, "Mapping failed on [" + localAddress.getHostAddress() + ":" + port + "] - Already mapped?");
}
}
private void deletePortMapping(int port) throws IOException, SAXException
{
if (_activeGW.deletePortMapping(port, PROTOCOL))
{
_log.log(Level.INFO, "Mapping was deleted from [" + _activeGW.getLocalAddress().getHostAddress() + ":" + port + "]");
}
}
public static UPnPService getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final UPnPService _instance = new UPnPService();
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jserver.Config;
import com.l2jserver.gameserver.model.L2Clan;
import com.l2jserver.gameserver.model.entity.Fort;
import com.l2jserver.gameserver.model.itemcontainer.Inventory;
/**
* Class managing periodical events with castle
* @author Vice - 2008
*/
public class FortUpdater implements Runnable
{
protected static Logger _log = Logger.getLogger(FortUpdater.class.getName());
private final L2Clan _clan;
private final Fort _fort;
private int _runCount;
private final UpdaterType _updaterType;
public enum UpdaterType
{
MAX_OWN_TIME, // gives fort back to NPC clan
PERIODIC_UPDATE // raise blood oath/supply level
}
public FortUpdater(Fort fort, L2Clan clan, int runCount, UpdaterType ut)
{
_fort = fort;
_clan = clan;
_runCount = runCount;
_updaterType = ut;
}
@Override
public void run()
{
try
{
switch (_updaterType)
{
case PERIODIC_UPDATE:
_runCount++;
if ((_fort.getOwnerClan() == null) || (_fort.getOwnerClan() != _clan))
{
return;
}
_fort.getOwnerClan().increaseBloodOathCount();
if (_fort.getFortState() == 2)
{
if (_clan.getWarehouse().getAdena() >= Config.FS_FEE_FOR_CASTLE)
{
_clan.getWarehouse().destroyItemByItemId("FS_fee_for_Castle", Inventory.ADENA_ID, Config.FS_FEE_FOR_CASTLE, null, null);
_fort.getContractedCastle().addToTreasuryNoTax(Config.FS_FEE_FOR_CASTLE);
_fort.raiseSupplyLvL();
}
else
{
_fort.setFortState(1, 0);
}
}
_fort.saveFortVariables();
break;
case MAX_OWN_TIME:
if ((_fort.getOwnerClan() == null) || (_fort.getOwnerClan() != _clan))
{
return;
}
if (_fort.getOwnedTime() > (Config.FS_MAX_OWN_TIME * 3600))
{
_fort.removeOwner(true);
_fort.setFortState(0, 0);
}
break;
}
}
catch (Exception e)
{
_log.log(Level.WARNING, "", e);
}
}
public int getRunCount()
{
return _runCount;
}
}

View File

@@ -0,0 +1,497 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver;
import java.awt.Toolkit;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Calendar;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import org.mmocore.network.SelectorConfig;
import org.mmocore.network.SelectorThread;
import com.l2jserver.Config;
import com.l2jserver.L2DatabaseFactory;
import com.l2jserver.Server;
import com.l2jserver.UPnPService;
import com.l2jserver.gameserver.cache.HtmCache;
import com.l2jserver.gameserver.datatables.AbilityPointsData;
import com.l2jserver.gameserver.datatables.AdminTable;
import com.l2jserver.gameserver.datatables.AnnouncementsTable;
import com.l2jserver.gameserver.datatables.ArmorSetsData;
import com.l2jserver.gameserver.datatables.AugmentationData;
import com.l2jserver.gameserver.datatables.BeautyShopData;
import com.l2jserver.gameserver.datatables.BotReportTable;
import com.l2jserver.gameserver.datatables.BuyListData;
import com.l2jserver.gameserver.datatables.CategoryData;
import com.l2jserver.gameserver.datatables.CharNameTable;
import com.l2jserver.gameserver.datatables.CharSummonTable;
import com.l2jserver.gameserver.datatables.CharTemplateTable;
import com.l2jserver.gameserver.datatables.ClanTable;
import com.l2jserver.gameserver.datatables.ClassListData;
import com.l2jserver.gameserver.datatables.CrestTable;
import com.l2jserver.gameserver.datatables.DoorTable;
import com.l2jserver.gameserver.datatables.EnchantItemData;
import com.l2jserver.gameserver.datatables.EnchantItemGroupsData;
import com.l2jserver.gameserver.datatables.EnchantItemHPBonusData;
import com.l2jserver.gameserver.datatables.EnchantItemOptionsData;
import com.l2jserver.gameserver.datatables.EnchantSkillGroupsData;
import com.l2jserver.gameserver.datatables.EventDroplist;
import com.l2jserver.gameserver.datatables.ExperienceTable;
import com.l2jserver.gameserver.datatables.FishData;
import com.l2jserver.gameserver.datatables.FishingMonstersData;
import com.l2jserver.gameserver.datatables.FishingRodsData;
import com.l2jserver.gameserver.datatables.HennaData;
import com.l2jserver.gameserver.datatables.HitConditionBonus;
import com.l2jserver.gameserver.datatables.InitialEquipmentData;
import com.l2jserver.gameserver.datatables.InitialShortcutData;
import com.l2jserver.gameserver.datatables.ItemCrystalizationData;
import com.l2jserver.gameserver.datatables.ItemTable;
import com.l2jserver.gameserver.datatables.KarmaData;
import com.l2jserver.gameserver.datatables.MerchantPriceConfigTable;
import com.l2jserver.gameserver.datatables.MultisellData;
import com.l2jserver.gameserver.datatables.NpcBufferTable;
import com.l2jserver.gameserver.datatables.NpcData;
import com.l2jserver.gameserver.datatables.OfflineTradersTable;
import com.l2jserver.gameserver.datatables.OptionsData;
import com.l2jserver.gameserver.datatables.PetDataTable;
import com.l2jserver.gameserver.datatables.PlayerXpPercentLostData;
import com.l2jserver.gameserver.datatables.RecipeData;
import com.l2jserver.gameserver.datatables.SecondaryAuthData;
import com.l2jserver.gameserver.datatables.ShuttleData;
import com.l2jserver.gameserver.datatables.SiegeScheduleData;
import com.l2jserver.gameserver.datatables.SkillData;
import com.l2jserver.gameserver.datatables.SkillLearnData;
import com.l2jserver.gameserver.datatables.SkillTreesData;
import com.l2jserver.gameserver.datatables.SpawnTable;
import com.l2jserver.gameserver.datatables.StaticObjects;
import com.l2jserver.gameserver.datatables.SummonSkillsTable;
import com.l2jserver.gameserver.datatables.TeleportLocationTable;
import com.l2jserver.gameserver.datatables.TeleportersData;
import com.l2jserver.gameserver.datatables.TransformData;
import com.l2jserver.gameserver.datatables.UIData;
import com.l2jserver.gameserver.geoeditorcon.GeoEditorListener;
import com.l2jserver.gameserver.handler.EffectHandler;
import com.l2jserver.gameserver.idfactory.IdFactory;
import com.l2jserver.gameserver.instancemanager.AirShipManager;
import com.l2jserver.gameserver.instancemanager.AntiFeedManager;
import com.l2jserver.gameserver.instancemanager.AuctionManager;
import com.l2jserver.gameserver.instancemanager.BoatManager;
import com.l2jserver.gameserver.instancemanager.CHSiegeManager;
import com.l2jserver.gameserver.instancemanager.CastleManager;
import com.l2jserver.gameserver.instancemanager.CastleManorManager;
import com.l2jserver.gameserver.instancemanager.ClanEntryManager;
import com.l2jserver.gameserver.instancemanager.ClanHallManager;
import com.l2jserver.gameserver.instancemanager.CoupleManager;
import com.l2jserver.gameserver.instancemanager.CursedWeaponsManager;
import com.l2jserver.gameserver.instancemanager.DayNightSpawnManager;
import com.l2jserver.gameserver.instancemanager.FortManager;
import com.l2jserver.gameserver.instancemanager.FortSiegeManager;
import com.l2jserver.gameserver.instancemanager.FourSepulchersManager;
import com.l2jserver.gameserver.instancemanager.GlobalVariablesManager;
import com.l2jserver.gameserver.instancemanager.GraciaSeedsManager;
import com.l2jserver.gameserver.instancemanager.GrandBossManager;
import com.l2jserver.gameserver.instancemanager.InstanceManager;
import com.l2jserver.gameserver.instancemanager.ItemAuctionManager;
import com.l2jserver.gameserver.instancemanager.ItemsOnGroundManager;
import com.l2jserver.gameserver.instancemanager.MailManager;
import com.l2jserver.gameserver.instancemanager.MapRegionManager;
import com.l2jserver.gameserver.instancemanager.MentorManager;
import com.l2jserver.gameserver.instancemanager.MercTicketManager;
import com.l2jserver.gameserver.instancemanager.PetitionManager;
import com.l2jserver.gameserver.instancemanager.PunishmentManager;
import com.l2jserver.gameserver.instancemanager.QuestManager;
import com.l2jserver.gameserver.instancemanager.RaidBossPointsManager;
import com.l2jserver.gameserver.instancemanager.RaidBossSpawnManager;
import com.l2jserver.gameserver.instancemanager.SiegeManager;
import com.l2jserver.gameserver.instancemanager.WalkingManager;
import com.l2jserver.gameserver.instancemanager.ZoneManager;
import com.l2jserver.gameserver.model.AutoSpawnHandler;
import com.l2jserver.gameserver.model.L2World;
import com.l2jserver.gameserver.model.PartyMatchRoomList;
import com.l2jserver.gameserver.model.PartyMatchWaitingList;
import com.l2jserver.gameserver.model.entity.Hero;
import com.l2jserver.gameserver.model.entity.TvTManager;
import com.l2jserver.gameserver.model.events.EventDispatcher;
import com.l2jserver.gameserver.model.olympiad.Olympiad;
import com.l2jserver.gameserver.network.L2GameClient;
import com.l2jserver.gameserver.network.L2GamePacketHandler;
import com.l2jserver.gameserver.pathfinding.PathFinding;
import com.l2jserver.gameserver.script.faenor.FaenorScriptEngine;
import com.l2jserver.gameserver.scripting.L2ScriptEngineManager;
import com.l2jserver.gameserver.taskmanager.KnownListUpdateTaskManager;
import com.l2jserver.gameserver.taskmanager.TaskManager;
import com.l2jserver.status.Status;
import com.l2jserver.util.DeadLockDetector;
import com.l2jserver.util.IPv4Filter;
public class GameServer
{
private static final Logger _log = Logger.getLogger(GameServer.class.getName());
private final SelectorThread<L2GameClient> _selectorThread;
private final L2GamePacketHandler _gamePacketHandler;
private final DeadLockDetector _deadDetectThread;
public static GameServer gameServer;
public static final Calendar dateTimeServerStarted = Calendar.getInstance();
public long getUsedMemoryMB()
{
return (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1048576;
}
public SelectorThread<L2GameClient> getSelectorThread()
{
return _selectorThread;
}
public L2GamePacketHandler getL2GamePacketHandler()
{
return _gamePacketHandler;
}
public DeadLockDetector getDeadLockDetectorThread()
{
return _deadDetectThread;
}
public GameServer() throws Exception
{
long serverLoadStart = System.currentTimeMillis();
_log.info(getClass().getSimpleName() + ": Used memory: " + getUsedMemoryMB() + "MB");
if (!IdFactory.getInstance().isInitialized())
{
_log.severe(getClass().getSimpleName() + ": Could not read object IDs from DB. Please check your data.");
throw new Exception("Could not initialize the ID factory");
}
ThreadPoolManager.getInstance();
EventDispatcher.getInstance();
new File("log/game").mkdirs();
// load script engines
printSection("Engines");
L2ScriptEngineManager.getInstance();
printSection("World");
// start game time control early
GameTimeController.init();
InstanceManager.getInstance();
L2World.getInstance();
MapRegionManager.getInstance();
AnnouncementsTable.getInstance();
GlobalVariablesManager.getInstance();
printSection("Data");
CategoryData.getInstance();
SecondaryAuthData.getInstance();
AbilityPointsData.getInstance();
printSection("Skills");
EffectHandler.getInstance().executeScript();
EnchantSkillGroupsData.getInstance();
SkillTreesData.getInstance();
SkillData.getInstance();
SummonSkillsTable.getInstance();
printSection("Items");
ItemTable.getInstance();
EnchantItemGroupsData.getInstance();
EnchantItemData.getInstance();
EnchantItemOptionsData.getInstance();
ItemCrystalizationData.getInstance();
OptionsData.getInstance();
EnchantItemHPBonusData.getInstance();
MerchantPriceConfigTable.getInstance().loadInstances();
BuyListData.getInstance();
MultisellData.getInstance();
RecipeData.getInstance();
ArmorSetsData.getInstance();
FishData.getInstance();
FishingMonstersData.getInstance();
FishingRodsData.getInstance();
HennaData.getInstance();
printSection("Characters");
ClassListData.getInstance();
InitialEquipmentData.getInstance();
InitialShortcutData.getInstance();
ExperienceTable.getInstance();
PlayerXpPercentLostData.getInstance();
KarmaData.getInstance();
HitConditionBonus.getInstance();
CharTemplateTable.getInstance();
CharNameTable.getInstance();
AdminTable.getInstance();
RaidBossPointsManager.getInstance();
PetDataTable.getInstance();
CharSummonTable.getInstance().init();
BeautyShopData.getInstance();
MentorManager.getInstance();
printSection("Clans");
ClanTable.getInstance();
CHSiegeManager.getInstance();
ClanHallManager.getInstance();
AuctionManager.getInstance();
ClanEntryManager.getInstance();
printSection("Geodata");
GeoData.getInstance();
if (Config.GEODATA == 2)
{
PathFinding.getInstance();
}
printSection("NPCs");
SkillLearnData.getInstance();
NpcData.getInstance();
WalkingManager.getInstance();
StaticObjects.getInstance();
ZoneManager.getInstance();
DoorTable.getInstance();
ItemAuctionManager.getInstance();
CastleManager.getInstance().loadInstances();
NpcBufferTable.getInstance();
DayNightSpawnManager.getInstance().trim().notifyChangeMode();
GrandBossManager.getInstance().initZones();
EventDroplist.getInstance();
printSection("Olympiad");
Olympiad.getInstance();
Hero.getInstance();
// Call to load caches
printSection("Cache");
HtmCache.getInstance();
CrestTable.getInstance();
TeleportLocationTable.getInstance();
TeleportersData.getInstance();
UIData.getInstance();
PartyMatchWaitingList.getInstance();
PartyMatchRoomList.getInstance();
PetitionManager.getInstance();
AugmentationData.getInstance();
CursedWeaponsManager.getInstance();
TransformData.getInstance();
BotReportTable.getInstance();
printSection("Scripts");
QuestManager.getInstance();
BoatManager.getInstance();
AirShipManager.getInstance();
ShuttleData.getInstance();
GraciaSeedsManager.getInstance();
try
{
_log.info(getClass().getSimpleName() + ": Loading server scripts:");
if (!Config.ALT_DEV_NO_HANDLERS || !Config.ALT_DEV_NO_QUESTS)
{
L2ScriptEngineManager.getInstance().executeScriptList(new File(Config.DATAPACK_ROOT, "data/scripts.cfg"));
}
}
catch (IOException ioe)
{
_log.severe(getClass().getSimpleName() + ": Failed loading scripts.cfg, scripts are not going to be loaded!");
}
SpawnTable.getInstance().load();
FourSepulchersManager.getInstance().init();
RaidBossSpawnManager.getInstance();
printSection("Siege");
SiegeManager.getInstance().getSieges();
CastleManager.getInstance().activateInstances();
FortManager.getInstance().loadInstances();
FortManager.getInstance().activateInstances();
FortSiegeManager.getInstance();
SiegeScheduleData.getInstance();
MerchantPriceConfigTable.getInstance().updateReferences();
CastleManorManager.getInstance();
MercTicketManager.getInstance();
QuestManager.getInstance().report();
if (Config.SAVE_DROPPED_ITEM)
{
ItemsOnGroundManager.getInstance();
}
if ((Config.AUTODESTROY_ITEM_AFTER > 0) || (Config.HERB_AUTO_DESTROY_TIME > 0))
{
ItemsAutoDestroy.getInstance();
}
MonsterRace.getInstance();
AutoSpawnHandler.getInstance();
FaenorScriptEngine.getInstance();
// Init of a cursed weapon manager
_log.info("AutoSpawnHandler: Loaded " + AutoSpawnHandler.getInstance().size() + " handlers in total.");
if (Config.L2JMOD_ALLOW_WEDDING)
{
CoupleManager.getInstance();
}
TaskManager.getInstance();
AntiFeedManager.getInstance().registerEvent(AntiFeedManager.GAME_ID);
if (Config.ALLOW_MAIL)
{
MailManager.getInstance();
}
if (Config.ACCEPT_GEOEDITOR_CONN)
{
GeoEditorListener.getInstance();
}
PunishmentManager.getInstance();
Runtime.getRuntime().addShutdownHook(Shutdown.getInstance());
_log.info("IdFactory: Free ObjectID's remaining: " + IdFactory.getInstance().size());
TvTManager.getInstance();
KnownListUpdateTaskManager.getInstance();
if ((Config.OFFLINE_TRADE_ENABLE || Config.OFFLINE_CRAFT_ENABLE) && Config.RESTORE_OFFLINERS)
{
OfflineTradersTable.getInstance().restoreOfflineTraders();
}
if (Config.DEADLOCK_DETECTOR)
{
_deadDetectThread = new DeadLockDetector();
_deadDetectThread.setDaemon(true);
_deadDetectThread.start();
}
else
{
_deadDetectThread = null;
}
System.gc();
// maxMemory is the upper limit the jvm can use, totalMemory the size of
// the current allocation pool, freeMemory the unused memory in the allocation pool
long freeMem = ((Runtime.getRuntime().maxMemory() - Runtime.getRuntime().totalMemory()) + Runtime.getRuntime().freeMemory()) / 1048576;
long totalMem = Runtime.getRuntime().maxMemory() / 1048576;
_log.info(getClass().getSimpleName() + ": Started, free memory " + freeMem + " Mb of " + totalMem + " Mb");
Toolkit.getDefaultToolkit().beep();
LoginServerThread.getInstance().start();
final SelectorConfig sc = new SelectorConfig();
sc.MAX_READ_PER_PASS = Config.MMO_MAX_READ_PER_PASS;
sc.MAX_SEND_PER_PASS = Config.MMO_MAX_SEND_PER_PASS;
sc.SLEEP_TIME = Config.MMO_SELECTOR_SLEEP_TIME;
sc.HELPER_BUFFER_COUNT = Config.MMO_HELPER_BUFFER_COUNT;
sc.TCP_NODELAY = Config.MMO_TCP_NODELAY;
_gamePacketHandler = new L2GamePacketHandler();
_selectorThread = new SelectorThread<>(sc, _gamePacketHandler, _gamePacketHandler, _gamePacketHandler, new IPv4Filter());
InetAddress bindAddress = null;
if (!Config.GAMESERVER_HOSTNAME.equals("*"))
{
try
{
bindAddress = InetAddress.getByName(Config.GAMESERVER_HOSTNAME);
}
catch (UnknownHostException e1)
{
_log.log(Level.SEVERE, getClass().getSimpleName() + ": WARNING: The GameServer bind address is invalid, using all avaliable IPs. Reason: " + e1.getMessage(), e1);
}
}
try
{
_selectorThread.openServerSocket(bindAddress, Config.PORT_GAME);
_selectorThread.start();
_log.log(Level.INFO, getClass().getSimpleName() + ": is now listening on: " + Config.GAMESERVER_HOSTNAME + ":" + Config.PORT_GAME);
}
catch (IOException e)
{
_log.log(Level.SEVERE, getClass().getSimpleName() + ": FATAL: Failed to open server socket. Reason: " + e.getMessage(), e);
System.exit(1);
}
_log.log(Level.INFO, getClass().getSimpleName() + ": Maximum numbers of connected players: " + Config.MAXIMUM_ONLINE_USERS);
_log.log(Level.INFO, getClass().getSimpleName() + ": Server loaded in " + ((System.currentTimeMillis() - serverLoadStart) / 1000) + " seconds.");
printSection("UPnP");
UPnPService.getInstance();
}
public static void main(String[] args) throws Exception
{
Server.serverMode = Server.MODE_GAMESERVER;
// Local Constants
final String LOG_FOLDER = "log"; // Name of folder for log file
final String LOG_NAME = "./log.cfg"; // Name of log file
/*** Main ***/
// Create log folder
File logFolder = new File(Config.DATAPACK_ROOT, LOG_FOLDER);
logFolder.mkdir();
// Create input stream for log file -- or store file data into memory
try (InputStream is = new FileInputStream(new File(LOG_NAME)))
{
LogManager.getLogManager().readConfiguration(is);
}
// Initialize config
Config.load();
printSection("Database");
L2DatabaseFactory.getInstance();
gameServer = new GameServer();
if (Config.IS_TELNET_ENABLED)
{
new Status(Server.serverMode).start();
}
else
{
_log.info(GameServer.class.getSimpleName() + ": Telnet server is currently disabled.");
}
}
public static void printSection(String s)
{
s = "=[ " + s + " ]";
while (s.length() < 61)
{
s = "-" + s;
}
_log.info(s);
}
}

View File

@@ -0,0 +1,229 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver;
import java.util.Calendar;
import java.util.logging.Level;
import java.util.logging.Logger;
import javolution.util.FastMap;
import com.l2jserver.Config;
import com.l2jserver.gameserver.ai.CtrlEvent;
import com.l2jserver.gameserver.ai.L2CharacterAI;
import com.l2jserver.gameserver.instancemanager.DayNightSpawnManager;
import com.l2jserver.gameserver.model.actor.L2Character;
/**
* Game Time controller class.
* @author Forsaiken
*/
public final class GameTimeController extends Thread
{
private static final Logger _log = Logger.getLogger(GameTimeController.class.getName());
public static final int TICKS_PER_SECOND = 10; // not able to change this without checking through code
public static final int MILLIS_IN_TICK = 1000 / TICKS_PER_SECOND;
public static final int IG_DAYS_PER_DAY = 6;
public static final int MILLIS_PER_IG_DAY = (3600000 * 24) / IG_DAYS_PER_DAY;
public static final int SECONDS_PER_IG_DAY = MILLIS_PER_IG_DAY / 1000;
public static final int MINUTES_PER_IG_DAY = SECONDS_PER_IG_DAY / 60;
public static final int TICKS_PER_IG_DAY = SECONDS_PER_IG_DAY * TICKS_PER_SECOND;
public static final int TICKS_SUN_STATE_CHANGE = TICKS_PER_IG_DAY / 4;
private static GameTimeController _instance;
private final FastMap<Integer, L2Character> _movingObjects = new FastMap<Integer, L2Character>().shared();
private final long _referenceTime;
private GameTimeController()
{
super("GameTimeController");
super.setDaemon(true);
super.setPriority(MAX_PRIORITY);
final Calendar c = Calendar.getInstance();
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
_referenceTime = c.getTimeInMillis();
super.start();
}
public static final void init()
{
_instance = new GameTimeController();
}
public final int getGameTime()
{
return (getGameTicks() % TICKS_PER_IG_DAY) / MILLIS_IN_TICK;
}
public final int getGameHour()
{
return getGameTime() / 60;
}
public final int getGameMinute()
{
return getGameTime() % 60;
}
public final boolean isNight()
{
return getGameHour() < 6;
}
/**
* The true GameTime tick. Directly taken from current time. This represents the tick of the time.
* @return
*/
public final int getGameTicks()
{
return (int) ((System.currentTimeMillis() - _referenceTime) / MILLIS_IN_TICK);
}
/**
* Add a L2Character to movingObjects of GameTimeController.
* @param cha The L2Character to add to movingObjects of GameTimeController
*/
public final void registerMovingObject(final L2Character cha)
{
if (cha == null)
{
return;
}
_movingObjects.putIfAbsent(cha.getObjectId(), cha);
}
/**
* Move all L2Characters contained in movingObjects of GameTimeController.<BR>
* <B><U> Concept</U> :</B><BR>
* All L2Character in movement are identified in <B>movingObjects</B> of GameTimeController.<BR>
* <B><U> Actions</U> :</B><BR>
* <ul>
* <li>Update the position of each L2Character</li>
* <li>If movement is finished, the L2Character is removed from movingObjects</li>
* <li>Create a task to update the _knownObject and _knowPlayers of each L2Character that finished its movement and of their already known L2Object then notify AI with EVT_ARRIVED</li>
* </ul>
*/
private final void moveObjects()
{
for (FastMap.Entry<Integer, L2Character> e = _movingObjects.head(), tail = _movingObjects.tail(); (e = e.getNext()) != tail;)
{
final L2Character character = e.getValue();
if (character.updatePosition(getGameTicks()))
{
// Destination reached. Remove from map and execute arrive event.
_movingObjects.remove(e.getKey());
fireCharacterArrived(character);
}
}
}
private final void fireCharacterArrived(final L2Character character)
{
final L2CharacterAI ai = character.getAI();
if (ai == null)
{
return;
}
ThreadPoolManager.getInstance().executeAi(() ->
{
try
{
if (Config.MOVE_BASED_KNOWNLIST)
{
character.getKnownList().findObjects();
}
ai.notifyEvent(CtrlEvent.EVT_ARRIVED);
}
catch (final Throwable e)
{
_log.log(Level.WARNING, "", e);
}
});
}
public final void stopTimer()
{
super.interrupt();
_log.log(Level.INFO, "Stopping " + getClass().getSimpleName());
}
@Override
public final void run()
{
_log.log(Level.CONFIG, getClass().getSimpleName() + ": Started.");
long nextTickTime, sleepTime;
boolean isNight = isNight();
if (isNight)
{
ThreadPoolManager.getInstance().executeAi(() -> DayNightSpawnManager.getInstance().notifyChangeMode());
}
while (true)
{
nextTickTime = ((System.currentTimeMillis() / MILLIS_IN_TICK) * MILLIS_IN_TICK) + 100;
try
{
moveObjects();
}
catch (final Throwable e)
{
_log.log(Level.WARNING, "", e);
}
sleepTime = nextTickTime - System.currentTimeMillis();
if (sleepTime > 0)
{
try
{
Thread.sleep(sleepTime);
}
catch (final InterruptedException e)
{
}
}
if (isNight() != isNight)
{
isNight = !isNight;
ThreadPoolManager.getInstance().executeAi(() -> DayNightSpawnManager.getInstance().notifyChangeMode());
}
}
}
public static final GameTimeController getInstance()
{
return _instance;
}
}

View File

@@ -0,0 +1,671 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver;
import java.io.FileInputStream;
import java.lang.reflect.Constructor;
import java.nio.file.Paths;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jserver.Config;
import com.l2jserver.gameserver.datatables.DoorTable;
import com.l2jserver.gameserver.geoengine.Direction;
import com.l2jserver.gameserver.geoengine.NullDriver;
import com.l2jserver.gameserver.geoengine.abstraction.IGeoDriver;
import com.l2jserver.gameserver.model.L2Object;
import com.l2jserver.gameserver.model.Location;
import com.l2jserver.gameserver.model.interfaces.ILocational;
import com.l2jserver.gameserver.util.GeoUtils;
import com.l2jserver.gameserver.util.LinePointIterator;
import com.l2jserver.gameserver.util.LinePointIterator3D;
/**
* @author -Nemesiss-, HorridoJoho
*/
public class GeoData implements IGeoDriver
{
private static final Logger LOGGER = Logger.getLogger(GeoData.class.getName());
private static final int ELEVATED_SEE_OVER_DISTANCE = 2;
private static final int MAX_SEE_OVER_HEIGHT = 48;
private final IGeoDriver _driver;
protected GeoData()
{
if (Config.GEODATA > 0)
{
IGeoDriver driver = null;
try
{
Class<?> cls = Class.forName(Config.GEODATA_DRIVER);
if (!IGeoDriver.class.isAssignableFrom(cls))
{
throw new ClassCastException("Geodata driver class needs to implement IGeoDriver!");
}
Constructor<?> ctor = cls.getConstructor(Properties.class);
Properties props = new Properties();
try (FileInputStream fis = new FileInputStream(Paths.get("config", "GeoDriver.properties").toString()))
{
props.load(fis);
}
driver = (IGeoDriver) ctor.newInstance(props);
}
catch (Exception ex)
{
LOGGER.log(Level.SEVERE, "Failed to load geodata driver!", ex);
System.exit(1);
}
// we do it this way so it's predictable for the compiler
_driver = driver;
}
else
{
_driver = new NullDriver(null);
}
}
@Override
public int getGeoX(int worldX)
{
return _driver.getGeoX(worldX);
}
@Override
public int getGeoY(int worldY)
{
return _driver.getGeoY(worldY);
}
@Override
public int getWorldX(int geoX)
{
return _driver.getWorldX(geoX);
}
@Override
public int getWorldY(int geoY)
{
return _driver.getWorldY(geoY);
}
@Override
public boolean hasGeoPos(int geoX, int geoY)
{
return _driver.hasGeoPos(geoX, geoY);
}
@Override
public int getNearestZ(int geoX, int geoY, int worldZ)
{
return _driver.getNearestZ(geoX, geoY, worldZ);
}
@Override
public int getNextLowerZ(int geoX, int geoY, int worldZ)
{
return _driver.getNextLowerZ(geoX, geoY, worldZ);
}
@Override
public int getNextHigherZ(int geoX, int geoY, int worldZ)
{
return _driver.getNextHigherZ(geoX, geoY, worldZ);
}
@Override
public boolean canEnterNeighbors(int geoX, int geoY, int worldZ, Direction first, Direction... more)
{
return _driver.canEnterNeighbors(geoX, geoY, worldZ, first, more);
}
@Override
public boolean canEnterAllNeighbors(int geoX, int geoY, int worldZ)
{
return _driver.canEnterAllNeighbors(geoX, geoY, worldZ);
}
// ///////////////////
// L2J METHODS
public boolean isNullDriver()
{
return _driver instanceof NullDriver;
}
/**
* Gets the height.
* @param x the x coordinate
* @param y the y coordinate
* @param z the z coordinate
* @return the height
*/
public int getHeight(int x, int y, int z)
{
return getNearestZ(getGeoX(x), getGeoY(y), z);
}
/**
* Gets the spawn height.
* @param x the x coordinate
* @param y the y coordinate
* @param z the the z coordinate
* @return the spawn height
*/
public int getSpawnHeight(int x, int y, int z)
{
// + 30, defend against defective geodata and invalid spawn z :(
return getNextLowerZ(getGeoX(x), getGeoY(y), z + 30);
}
/**
* Gets the spawn height.
* @param location the location
* @return the spawn height
*/
public int getSpawnHeight(Location location)
{
return getSpawnHeight(location.getX(), location.getY(), location.getZ());
}
/**
* Can see target. Doors as target always return true. Checks doors between.
* @param cha the character
* @param target the target
* @return {@code true} if the character can see the target (LOS), {@code false} otherwise
*/
public boolean canSeeTarget(L2Object cha, L2Object target)
{
if (target.isDoor())
{
// can always see doors :o
return true;
}
return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceId(), target.getX(), target.getY(), target.getZ(), target.getInstanceId());
}
/**
* Can see target. Checks doors between.
* @param cha the character
* @param worldPosition the world position
* @return {@code true} if the character can see the target at the given world position, {@code false} otherwise
*/
public boolean canSeeTarget(L2Object cha, ILocational worldPosition)
{
return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceId(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ());
}
/**
* Can see target. Checks doors between.
* @param x the x coordinate
* @param y the y coordinate
* @param z the z coordinate
* @param instanceId
* @param tx the target's x coordinate
* @param ty the target's y coordinate
* @param tz the target's z coordinate
* @param tInstanceId the target's instanceId
* @return
*/
public boolean canSeeTarget(int x, int y, int z, int instanceId, int tx, int ty, int tz, int tInstanceId)
{
if ((instanceId != tInstanceId))
{
return false;
}
return canSeeTarget(x, y, z, instanceId, tx, ty, tz);
}
/**
* Can see target. Checks doors between.
* @param x the x coordinate
* @param y the y coordinate
* @param z the z coordinate
* @param instanceId
* @param tx the target's x coordinate
* @param ty the target's y coordinate
* @param tz the target's z coordinate
* @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise
*/
public boolean canSeeTarget(int x, int y, int z, int instanceId, int tx, int ty, int tz)
{
if (DoorTable.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instanceId, true))
{
return false;
}
return canSeeTarget(x, y, z, tx, ty, tz);
}
private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, Direction dir)
{
boolean can = true;
switch (dir)
{
case NORTH_EAST:
can = canEnterNeighbors(prevX, prevY - 1, prevGeoZ, Direction.EAST) && canEnterNeighbors(prevX + 1, prevY, prevGeoZ, Direction.NORTH);
break;
case NORTH_WEST:
can = canEnterNeighbors(prevX, prevY - 1, prevGeoZ, Direction.WEST) && canEnterNeighbors(prevX - 1, prevY, prevGeoZ, Direction.NORTH);
break;
case SOUTH_EAST:
can = canEnterNeighbors(prevX, prevY + 1, prevGeoZ, Direction.EAST) && canEnterNeighbors(prevX + 1, prevY, prevGeoZ, Direction.SOUTH);
break;
case SOUTH_WEST:
can = canEnterNeighbors(prevX, prevY + 1, prevGeoZ, Direction.WEST) && canEnterNeighbors(prevX - 1, prevY, prevGeoZ, Direction.SOUTH);
break;
}
if (can && canEnterNeighbors(prevX, prevY, prevGeoZ, dir))
{
return getNearestZ(curX, curY, prevGeoZ);
}
return getNextHigherZ(curX, curY, prevGeoZ);
}
/**
* Can see target. Does not check doors between.
* @param x the x coordinate
* @param y the y coordinate
* @param z the z coordinate
* @param tx the target's x coordinate
* @param ty the target's y coordinate
* @param tz the target's z coordinate
* @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise
*/
public boolean canSeeTarget(int x, int y, int z, int tx, int ty, int tz)
{
int geoX = getGeoX(x);
int geoY = getGeoY(y);
int tGeoX = getGeoX(tx);
int tGeoY = getGeoY(ty);
z = getNearestZ(geoX, geoY, z);
tz = getNearestZ(tGeoX, tGeoY, tz);
if ((geoX == tGeoX) && (geoY == tGeoY))
{
if (hasGeoPos(tGeoX, tGeoY))
{
return z == tz;
}
return true;
}
if (tz > z)
{
int tmp = tx;
tx = x;
x = tmp;
tmp = ty;
ty = y;
y = tmp;
tmp = tz;
tz = z;
z = tmp;
tmp = tGeoX;
tGeoX = geoX;
geoX = tmp;
tmp = tGeoY;
tGeoY = geoY;
geoY = tmp;
}
LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz);
// first point is guaranteed to be available, skip it, we can always see our own position
pointIter.next();
int prevX = pointIter.x();
int prevY = pointIter.y();
int prevZ = pointIter.z();
int prevGeoZ = prevZ;
int ptIndex = 0;
while (pointIter.next())
{
int curX = pointIter.x();
int curY = pointIter.y();
if ((curX == prevX) && (curY == prevY))
{
continue;
}
int beeCurZ = pointIter.z();
int curGeoZ = prevGeoZ;
// the current position has geodata
if (hasGeoPos(curX, curY))
{
int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ);
Direction dir = GeoUtils.computeDirection(prevX, prevY, curX, curY);
curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, dir);
int maxHeight;
if (ptIndex < ELEVATED_SEE_OVER_DISTANCE)
{
maxHeight = z + MAX_SEE_OVER_HEIGHT;
}
else
{
maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT;
}
boolean canSeeThrough = false;
if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ))
{
switch (dir)
{
case NORTH_EAST:
{
int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Direction.EAST);
int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Direction.NORTH);
canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ));
break;
}
case NORTH_WEST:
{
int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Direction.WEST);
int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Direction.NORTH);
canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ));
break;
}
case SOUTH_EAST:
{
int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Direction.EAST);
int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Direction.SOUTH);
canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ));
break;
}
case SOUTH_WEST:
{
int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Direction.WEST);
int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Direction.SOUTH);
canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ));
break;
}
default:
{
canSeeThrough = true;
break;
}
}
}
if (!canSeeThrough)
{
return false;
}
}
prevX = curX;
prevY = curY;
prevGeoZ = curGeoZ;
++ptIndex;
}
return true;
}
/**
* Move check.
* @param x the x coordinate
* @param y the y coordinate
* @param z the z coordinate
* @param tx the target's x coordinate
* @param ty the target's y coordinate
* @param tz the target's z coordinate
* @param instanceId the instance id
* @return the last Location (x,y,z) where player can walk - just before wall
*/
public Location moveCheck(int x, int y, int z, int tx, int ty, int tz, int instanceId)
{
int geoX = getGeoX(x);
int geoY = getGeoY(y);
z = getNearestZ(geoX, geoY, z);
int tGeoX = getGeoX(tx);
int tGeoY = getGeoY(ty);
tz = getNearestZ(tGeoX, tGeoY, tz);
if (DoorTable.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instanceId, false))
{
return new Location(x, y, getHeight(x, y, z));
}
LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY);
// first point is guaranteed to be available
pointIter.next();
int prevX = pointIter.x();
int prevY = pointIter.y();
int prevZ = z;
while (pointIter.next())
{
int curX = pointIter.x();
int curY = pointIter.y();
int curZ = getNearestZ(curX, curY, prevZ);
if (hasGeoPos(prevX, prevY))
{
Direction dir = GeoUtils.computeDirection(prevX, prevY, curX, curY);
boolean canEnter = false;
if (canEnterNeighbors(prevX, prevY, prevZ, dir))
{
// check diagonal movement
switch (dir)
{
case NORTH_EAST:
canEnter = canEnterNeighbors(prevX, prevY - 1, prevZ, Direction.EAST) && canEnterNeighbors(prevX + 1, prevY, prevZ, Direction.NORTH);
break;
case NORTH_WEST:
canEnter = canEnterNeighbors(prevX, prevY - 1, prevZ, Direction.WEST) && canEnterNeighbors(prevX - 1, prevY, prevZ, Direction.NORTH);
break;
case SOUTH_EAST:
canEnter = canEnterNeighbors(prevX, prevY + 1, prevZ, Direction.EAST) && canEnterNeighbors(prevX + 1, prevY, prevZ, Direction.SOUTH);
break;
case SOUTH_WEST:
canEnter = canEnterNeighbors(prevX, prevY + 1, prevZ, Direction.WEST) && canEnterNeighbors(prevX - 1, prevY, prevZ, Direction.SOUTH);
break;
default:
canEnter = true;
break;
}
}
if (!canEnter)
{
// can't move, return previous location
return new Location(getWorldX(prevX), getWorldY(prevY), prevZ);
}
}
prevX = curX;
prevY = curY;
prevZ = curZ;
}
if (hasGeoPos(prevX, prevY) && (prevZ != tz))
{
// different floors, return start location
return new Location(x, y, z);
}
return new Location(tx, ty, tz);
}
/**
* Checks if its possible to move from one location to another.
* @param fromX the X coordinate to start checking from
* @param fromY the Y coordinate to start checking from
* @param fromZ the Z coordinate to start checking from
* @param toX the X coordinate to end checking at
* @param toY the Y coordinate to end checking at
* @param toZ the Z coordinate to end checking at
* @param instanceId the instance ID
* @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise
*/
public boolean canMove(int fromX, int fromY, int fromZ, int toX, int toY, int toZ, int instanceId)
{
int geoX = getGeoX(fromX);
int geoY = getGeoY(fromY);
fromZ = getNearestZ(geoX, geoY, fromZ);
int tGeoX = getGeoX(toX);
int tGeoY = getGeoY(toY);
toZ = getNearestZ(tGeoX, tGeoY, toZ);
if (DoorTable.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instanceId, false))
{
return false;
}
LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY);
// first point is guaranteed to be available
pointIter.next();
int prevX = pointIter.x();
int prevY = pointIter.y();
int prevZ = fromZ;
while (pointIter.next())
{
int curX = pointIter.x();
int curY = pointIter.y();
int curZ = getNearestZ(curX, curY, prevZ);
if (hasGeoPos(prevX, prevY))
{
Direction dir = GeoUtils.computeDirection(prevX, prevY, curX, curY);
boolean canEnter = false;
if (canEnterNeighbors(prevX, prevY, prevZ, dir))
{
// check diagonal movement
switch (dir)
{
case NORTH_EAST:
canEnter = canEnterNeighbors(prevX, prevY - 1, prevZ, Direction.EAST) && canEnterNeighbors(prevX + 1, prevY, prevZ, Direction.NORTH);
break;
case NORTH_WEST:
canEnter = canEnterNeighbors(prevX, prevY - 1, prevZ, Direction.WEST) && canEnterNeighbors(prevX - 1, prevY, prevZ, Direction.NORTH);
break;
case SOUTH_EAST:
canEnter = canEnterNeighbors(prevX, prevY + 1, prevZ, Direction.EAST) && canEnterNeighbors(prevX + 1, prevY, prevZ, Direction.SOUTH);
break;
case SOUTH_WEST:
canEnter = canEnterNeighbors(prevX, prevY + 1, prevZ, Direction.WEST) && canEnterNeighbors(prevX - 1, prevY, prevZ, Direction.SOUTH);
break;
default:
canEnter = true;
break;
}
}
if (!canEnter)
{
return false;
}
}
prevX = curX;
prevY = curY;
prevZ = curZ;
}
if (hasGeoPos(prevX, prevY) && (prevZ != toZ))
{
// different floors
return false;
}
return true;
}
public int traceTerrainZ(int x, int y, int z, int tx, int ty)
{
int geoX = getGeoX(x);
int geoY = getGeoY(y);
z = getNearestZ(geoX, geoY, z);
int tGeoX = getGeoX(tx);
int tGeoY = getGeoY(ty);
LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY);
// first point is guaranteed to be available
pointIter.next();
int prevZ = z;
while (pointIter.next())
{
int curX = pointIter.x();
int curY = pointIter.y();
int curZ = getNearestZ(curX, curY, prevZ);
prevZ = curZ;
}
return prevZ;
}
/**
* Checks if its possible to move from one location to another.
* @param from the {@code ILocational} to start checking from
* @param toX the X coordinate to end checking at
* @param toY the Y coordinate to end checking at
* @param toZ the Z coordinate to end checking at
* @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise
*/
public boolean canMove(ILocational from, int toX, int toY, int toZ)
{
return canMove(from.getX(), from.getY(), from.getZ(), toX, toY, toZ, from.getInstanceId());
}
/**
* Checks if its possible to move from one location to another.
* @param from the {@code ILocational} to start checking from
* @param to the {@code ILocational} to end checking at
* @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise
*/
public boolean canMove(ILocational from, ILocational to)
{
return canMove(from, to.getX(), to.getY(), to.getZ());
}
/**
* Checks the specified position for available geodata.
* @param x the X coordinate
* @param y the Y coordinate
* @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise
*/
public boolean hasGeo(int x, int y)
{
return hasGeoPos(getGeoX(x), getGeoY(y));
}
public static GeoData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected final static GeoData _instance = new GeoData();
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver;
/**
* Interface for managers of list of instances.
* @author fordfrog
*/
public interface InstanceListManager
{
/**
* Loads instances with their data from persistent format.<br>
* This method has no side effect as calling methods of another instance manager.
*/
void loadInstances();
/**
* For each loaded instance, updates references to related instances.
*/
void updateReferences();
/**
* Activates instances so their setup is performed.
*/
void activateInstances();
}

View File

@@ -0,0 +1,116 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver;
import java.util.List;
import javolution.util.FastList;
import com.l2jserver.Config;
import com.l2jserver.gameserver.enums.ItemLocation;
import com.l2jserver.gameserver.instancemanager.ItemsOnGroundManager;
import com.l2jserver.gameserver.model.L2World;
import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
public final class ItemsAutoDestroy
{
private final List<L2ItemInstance> _items = new FastList<>();
protected ItemsAutoDestroy()
{
ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(this::removeItems, 5000, 5000);
}
public static ItemsAutoDestroy getInstance()
{
return SingletonHolder._instance;
}
public synchronized void addItem(L2ItemInstance item)
{
item.setDropTime(System.currentTimeMillis());
_items.add(item);
}
public synchronized void removeItems()
{
if (_items.isEmpty())
{
return;
}
long curtime = System.currentTimeMillis();
for (L2ItemInstance item : _items)
{
if ((item == null) || (item.getDropTime() == 0) || (item.getItemLocation() != ItemLocation.VOID))
{
_items.remove(item);
}
else
{
if (item.getItem().getAutoDestroyTime() > 0)
{
if ((curtime - item.getDropTime()) > item.getItem().getAutoDestroyTime())
{
L2World.getInstance().removeVisibleObject(item, item.getWorldRegion());
L2World.getInstance().removeObject(item);
_items.remove(item);
if (Config.SAVE_DROPPED_ITEM)
{
ItemsOnGroundManager.getInstance().removeObject(item);
}
}
}
else if (item.getItem().hasExImmediateEffect())
{
if ((curtime - item.getDropTime()) > Config.HERB_AUTO_DESTROY_TIME)
{
L2World.getInstance().removeVisibleObject(item, item.getWorldRegion());
L2World.getInstance().removeObject(item);
_items.remove(item);
if (Config.SAVE_DROPPED_ITEM)
{
ItemsOnGroundManager.getInstance().removeObject(item);
}
}
}
else
{
final long sleep = ((Config.AUTODESTROY_ITEM_AFTER == 0) ? 3600000 : Config.AUTODESTROY_ITEM_AFTER * 1000);
if ((curtime - item.getDropTime()) > sleep)
{
L2World.getInstance().removeVisibleObject(item, item.getWorldRegion());
L2World.getInstance().removeObject(item);
_items.remove(item);
if (Config.SAVE_DROPPED_ITEM)
{
ItemsOnGroundManager.getInstance().removeObject(item);
}
}
}
}
}
}
private static class SingletonHolder
{
protected static final ItemsAutoDestroy _instance = new ItemsAutoDestroy();
}
}

View File

@@ -0,0 +1,814 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAKeyGenParameterSpec;
import java.security.spec.RSAPublicKeySpec;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import javolution.util.FastList;
import javolution.util.FastMap;
import com.l2jserver.Config;
import com.l2jserver.L2DatabaseFactory;
import com.l2jserver.gameserver.model.L2World;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.network.L2GameClient;
import com.l2jserver.gameserver.network.L2GameClient.GameClientState;
import com.l2jserver.gameserver.network.SystemMessageId;
import com.l2jserver.gameserver.network.gameserverpackets.AuthRequest;
import com.l2jserver.gameserver.network.gameserverpackets.BlowFishKey;
import com.l2jserver.gameserver.network.gameserverpackets.ChangeAccessLevel;
import com.l2jserver.gameserver.network.gameserverpackets.ChangePassword;
import com.l2jserver.gameserver.network.gameserverpackets.PlayerAuthRequest;
import com.l2jserver.gameserver.network.gameserverpackets.PlayerInGame;
import com.l2jserver.gameserver.network.gameserverpackets.PlayerLogout;
import com.l2jserver.gameserver.network.gameserverpackets.PlayerTracert;
import com.l2jserver.gameserver.network.gameserverpackets.ReplyCharacters;
import com.l2jserver.gameserver.network.gameserverpackets.SendMail;
import com.l2jserver.gameserver.network.gameserverpackets.ServerStatus;
import com.l2jserver.gameserver.network.gameserverpackets.TempBan;
import com.l2jserver.gameserver.network.loginserverpackets.AuthResponse;
import com.l2jserver.gameserver.network.loginserverpackets.ChangePasswordResponse;
import com.l2jserver.gameserver.network.loginserverpackets.InitLS;
import com.l2jserver.gameserver.network.loginserverpackets.KickPlayer;
import com.l2jserver.gameserver.network.loginserverpackets.LoginServerFail;
import com.l2jserver.gameserver.network.loginserverpackets.PlayerAuthResponse;
import com.l2jserver.gameserver.network.loginserverpackets.RequestCharacters;
import com.l2jserver.gameserver.network.serverpackets.CharSelectionInfo;
import com.l2jserver.gameserver.network.serverpackets.LoginFail;
import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
import com.l2jserver.util.Util;
import com.l2jserver.util.crypt.NewCrypt;
import com.l2jserver.util.network.BaseSendablePacket;
public class LoginServerThread extends Thread
{
protected static final Logger _log = Logger.getLogger(LoginServerThread.class.getName());
protected static final Logger _logAccounting = Logger.getLogger("accounting");
/** @see com.l2jserver.loginserver.L2LoginServer#PROTOCOL_REV */
private static final int REVISION = 0x0106;
private final String _hostname;
private final int _port;
private final int _gamePort;
private Socket _loginSocket;
private OutputStream _out;
/**
* The BlowFish engine used to encrypt packets<br>
* It is first initialized with a unified key:<br>
* "_;v.]05-31!|+-%xT!^[$\00"<br>
* <br>
* and then after handshake, with a new key sent by<br>
* login server during the handshake. This new key is stored<br>
* in blowfishKey
*/
private NewCrypt _blowfish;
private byte[] _hexID;
private final boolean _acceptAlternate;
private int _requestID;
private final boolean _reserveHost;
private int _maxPlayer;
private final List<WaitingClient> _waitingClients;
private final FastMap<String, L2GameClient> _accountsInGameServer = new FastMap<>();
private int _status;
private String _serverName;
private final List<String> _subnets;
private final List<String> _hosts;
/**
* Instantiates a new login server thread.
*/
protected LoginServerThread()
{
super("LoginServerThread");
_port = Config.GAME_SERVER_LOGIN_PORT;
_gamePort = Config.PORT_GAME;
_hostname = Config.GAME_SERVER_LOGIN_HOST;
_hexID = Config.HEX_ID;
if (_hexID == null)
{
_requestID = Config.REQUEST_ID;
_hexID = Util.generateHex(16);
}
else
{
_requestID = Config.SERVER_ID;
}
_acceptAlternate = Config.ACCEPT_ALTERNATE_ID;
_reserveHost = Config.RESERVE_HOST_ON_LOGIN;
_subnets = Config.GAME_SERVER_SUBNETS;
_hosts = Config.GAME_SERVER_HOSTS;
_waitingClients = new FastList<>();
_accountsInGameServer.shared();
_maxPlayer = Config.MAXIMUM_ONLINE_USERS;
}
/**
* Gets the single instance of LoginServerThread.
* @return single instance of LoginServerThread
*/
public static LoginServerThread getInstance()
{
return SingletonHolder._instance;
}
@Override
public void run()
{
while (!isInterrupted())
{
int lengthHi = 0;
int lengthLo = 0;
int length = 0;
boolean checksumOk = false;
try
{
// Connection
_log.info("Connecting to login on " + _hostname + ":" + _port);
_loginSocket = new Socket(_hostname, _port);
InputStream in = _loginSocket.getInputStream();
_out = new BufferedOutputStream(_loginSocket.getOutputStream());
// init Blowfish
byte[] blowfishKey = Util.generateHex(40);
_blowfish = new NewCrypt("_;v.]05-31!|+-%xT!^[$\00");
while (!isInterrupted())
{
lengthLo = in.read();
lengthHi = in.read();
length = (lengthHi * 256) + lengthLo;
if (lengthHi < 0)
{
_log.finer("LoginServerThread: Login terminated the connection.");
break;
}
byte[] incoming = new byte[length - 2];
int receivedBytes = 0;
int newBytes = 0;
int left = length - 2;
while ((newBytes != -1) && (receivedBytes < (length - 2)))
{
newBytes = in.read(incoming, receivedBytes, left);
receivedBytes = receivedBytes + newBytes;
left -= newBytes;
}
if (receivedBytes != (length - 2))
{
_log.warning("Incomplete Packet is sent to the server, closing connection.(LS)");
break;
}
// decrypt if we have a key
_blowfish.decrypt(incoming, 0, incoming.length);
checksumOk = NewCrypt.verifyChecksum(incoming);
if (!checksumOk)
{
_log.warning("Incorrect packet checksum, ignoring packet (LS)");
break;
}
int packetType = incoming[0] & 0xff;
switch (packetType)
{
case 0x00:
InitLS init = new InitLS(incoming);
if (init.getRevision() != REVISION)
{
// TODO: revision mismatch
_log.warning("/!\\ Revision mismatch between LS and GS /!\\");
break;
}
RSAPublicKey publicKey;
try
{
KeyFactory kfac = KeyFactory.getInstance("RSA");
BigInteger modulus = new BigInteger(init.getRSAKey());
RSAPublicKeySpec kspec1 = new RSAPublicKeySpec(modulus, RSAKeyGenParameterSpec.F4);
publicKey = (RSAPublicKey) kfac.generatePublic(kspec1);
}
catch (GeneralSecurityException e)
{
_log.warning("Trouble while init the public key send by login");
break;
}
// send the blowfish key through the rsa encryption
sendPacket(new BlowFishKey(blowfishKey, publicKey));
// now, only accept packet with the new encryption
_blowfish = new NewCrypt(blowfishKey);
sendPacket(new AuthRequest(_requestID, _acceptAlternate, _hexID, _gamePort, _reserveHost, _maxPlayer, _subnets, _hosts));
break;
case 0x01:
LoginServerFail lsf = new LoginServerFail(incoming);
_log.info("Damn! Registeration Failed: " + lsf.getReasonString());
// login will close the connection here
break;
case 0x02:
AuthResponse aresp = new AuthResponse(incoming);
int serverID = aresp.getServerId();
_serverName = aresp.getServerName();
Config.saveHexid(serverID, hexToString(_hexID));
_log.info("Registered on login as Server " + serverID + " : " + _serverName);
ServerStatus st = new ServerStatus();
if (Config.SERVER_LIST_BRACKET)
{
st.addAttribute(ServerStatus.SERVER_LIST_SQUARE_BRACKET, ServerStatus.ON);
}
else
{
st.addAttribute(ServerStatus.SERVER_LIST_SQUARE_BRACKET, ServerStatus.OFF);
}
st.addAttribute(ServerStatus.SERVER_TYPE, Config.SERVER_LIST_TYPE);
if (Config.SERVER_GMONLY)
{
st.addAttribute(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_GM_ONLY);
}
else
{
st.addAttribute(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_AUTO);
}
if (Config.SERVER_LIST_AGE == 15)
{
st.addAttribute(ServerStatus.SERVER_AGE, ServerStatus.SERVER_AGE_15);
}
else if (Config.SERVER_LIST_AGE == 18)
{
st.addAttribute(ServerStatus.SERVER_AGE, ServerStatus.SERVER_AGE_18);
}
else
{
st.addAttribute(ServerStatus.SERVER_AGE, ServerStatus.SERVER_AGE_ALL);
}
sendPacket(st);
if (L2World.getInstance().getAllPlayersCount() > 0)
{
final List<String> playerList = new ArrayList<>();
for (L2PcInstance player : L2World.getInstance().getPlayers())
{
playerList.add(player.getAccountName());
}
sendPacket(new PlayerInGame(playerList));
}
break;
case 0x03:
PlayerAuthResponse par = new PlayerAuthResponse(incoming);
String account = par.getAccount();
WaitingClient wcToRemove = null;
synchronized (_waitingClients)
{
for (WaitingClient wc : _waitingClients)
{
if (wc.account.equals(account))
{
wcToRemove = wc;
}
}
}
if (wcToRemove != null)
{
if (par.isAuthed())
{
PlayerInGame pig = new PlayerInGame(par.getAccount());
sendPacket(pig);
wcToRemove.gameClient.setState(GameClientState.AUTHED);
wcToRemove.gameClient.setSessionId(wcToRemove.session);
CharSelectionInfo cl = new CharSelectionInfo(wcToRemove.account, wcToRemove.gameClient.getSessionId().playOkID1);
wcToRemove.gameClient.getConnection().sendPacket(cl);
wcToRemove.gameClient.setCharSelection(cl.getCharInfo());
}
else
{
_log.warning("Session key is not correct. Closing connection for account " + wcToRemove.account + ".");
// wcToRemove.gameClient.getConnection().sendPacket(new LoginFail(LoginFail.SYSTEM_ERROR_LOGIN_LATER));
wcToRemove.gameClient.close(new LoginFail(LoginFail.SYSTEM_ERROR_LOGIN_LATER));
_accountsInGameServer.remove(wcToRemove.account);
}
_waitingClients.remove(wcToRemove);
}
break;
case 0x04:
KickPlayer kp = new KickPlayer(incoming);
doKickPlayer(kp.getAccount());
break;
case 0x05:
RequestCharacters rc = new RequestCharacters(incoming);
getCharsOnServer(rc.getAccount());
break;
case 0x06:
new ChangePasswordResponse(incoming);
break;
}
}
}
catch (UnknownHostException e)
{
_log.log(Level.WARNING, "", e);
}
catch (SocketException e)
{
_log.warning("LoginServer not avaible, trying to reconnect...");
}
catch (IOException e)
{
_log.log(Level.WARNING, "Disconnected from Login, Trying to reconnect: " + e.getMessage(), e);
}
finally
{
try
{
_loginSocket.close();
if (isInterrupted())
{
return;
}
}
catch (Exception e)
{
}
}
try
{
Thread.sleep(5000); // 5 seconds tempo.
}
catch (InterruptedException e)
{
return; // never swallow an interrupt!
}
}
}
/**
* Adds the waiting client and send request.
* @param acc the account
* @param client the game client
* @param key the session key
*/
public void addWaitingClientAndSendRequest(String acc, L2GameClient client, SessionKey key)
{
WaitingClient wc = new WaitingClient(acc, client, key);
synchronized (_waitingClients)
{
_waitingClients.add(wc);
}
PlayerAuthRequest par = new PlayerAuthRequest(acc, key);
try
{
sendPacket(par);
}
catch (IOException e)
{
_log.warning("Error while sending player auth request");
}
}
/**
* Removes the waiting client.
* @param client the client
*/
public void removeWaitingClient(L2GameClient client)
{
WaitingClient toRemove = null;
synchronized (_waitingClients)
{
for (WaitingClient c : _waitingClients)
{
if (c.gameClient == client)
{
toRemove = c;
}
}
if (toRemove != null)
{
_waitingClients.remove(toRemove);
}
}
}
/**
* Send logout for the given account.
* @param account the account
*/
public void sendLogout(String account)
{
if (account == null)
{
return;
}
PlayerLogout pl = new PlayerLogout(account);
try
{
sendPacket(pl);
}
catch (IOException e)
{
_log.warning("Error while sending logout packet to login");
}
finally
{
_accountsInGameServer.remove(account);
}
}
/**
* Adds the game server login.
* @param account the account
* @param client the client
* @return {@code true} if account was not already logged in, {@code false} otherwise
*/
public boolean addGameServerLogin(String account, L2GameClient client)
{
return _accountsInGameServer.putIfAbsent(account, client) == null;
}
/**
* Send access level.
* @param account the account
* @param level the access level
*/
public void sendAccessLevel(String account, int level)
{
ChangeAccessLevel cal = new ChangeAccessLevel(account, level);
try
{
sendPacket(cal);
}
catch (IOException e)
{
}
}
/**
* Send client tracert.
* @param account the account
* @param address the address
*/
public void sendClientTracert(String account, String[] address)
{
PlayerTracert ptc = new PlayerTracert(account, address[0], address[1], address[2], address[3], address[4]);
try
{
sendPacket(ptc);
}
catch (IOException e)
{
}
}
/**
* Send mail.
* @param account the account
* @param mailId the mail id
* @param args the args
*/
public void sendMail(String account, String mailId, String... args)
{
SendMail sem = new SendMail(account, mailId, args);
try
{
sendPacket(sem);
}
catch (IOException e)
{
}
}
/**
* Send temp ban.
* @param account the account
* @param ip the ip
* @param time the time
*/
public void sendTempBan(String account, String ip, long time)
{
TempBan tbn = new TempBan(account, ip, time);
try
{
sendPacket(tbn);
}
catch (IOException e)
{
}
}
/**
* Hex to string.
* @param hex the hex value
* @return the hex value as string
*/
private String hexToString(byte[] hex)
{
return new BigInteger(hex).toString(16);
}
/**
* Kick player for the given account.
* @param account the account
*/
public void doKickPlayer(String account)
{
L2GameClient client = _accountsInGameServer.get(account);
if (client != null)
{
LogRecord record = new LogRecord(Level.WARNING, "Kicked by login");
record.setParameters(new Object[]
{
client
});
_logAccounting.log(record);
client.setAditionalClosePacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_ARE_LOGGED_IN_TO_TWO_PLACES_IF_YOU_SUSPECT_ACCOUNT_THEFT_WE_RECOMMEND_CHANGING_YOUR_PASSWORD_SCANNING_YOUR_COMPUTER_FOR_VIRUSES_AND_USING_AN_ANTI_VIRUS_SOFTWARE));
client.closeNow();
}
}
/**
* Gets the chars on server.
* @param account the account
*/
private void getCharsOnServer(String account)
{
int chars = 0;
List<Long> charToDel = new ArrayList<>();
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement ps = con.prepareStatement("SELECT deletetime FROM characters WHERE account_name=?"))
{
ps.setString(1, account);
try (ResultSet rs = ps.executeQuery())
{
while (rs.next())
{
chars++;
long delTime = rs.getLong("deletetime");
if (delTime != 0)
{
charToDel.add(delTime);
}
}
}
}
catch (SQLException e)
{
_log.log(Level.WARNING, "Exception: getCharsOnServer: " + e.getMessage(), e);
}
ReplyCharacters rec = new ReplyCharacters(account, chars, charToDel);
try
{
sendPacket(rec);
}
catch (IOException e)
{
}
}
/**
* Send packet.
* @param sl the sendable packet
* @throws IOException Signals that an I/O exception has occurred.
*/
private void sendPacket(BaseSendablePacket sl) throws IOException
{
byte[] data = sl.getContent();
NewCrypt.appendChecksum(data);
_blowfish.crypt(data, 0, data.length);
int len = data.length + 2;
synchronized (_out) // avoids tow threads writing in the mean time
{
_out.write(len & 0xff);
_out.write((len >> 8) & 0xff);
_out.write(data);
_out.flush();
}
}
/**
* Sets the max player.
* @param maxPlayer The maxPlayer to set.
*/
public void setMaxPlayer(int maxPlayer)
{
sendServerStatus(ServerStatus.MAX_PLAYERS, maxPlayer);
_maxPlayer = maxPlayer;
}
/**
* Gets the max player.
* @return Returns the maxPlayer.
*/
public int getMaxPlayer()
{
return _maxPlayer;
}
/**
* Send server status.
* @param id the id
* @param value the value
*/
public void sendServerStatus(int id, int value)
{
ServerStatus ss = new ServerStatus();
ss.addAttribute(id, value);
try
{
sendPacket(ss);
}
catch (IOException e)
{
}
}
/**
* Send Server Type Config to LS.
*/
public void sendServerType()
{
ServerStatus ss = new ServerStatus();
ss.addAttribute(ServerStatus.SERVER_TYPE, Config.SERVER_LIST_TYPE);
try
{
sendPacket(ss);
}
catch (IOException e)
{
}
}
/**
* Send change password.
* @param accountName the account name
* @param charName the char name
* @param oldpass the old pass
* @param newpass the new pass
*/
public void sendChangePassword(String accountName, String charName, String oldpass, String newpass)
{
ChangePassword cp = new ChangePassword(accountName, charName, oldpass, newpass);
try
{
sendPacket(cp);
}
catch (IOException e)
{
}
}
/**
* Gets the status string.
* @return the status string
*/
public String getStatusString()
{
return ServerStatus.STATUS_STRING[_status];
}
/**
* Gets the server name.
* @return the server name.
*/
public String getServerName()
{
return _serverName;
}
/**
* Sets the server status.
* @param status the new server status
*/
public void setServerStatus(int status)
{
switch (status)
{
case ServerStatus.STATUS_AUTO:
sendServerStatus(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_AUTO);
_status = status;
break;
case ServerStatus.STATUS_DOWN:
sendServerStatus(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_DOWN);
_status = status;
break;
case ServerStatus.STATUS_FULL:
sendServerStatus(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_FULL);
_status = status;
break;
case ServerStatus.STATUS_GM_ONLY:
sendServerStatus(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_GM_ONLY);
_status = status;
break;
case ServerStatus.STATUS_GOOD:
sendServerStatus(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_GOOD);
_status = status;
break;
case ServerStatus.STATUS_NORMAL:
sendServerStatus(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_NORMAL);
_status = status;
break;
default:
throw new IllegalArgumentException("Status does not exists:" + status);
}
}
public L2GameClient getClient(String name)
{
return name != null ? _accountsInGameServer.get(name) : null;
}
public static class SessionKey
{
public int playOkID1;
public int playOkID2;
public int loginOkID1;
public int loginOkID2;
/**
* Instantiates a new session key.
* @param loginOK1 the login o k1
* @param loginOK2 the login o k2
* @param playOK1 the play o k1
* @param playOK2 the play o k2
*/
public SessionKey(int loginOK1, int loginOK2, int playOK1, int playOK2)
{
playOkID1 = playOK1;
playOkID2 = playOK2;
loginOkID1 = loginOK1;
loginOkID2 = loginOK2;
}
@Override
public String toString()
{
return "PlayOk: " + playOkID1 + " " + playOkID2 + " LoginOk:" + loginOkID1 + " " + loginOkID2;
}
}
private static class WaitingClient
{
public String account;
public L2GameClient gameClient;
public SessionKey session;
/**
* Instantiates a new waiting client.
* @param acc the acc
* @param client the client
* @param key the key
*/
public WaitingClient(String acc, L2GameClient client, SessionKey key)
{
account = acc;
gameClient = client;
session = key;
}
}
private static class SingletonHolder
{
protected static final LoginServerThread _instance = new LoginServerThread();
}
}

View File

@@ -0,0 +1,154 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver;
import java.lang.reflect.Constructor;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jserver.gameserver.datatables.NpcData;
import com.l2jserver.gameserver.idfactory.IdFactory;
import com.l2jserver.gameserver.model.actor.L2Npc;
import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate;
import com.l2jserver.util.Rnd;
public class MonsterRace
{
protected static final Logger _log = Logger.getLogger(MonsterRace.class.getName());
private final L2Npc[] _monsters;
private int[][] _speeds;
private final int[] _first, _second;
protected MonsterRace()
{
_monsters = new L2Npc[8];
_speeds = new int[8][20];
_first = new int[2];
_second = new int[2];
}
public static MonsterRace getInstance()
{
return SingletonHolder._instance;
}
public void newRace()
{
int random = 0;
for (int i = 0; i < 8; i++)
{
int id = 31003;
random = Rnd.get(24);
while (true)
{
for (int j = i - 1; j >= 0; j--)
{
if (_monsters[j].getTemplate().getId() == (id + random))
{
random = Rnd.get(24);
continue;
}
}
break;
}
try
{
L2NpcTemplate template = NpcData.getInstance().getTemplate(id + random);
Constructor<?> constructor = Class.forName("com.l2jserver.gameserver.model.actor.instance." + template.getType() + "Instance").getConstructors()[0];
int objectId = IdFactory.getInstance().getNextId();
_monsters[i] = (L2Npc) constructor.newInstance(objectId, template);
}
catch (Exception e)
{
_log.log(Level.WARNING, "", e);
}
// _log.info("Monster "+i+" is id: "+(id+random));
}
newSpeeds();
}
public void newSpeeds()
{
_speeds = new int[8][20];
int total = 0;
_first[1] = 0;
_second[1] = 0;
for (int i = 0; i < 8; i++)
{
total = 0;
for (int j = 0; j < 20; j++)
{
if (j == 19)
{
_speeds[i][j] = 100;
}
else
{
_speeds[i][j] = Rnd.get(60) + 65;
}
total += _speeds[i][j];
}
if (total >= _first[1])
{
_second[0] = _first[0];
_second[1] = _first[1];
_first[0] = 8 - i;
_first[1] = total;
}
else if (total >= _second[1])
{
_second[0] = 8 - i;
_second[1] = total;
}
}
}
/**
* @return Returns the monsters.
*/
public L2Npc[] getMonsters()
{
return _monsters;
}
/**
* @return Returns the speeds.
*/
public int[][] getSpeeds()
{
return _speeds;
}
public int getFirstPlace()
{
return _first[0];
}
public int getSecondPlace()
{
return _second[0];
}
private static class SingletonHolder
{
protected static final MonsterRace _instance = new MonsterRace();
}
}

View File

@@ -0,0 +1,784 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Logger;
import javolution.util.FastMap;
import com.l2jserver.Config;
import com.l2jserver.gameserver.datatables.ItemTable;
import com.l2jserver.gameserver.datatables.RecipeData;
import com.l2jserver.gameserver.enums.StatType;
import com.l2jserver.gameserver.model.L2ManufactureItem;
import com.l2jserver.gameserver.model.L2RecipeInstance;
import com.l2jserver.gameserver.model.L2RecipeList;
import com.l2jserver.gameserver.model.L2RecipeStatInstance;
import com.l2jserver.gameserver.model.TempItem;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.itemcontainer.Inventory;
import com.l2jserver.gameserver.model.items.L2Item;
import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
import com.l2jserver.gameserver.model.skills.CommonSkill;
import com.l2jserver.gameserver.model.skills.Skill;
import com.l2jserver.gameserver.model.stats.Stats;
import com.l2jserver.gameserver.network.SystemMessageId;
import com.l2jserver.gameserver.network.serverpackets.ActionFailed;
import com.l2jserver.gameserver.network.serverpackets.ExUserInfoInvenWeight;
import com.l2jserver.gameserver.network.serverpackets.ItemList;
import com.l2jserver.gameserver.network.serverpackets.MagicSkillUse;
import com.l2jserver.gameserver.network.serverpackets.RecipeBookItemList;
import com.l2jserver.gameserver.network.serverpackets.RecipeItemMakeInfo;
import com.l2jserver.gameserver.network.serverpackets.RecipeShopItemInfo;
import com.l2jserver.gameserver.network.serverpackets.SetupGauge;
import com.l2jserver.gameserver.network.serverpackets.StatusUpdate;
import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
import com.l2jserver.gameserver.util.Util;
import com.l2jserver.util.Rnd;
public class RecipeController
{
protected static final FastMap<Integer, RecipeItemMaker> _activeMakers = new FastMap<>();
protected RecipeController()
{
_activeMakers.shared();
}
public void requestBookOpen(L2PcInstance player, boolean isDwarvenCraft)
{
// Check if player is trying to alter recipe book while engaged in manufacturing.
if (!_activeMakers.containsKey(player.getObjectId()))
{
RecipeBookItemList response = new RecipeBookItemList(isDwarvenCraft, player.getMaxMp());
response.addRecipes(isDwarvenCraft ? player.getDwarvenRecipeBook() : player.getCommonRecipeBook());
player.sendPacket(response);
return;
}
player.sendPacket(SystemMessageId.YOU_MAY_NOT_ALTER_YOUR_RECIPE_BOOK_WHILE_ENGAGED_IN_MANUFACTURING);
}
public void requestMakeItemAbort(L2PcInstance player)
{
_activeMakers.remove(player.getObjectId()); // TODO: anything else here?
}
public void requestManufactureItem(L2PcInstance manufacturer, int recipeListId, L2PcInstance player)
{
final L2RecipeList recipeList = RecipeData.getInstance().getValidRecipeList(player, recipeListId);
if (recipeList == null)
{
return;
}
List<L2RecipeList> dwarfRecipes = Arrays.asList(manufacturer.getDwarvenRecipeBook());
List<L2RecipeList> commonRecipes = Arrays.asList(manufacturer.getCommonRecipeBook());
if (!dwarfRecipes.contains(recipeList) && !commonRecipes.contains(recipeList))
{
Util.handleIllegalPlayerAction(player, "Warning!! Character " + player.getName() + " of account " + player.getAccountName() + " sent a false recipe id.", Config.DEFAULT_PUNISH);
return;
}
// Check if manufacturer is under manufacturing store or private store.
if (Config.ALT_GAME_CREATION && _activeMakers.containsKey(manufacturer.getObjectId()))
{
player.sendPacket(SystemMessageId.PLEASE_CLOSE_THE_SETUP_WINDOW_FOR_YOUR_PRIVATE_WORKSHOP_OR_PRIVATE_STORE_AND_TRY_AGAIN);
return;
}
final RecipeItemMaker maker = new RecipeItemMaker(manufacturer, recipeList, player);
if (maker._isValid)
{
if (Config.ALT_GAME_CREATION)
{
_activeMakers.put(manufacturer.getObjectId(), maker);
ThreadPoolManager.getInstance().scheduleGeneral(maker, 100);
}
else
{
maker.run();
}
}
}
public void requestMakeItem(L2PcInstance player, int recipeListId)
{
// Check if player is trying to operate a private store or private workshop while engaged in combat.
if (player.isInCombat() || player.isInDuel())
{
player.sendPacket(SystemMessageId.WHILE_YOU_ARE_ENGAGED_IN_COMBAT_YOU_CANNOT_OPERATE_A_PRIVATE_STORE_OR_PRIVATE_WORKSHOP);
return;
}
final L2RecipeList recipeList = RecipeData.getInstance().getValidRecipeList(player, recipeListId);
if (recipeList == null)
{
return;
}
List<L2RecipeList> dwarfRecipes = Arrays.asList(player.getDwarvenRecipeBook());
List<L2RecipeList> commonRecipes = Arrays.asList(player.getCommonRecipeBook());
if (!dwarfRecipes.contains(recipeList) && !commonRecipes.contains(recipeList))
{
Util.handleIllegalPlayerAction(player, "Warning!! Character " + player.getName() + " of account " + player.getAccountName() + " sent a false recipe id.", Config.DEFAULT_PUNISH);
return;
}
// Check if player is busy (possible if alt game creation is enabled)
if (Config.ALT_GAME_CREATION && _activeMakers.containsKey(player.getObjectId()))
{
SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1);
sm.addItemName(recipeList.getItemId());
sm.addString("You are busy creating.");
player.sendPacket(sm);
return;
}
final RecipeItemMaker maker = new RecipeItemMaker(player, recipeList, player);
if (maker._isValid)
{
if (Config.ALT_GAME_CREATION)
{
_activeMakers.put(player.getObjectId(), maker);
ThreadPoolManager.getInstance().scheduleGeneral(maker, 100);
}
else
{
maker.run();
}
}
}
private static class RecipeItemMaker implements Runnable
{
private static final Logger _log = Logger.getLogger(RecipeItemMaker.class.getName());
protected boolean _isValid;
protected List<TempItem> _items = null;
protected final L2RecipeList _recipeList;
protected final L2PcInstance _player; // "crafter"
protected final L2PcInstance _target; // "customer"
protected final Skill _skill;
protected final int _skillId;
protected final int _skillLevel;
protected int _creationPasses = 1;
protected int _itemGrab;
protected int _exp = -1;
protected int _sp = -1;
protected long _price;
protected int _totalItems;
protected int _delay;
public RecipeItemMaker(L2PcInstance pPlayer, L2RecipeList pRecipeList, L2PcInstance pTarget)
{
_player = pPlayer;
_target = pTarget;
_recipeList = pRecipeList;
_isValid = false;
_skillId = _recipeList.isDwarvenRecipe() ? CommonSkill.CREATE_DWARVEN.getId() : CommonSkill.CREATE_COMMON.getId();
_skillLevel = _player.getSkillLevel(_skillId);
_skill = _player.getKnownSkill(_skillId);
_player.isInCraftMode(true);
if (_player.isAlikeDead())
{
_player.sendPacket(ActionFailed.STATIC_PACKET);
abort();
return;
}
if (_target.isAlikeDead())
{
_target.sendPacket(ActionFailed.STATIC_PACKET);
abort();
return;
}
if (_target.isProcessingTransaction())
{
_target.sendPacket(ActionFailed.STATIC_PACKET);
abort();
return;
}
if (_player.isProcessingTransaction())
{
_player.sendPacket(ActionFailed.STATIC_PACKET);
abort();
return;
}
// validate recipe list
if (_recipeList.getRecipes().length == 0)
{
_player.sendPacket(ActionFailed.STATIC_PACKET);
abort();
return;
}
// validate skill level
if (_recipeList.getLevel() > _skillLevel)
{
_player.sendPacket(ActionFailed.STATIC_PACKET);
abort();
return;
}
// check that customer can afford to pay for creation services
if (_player != _target)
{
final L2ManufactureItem item = _player.getManufactureItems().get(_recipeList.getId());
if (item != null)
{
_price = item.getCost();
if (_target.getAdena() < _price) // check price
{
_target.sendPacket(SystemMessageId.YOU_DO_NOT_HAVE_ENOUGH_ADENA);
abort();
return;
}
}
}
// make temporary items
_items = listItems(false);
if (_items == null)
{
abort();
return;
}
for (TempItem i : _items)
{
_totalItems += i.getQuantity();
}
// initial statUse checks
if (!calculateStatUse(false, false))
{
abort();
return;
}
// initial AltStatChange checks
if (Config.ALT_GAME_CREATION)
{
calculateAltStatChange();
}
updateMakeInfo(true);
updateCurMp();
updateCurLoad();
_player.isInCraftMode(false);
_isValid = true;
}
@Override
public void run()
{
if (!Config.IS_CRAFTING_ENABLED)
{
_target.sendMessage("Item creation is currently disabled.");
abort();
return;
}
if ((_player == null) || (_target == null))
{
_log.warning("player or target == null (disconnected?), aborting" + _target + _player);
abort();
return;
}
if (!_player.isOnline() || !_target.isOnline())
{
_log.warning("player or target is not online, aborting " + _target + _player);
abort();
return;
}
if (Config.ALT_GAME_CREATION && !_activeMakers.containsKey(_player.getObjectId()))
{
if (_target != _player)
{
_target.sendMessage("Manufacture aborted");
_player.sendMessage("Manufacture aborted");
}
else
{
_player.sendMessage("Item creation aborted");
}
abort();
return;
}
if (Config.ALT_GAME_CREATION && !_items.isEmpty())
{
if (!calculateStatUse(true, true))
{
return; // check stat use
}
updateCurMp(); // update craft window mp bar
grabSomeItems(); // grab (equip) some more items with a nice msg to player
// if still not empty, schedule another pass
if (!_items.isEmpty())
{
_delay = (int) (Config.ALT_GAME_CREATION_SPEED * _player.getMReuseRate(_skill) * GameTimeController.TICKS_PER_SECOND * GameTimeController.MILLIS_IN_TICK);
// FIXME: please fix this packet to show crafting animation (somebody)
MagicSkillUse msk = new MagicSkillUse(_player, _skillId, _skillLevel, _delay, 0);
_player.broadcastPacket(msk);
_player.sendPacket(new SetupGauge(0, _delay));
ThreadPoolManager.getInstance().scheduleGeneral(this, 100 + _delay);
}
else
{
// for alt mode, sleep delay msec before finishing
_player.sendPacket(new SetupGauge(0, _delay));
try
{
Thread.sleep(_delay);
}
catch (InterruptedException e)
{
}
finally
{
finishCrafting();
}
}
} // for old craft mode just finish
else
{
finishCrafting();
}
}
private void finishCrafting()
{
if (!Config.ALT_GAME_CREATION)
{
calculateStatUse(false, true);
}
// first take adena for manufacture
if ((_target != _player) && (_price > 0)) // customer must pay for services
{
// attempt to pay for item
L2ItemInstance adenatransfer = _target.transferItem("PayManufacture", _target.getInventory().getAdenaInstance().getObjectId(), _price, _player.getInventory(), _player);
if (adenatransfer == null)
{
_target.sendPacket(SystemMessageId.YOU_DO_NOT_HAVE_ENOUGH_ADENA);
abort();
return;
}
}
_items = listItems(true); // this line actually takes materials from inventory
if (_items == null)
{
// handle possible cheaters here
// (they click craft then try to get rid of items in order to get free craft)
}
else if (Rnd.get(100) < _recipeList.getSuccessRate())
{
rewardPlayer(); // and immediately puts created item in its place
updateMakeInfo(true);
}
else
{
if (_target != _player)
{
SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.YOU_FAILED_TO_CREATE_S2_FOR_C1_AT_THE_PRICE_OF_S3_ADENA);
msg.addString(_target.getName());
msg.addItemName(_recipeList.getItemId());
msg.addLong(_price);
_player.sendPacket(msg);
msg = SystemMessage.getSystemMessage(SystemMessageId.C1_HAS_FAILED_TO_CREATE_S2_AT_THE_PRICE_OF_S3_ADENA);
msg.addString(_player.getName());
msg.addItemName(_recipeList.getItemId());
msg.addLong(_price);
_target.sendPacket(msg);
}
else
{
_target.sendPacket(SystemMessageId.YOU_FAILED_AT_MIXING_THE_ITEM);
}
updateMakeInfo(false);
}
// update load and mana bar of craft window
updateCurMp();
updateCurLoad();
_activeMakers.remove(_player.getObjectId());
_player.isInCraftMode(false);
_target.sendPacket(new ItemList(_target, false));
}
private void updateMakeInfo(boolean success)
{
if (_target == _player)
{
_target.sendPacket(new RecipeItemMakeInfo(_recipeList.getId(), _target, success));
}
else
{
_target.sendPacket(new RecipeShopItemInfo(_player, _recipeList.getId()));
}
}
private void updateCurLoad()
{
_target.sendPacket(new ExUserInfoInvenWeight(_target));
}
private void updateCurMp()
{
StatusUpdate su = new StatusUpdate(_target);
su.addAttribute(StatusUpdate.CUR_MP, (int) _target.getCurrentMp());
_target.sendPacket(su);
}
private void grabSomeItems()
{
int grabItems = _itemGrab;
while ((grabItems > 0) && !_items.isEmpty())
{
TempItem item = _items.get(0);
int count = item.getQuantity();
if (count >= grabItems)
{
count = grabItems;
}
item.setQuantity(item.getQuantity() - count);
if (item.getQuantity() <= 0)
{
_items.remove(0);
}
else
{
_items.set(0, item);
}
grabItems -= count;
if (_target == _player)
{
SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.EQUIPPED_S1_S2); // you equipped ...
sm.addLong(count);
sm.addItemName(item.getItemId());
_player.sendPacket(sm);
}
else
{
_target.sendMessage("Manufacturer " + _player.getName() + " used " + count + " " + item.getItemName());
}
}
}
// AltStatChange parameters make their effect here
private void calculateAltStatChange()
{
_itemGrab = _skillLevel;
for (L2RecipeStatInstance altStatChange : _recipeList.getAltStatChange())
{
if (altStatChange.getType() == StatType.XP)
{
_exp = altStatChange.getValue();
}
else if (altStatChange.getType() == StatType.SP)
{
_sp = altStatChange.getValue();
}
else if (altStatChange.getType() == StatType.GIM)
{
_itemGrab *= altStatChange.getValue();
}
}
// determine number of creation passes needed
_creationPasses = (_totalItems / _itemGrab) + ((_totalItems % _itemGrab) != 0 ? 1 : 0);
if (_creationPasses < 1)
{
_creationPasses = 1;
}
}
// StatUse
private boolean calculateStatUse(boolean isWait, boolean isReduce)
{
boolean ret = true;
for (L2RecipeStatInstance statUse : _recipeList.getStatUse())
{
double modifiedValue = statUse.getValue() / _creationPasses;
if (statUse.getType() == StatType.HP)
{
// we do not want to kill the player, so its CurrentHP must be greater than the reduce value
if (_player.getCurrentHp() <= modifiedValue)
{
// rest (wait for HP)
if (Config.ALT_GAME_CREATION && isWait)
{
_player.sendPacket(new SetupGauge(0, _delay));
ThreadPoolManager.getInstance().scheduleGeneral(this, 100 + _delay);
}
else
{
_target.sendPacket(SystemMessageId.NOT_ENOUGH_HP);
abort();
}
ret = false;
}
else if (isReduce)
{
_player.reduceCurrentHp(modifiedValue, _player, _skill);
}
}
else if (statUse.getType() == StatType.MP)
{
if (_player.getCurrentMp() < modifiedValue)
{
// rest (wait for MP)
if (Config.ALT_GAME_CREATION && isWait)
{
_player.sendPacket(new SetupGauge(0, _delay));
ThreadPoolManager.getInstance().scheduleGeneral(this, 100 + _delay);
}
else
{
_target.sendPacket(SystemMessageId.NOT_ENOUGH_MP);
abort();
}
ret = false;
}
else if (isReduce)
{
_player.reduceCurrentMp(modifiedValue);
}
}
else
{
// there is an unknown StatUse value
_target.sendMessage("Recipe error!!!, please tell this to your GM.");
ret = false;
abort();
}
}
return ret;
}
private List<TempItem> listItems(boolean remove)
{
L2RecipeInstance[] recipes = _recipeList.getRecipes();
Inventory inv = _target.getInventory();
List<TempItem> materials = new ArrayList<>();
SystemMessage sm;
for (L2RecipeInstance recipe : recipes)
{
if (recipe.getQuantity() > 0)
{
L2ItemInstance item = inv.getItemByItemId(recipe.getItemId());
long itemQuantityAmount = item == null ? 0 : item.getCount();
// check materials
if (itemQuantityAmount < recipe.getQuantity())
{
sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_NEED_S2_MORE_S1_S);
sm.addItemName(recipe.getItemId());
sm.addLong(recipe.getQuantity() - itemQuantityAmount);
_target.sendPacket(sm);
abort();
return null;
}
// make new temporary object, just for counting purposes
materials.add(new TempItem(item, recipe.getQuantity()));
}
}
if (remove)
{
for (TempItem tmp : materials)
{
inv.destroyItemByItemId("Manufacture", tmp.getItemId(), tmp.getQuantity(), _target, _player);
if (tmp.getQuantity() > 1)
{
sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_S_DISAPPEARED);
sm.addItemName(tmp.getItemId());
sm.addLong(tmp.getQuantity());
_target.sendPacket(sm);
}
else
{
sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED);
sm.addItemName(tmp.getItemId());
_target.sendPacket(sm);
}
}
}
return materials;
}
private void abort()
{
updateMakeInfo(false);
_player.isInCraftMode(false);
_activeMakers.remove(_player.getObjectId());
}
private void rewardPlayer()
{
int rareProdId = _recipeList.getRareItemId();
int itemId = _recipeList.getItemId();
int itemCount = _recipeList.getCount();
L2Item template = ItemTable.getInstance().getTemplate(itemId);
// check that the current recipe has a rare production or not
if ((rareProdId != -1) && ((rareProdId == itemId) || Config.CRAFT_MASTERWORK))
{
if (Rnd.get(100) < _recipeList.getRarity())
{
itemId = rareProdId;
itemCount = _recipeList.getRareCount();
}
}
_target.getInventory().addItem("Manufacture", itemId, itemCount, _target, _player);
// inform customer of earned item
SystemMessage sm = null;
if (_target != _player)
{
// inform manufacturer of earned profit
if (itemCount == 1)
{
sm = SystemMessage.getSystemMessage(SystemMessageId.S2_HAS_BEEN_CREATED_FOR_C1_AFTER_THE_PAYMENT_OF_S3_ADENA_WAS_RECEIVED);
sm.addString(_target.getName());
sm.addItemName(itemId);
sm.addLong(_price);
_player.sendPacket(sm);
sm = SystemMessage.getSystemMessage(SystemMessageId.C1_CREATED_S2_AFTER_RECEIVING_S3_ADENA);
sm.addString(_player.getName());
sm.addItemName(itemId);
sm.addLong(_price);
_target.sendPacket(sm);
}
else
{
sm = SystemMessage.getSystemMessage(SystemMessageId.S3_S2_S_HAVE_BEEN_CREATED_FOR_C1_AT_THE_PRICE_OF_S4_ADENA);
sm.addString(_target.getName());
sm.addInt(itemCount);
sm.addItemName(itemId);
sm.addLong(_price);
_player.sendPacket(sm);
sm = SystemMessage.getSystemMessage(SystemMessageId.C1_CREATED_S3_S2_S_AT_THE_PRICE_OF_S4_ADENA);
sm.addString(_player.getName());
sm.addInt(itemCount);
sm.addItemName(itemId);
sm.addLong(_price);
_target.sendPacket(sm);
}
}
if (itemCount > 1)
{
sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_EARNED_S2_S1_S);
sm.addItemName(itemId);
sm.addLong(itemCount);
_target.sendPacket(sm);
}
else
{
sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_EARNED_S1);
sm.addItemName(itemId);
_target.sendPacket(sm);
}
if (Config.ALT_GAME_CREATION)
{
int recipeLevel = _recipeList.getLevel();
if (_exp < 0)
{
_exp = template.getReferencePrice() * itemCount;
_exp /= recipeLevel;
}
if (_sp < 0)
{
_sp = _exp / 10;
}
if (itemId == rareProdId)
{
_exp *= Config.ALT_GAME_CREATION_RARE_XPSP_RATE;
_sp *= Config.ALT_GAME_CREATION_RARE_XPSP_RATE;
}
if (_exp < 0)
{
_exp = 0;
}
if (_sp < 0)
{
_sp = 0;
}
for (int i = _skillLevel; i > recipeLevel; i--)
{
_exp /= 4;
_sp /= 4;
}
// Added multiplication of Creation speed with XP/SP gain slower crafting -> more XP,
// faster crafting -> less XP you can use ALT_GAME_CREATION_XP_RATE/SP to modify XP/SP gained (default = 1)
_player.addExpAndSp((int) _player.calcStat(Stats.EXPSP_RATE, _exp * Config.ALT_GAME_CREATION_XP_RATE * Config.ALT_GAME_CREATION_SPEED, null, null), (int) _player.calcStat(Stats.EXPSP_RATE, _sp * Config.ALT_GAME_CREATION_SP_RATE * Config.ALT_GAME_CREATION_SPEED, null, null));
}
updateMakeInfo(true); // success
}
}
public static RecipeController getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final RecipeController _instance = new RecipeController();
}
}

View File

@@ -0,0 +1,640 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jserver.Config;
import com.l2jserver.L2DatabaseFactory;
import com.l2jserver.UPnPService;
import com.l2jserver.gameserver.datatables.BotReportTable;
import com.l2jserver.gameserver.datatables.ClanTable;
import com.l2jserver.gameserver.datatables.OfflineTradersTable;
import com.l2jserver.gameserver.instancemanager.CHSiegeManager;
import com.l2jserver.gameserver.instancemanager.CastleManorManager;
import com.l2jserver.gameserver.instancemanager.CursedWeaponsManager;
import com.l2jserver.gameserver.instancemanager.GlobalVariablesManager;
import com.l2jserver.gameserver.instancemanager.GrandBossManager;
import com.l2jserver.gameserver.instancemanager.ItemAuctionManager;
import com.l2jserver.gameserver.instancemanager.ItemsOnGroundManager;
import com.l2jserver.gameserver.instancemanager.QuestManager;
import com.l2jserver.gameserver.instancemanager.RaidBossSpawnManager;
import com.l2jserver.gameserver.model.L2World;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.entity.Hero;
import com.l2jserver.gameserver.model.olympiad.Olympiad;
import com.l2jserver.gameserver.network.L2GameClient;
import com.l2jserver.gameserver.network.SystemMessageId;
import com.l2jserver.gameserver.network.gameserverpackets.ServerStatus;
import com.l2jserver.gameserver.network.serverpackets.ServerClose;
import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
import com.l2jserver.gameserver.util.Broadcast;
/**
* This class provides the functions for shutting down and restarting the server.<br>
* It closes all open client connections and saves all data.
* @version $Revision: 1.2.4.5 $ $Date: 2005/03/27 15:29:09 $
*/
public class Shutdown extends Thread
{
private static final Logger _log = Logger.getLogger(Shutdown.class.getName());
private static Shutdown _counterInstance = null;
private int _secondsShut;
private int _shutdownMode;
public static final int SIGTERM = 0;
public static final int GM_SHUTDOWN = 1;
public static final int GM_RESTART = 2;
public static final int ABORT = 3;
private static final String[] MODE_TEXT =
{
"SIGTERM",
"shutting down",
"restarting",
"aborting"
};
/**
* This function starts a shutdown count down from Telnet (Copied from Function startShutdown())
* @param seconds seconds until shutdown
*/
private void SendServerQuit(int seconds)
{
SystemMessage sysm = SystemMessage.getSystemMessage(SystemMessageId.THE_SERVER_WILL_BE_COMING_DOWN_IN_S1_SECOND_S_PLEASE_FIND_A_SAFE_PLACE_TO_LOG_OUT);
sysm.addInt(seconds);
Broadcast.toAllOnlinePlayers(sysm);
}
public void startTelnetShutdown(String IP, int seconds, boolean restart)
{
_log.warning("IP: " + IP + " issued shutdown command. " + MODE_TEXT[_shutdownMode] + " in " + seconds + " seconds!");
if (restart)
{
_shutdownMode = GM_RESTART;
}
else
{
_shutdownMode = GM_SHUTDOWN;
}
if (_shutdownMode > 0)
{
switch (seconds)
{
case 540:
case 480:
case 420:
case 360:
case 300:
case 240:
case 180:
case 120:
case 60:
case 30:
case 10:
case 5:
case 4:
case 3:
case 2:
case 1:
break;
default:
SendServerQuit(seconds);
}
}
if (_counterInstance != null)
{
_counterInstance._abort();
}
_counterInstance = new Shutdown(seconds, restart);
_counterInstance.start();
}
/**
* This function aborts a running countdown
* @param IP IP Which Issued shutdown command
*/
public void telnetAbort(String IP)
{
_log.warning("IP: " + IP + " issued shutdown ABORT. " + MODE_TEXT[_shutdownMode] + " has been stopped!");
if (_counterInstance != null)
{
_counterInstance._abort();
Broadcast.toAllOnlinePlayers("Server aborts " + MODE_TEXT[_shutdownMode] + " and continues normal operation!", false);
}
}
/**
* Default constructor is only used internal to create the shutdown-hook instance
*/
protected Shutdown()
{
_secondsShut = -1;
_shutdownMode = SIGTERM;
}
/**
* This creates a countdown instance of Shutdown.
* @param seconds how many seconds until shutdown
* @param restart true is the server shall restart after shutdown
*/
public Shutdown(int seconds, boolean restart)
{
if (seconds < 0)
{
seconds = 0;
}
_secondsShut = seconds;
if (restart)
{
_shutdownMode = GM_RESTART;
}
else
{
_shutdownMode = GM_SHUTDOWN;
}
}
/**
* This function is called, when a new thread starts if this thread is the thread of getInstance, then this is the shutdown hook and we save all data and disconnect all clients.<br>
* After this thread ends, the server will completely exit if this is not the thread of getInstance, then this is a countdown thread.<br>
* We start the countdown, and when we finished it, and it was not aborted, we tell the shutdown-hook why we call exit, and then call exit when the exit status of the server is 1, startServer.sh / startServer.bat will restart the server.
*/
@Override
public void run()
{
if (this == getInstance())
{
TimeCounter tc = new TimeCounter();
TimeCounter tc1 = new TimeCounter();
try
{
UPnPService.getInstance().removeAllPorts();
_log.info("UPnP Service: All ports mappings deleted (" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
}
catch (Throwable t)
{
_log.log(Level.WARNING, "Error while removing UPnP port mappings: ", t);
}
try
{
if ((Config.OFFLINE_TRADE_ENABLE || Config.OFFLINE_CRAFT_ENABLE) && Config.RESTORE_OFFLINERS)
{
OfflineTradersTable.getInstance().storeOffliners();
_log.info("Offline Traders Table: Offline shops stored(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
}
}
catch (Throwable t)
{
_log.log(Level.WARNING, "Error saving offline shops.", t);
}
try
{
disconnectAllCharacters();
_log.info("All players disconnected and saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
}
catch (Throwable t)
{
// ignore
}
// ensure all services are stopped
try
{
GameTimeController.getInstance().stopTimer();
_log.info("Game Time Controller: Timer stopped(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
}
catch (Throwable t)
{
// ignore
}
// stop all threadpolls
try
{
ThreadPoolManager.getInstance().shutdown();
_log.info("Thread Pool Manager: Manager has been shut down(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
}
catch (Throwable t)
{
// ignore
}
try
{
LoginServerThread.getInstance().interrupt();
_log.info("Login Server Thread: Thread interruped(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
}
catch (Throwable t)
{
// ignore
}
// last byebye, save all data and quit this server
saveData();
tc.restartCounter();
// saveData sends messages to exit players, so shutdown selector after it
try
{
GameServer.gameServer.getSelectorThread().shutdown();
_log.info("Game Server: Selector thread has been shut down(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
}
catch (Throwable t)
{
// ignore
}
// commit data, last chance
try
{
L2DatabaseFactory.getInstance().shutdown();
_log.info("L2Database Factory: Database connection has been shut down(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
}
catch (Throwable t)
{
}
// server will quit, when this function ends.
if (getInstance()._shutdownMode == GM_RESTART)
{
Runtime.getRuntime().halt(2);
}
else
{
Runtime.getRuntime().halt(0);
}
_log.info("The server has been successfully shut down in " + (tc1.getEstimatedTime() / 1000) + "seconds.");
}
else
{
// gm shutdown: send warnings and then call exit to start shutdown sequence
countdown();
// last point where logging is operational :(
_log.warning("GM shutdown countdown is over. " + MODE_TEXT[_shutdownMode] + " NOW!");
switch (_shutdownMode)
{
case GM_SHUTDOWN:
getInstance().setMode(GM_SHUTDOWN);
System.exit(0);
break;
case GM_RESTART:
getInstance().setMode(GM_RESTART);
System.exit(2);
break;
case ABORT:
LoginServerThread.getInstance().setServerStatus(ServerStatus.STATUS_AUTO);
break;
}
}
}
/**
* This functions starts a shutdown countdown.
* @param activeChar GM who issued the shutdown command
* @param seconds seconds until shutdown
* @param restart true if the server will restart after shutdown
*/
public void startShutdown(L2PcInstance activeChar, int seconds, boolean restart)
{
if (restart)
{
_shutdownMode = GM_RESTART;
}
else
{
_shutdownMode = GM_SHUTDOWN;
}
_log.warning("GM: " + activeChar.getName() + "(" + activeChar.getObjectId() + ") issued shutdown command. " + MODE_TEXT[_shutdownMode] + " in " + seconds + " seconds!");
if (_shutdownMode > 0)
{
switch (seconds)
{
case 540:
case 480:
case 420:
case 360:
case 300:
case 240:
case 180:
case 120:
case 60:
case 30:
case 10:
case 5:
case 4:
case 3:
case 2:
case 1:
break;
default:
SendServerQuit(seconds);
}
}
if (_counterInstance != null)
{
_counterInstance._abort();
}
// the main instance should only run for shutdown hook, so we start a new instance
_counterInstance = new Shutdown(seconds, restart);
_counterInstance.start();
}
/**
* This function aborts a running countdown.
* @param activeChar GM who issued the abort command
*/
public void abort(L2PcInstance activeChar)
{
_log.warning("GM: " + activeChar.getName() + "(" + activeChar.getObjectId() + ") issued shutdown ABORT. " + MODE_TEXT[_shutdownMode] + " has been stopped!");
if (_counterInstance != null)
{
_counterInstance._abort();
Broadcast.toAllOnlinePlayers("Server aborts " + MODE_TEXT[_shutdownMode] + " and continues normal operation!", false);
}
}
/**
* Set the shutdown mode.
* @param mode what mode shall be set
*/
private void setMode(int mode)
{
_shutdownMode = mode;
}
/**
* Set shutdown mode to ABORT.
*/
private void _abort()
{
_shutdownMode = ABORT;
}
/**
* This counts the countdown and reports it to all players countdown is aborted if mode changes to ABORT.
*/
private void countdown()
{
try
{
while (_secondsShut > 0)
{
switch (_secondsShut)
{
case 540:
SendServerQuit(540);
break;
case 480:
SendServerQuit(480);
break;
case 420:
SendServerQuit(420);
break;
case 360:
SendServerQuit(360);
break;
case 300:
SendServerQuit(300);
break;
case 240:
SendServerQuit(240);
break;
case 180:
SendServerQuit(180);
break;
case 120:
SendServerQuit(120);
break;
case 60:
LoginServerThread.getInstance().setServerStatus(ServerStatus.STATUS_DOWN); // avoids new players from logging in
SendServerQuit(60);
break;
case 30:
SendServerQuit(30);
break;
case 10:
SendServerQuit(10);
break;
case 5:
SendServerQuit(5);
break;
case 4:
SendServerQuit(4);
break;
case 3:
SendServerQuit(3);
break;
case 2:
SendServerQuit(2);
break;
case 1:
SendServerQuit(1);
break;
}
_secondsShut--;
int delay = 1000; // milliseconds
Thread.sleep(delay);
if (_shutdownMode == ABORT)
{
break;
}
}
}
catch (InterruptedException e)
{
// this will never happen
}
}
/**
* This sends a last byebye, disconnects all players and saves data.
*/
private void saveData()
{
switch (_shutdownMode)
{
case SIGTERM:
_log.info("SIGTERM received. Shutting down NOW!");
break;
case GM_SHUTDOWN:
_log.info("GM shutdown received. Shutting down NOW!");
break;
case GM_RESTART:
_log.info("GM restart received. Restarting NOW!");
break;
}
/*
* if (Config.ACTIVATE_POSITION_RECORDER) Universe.getInstance().implode(true);
*/
TimeCounter tc = new TimeCounter();
// Save all raidboss and GrandBoss status ^_^
RaidBossSpawnManager.getInstance().cleanUp();
_log.info("RaidBossSpawnManager: All raidboss info saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
GrandBossManager.getInstance().cleanUp();
_log.info("GrandBossManager: All Grand Boss info saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
ItemAuctionManager.getInstance().shutdown();
_log.info("Item Auction Manager: All tasks stopped(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
Olympiad.getInstance().saveOlympiadStatus();
_log.info("Olympiad System: Data saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
Hero.getInstance().shutdown();
_log.info("Hero System: Data saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
ClanTable.getInstance().storeClanScore();
_log.info("Clan System: Data saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
// Save Cursed Weapons data before closing.
CursedWeaponsManager.getInstance().saveData();
_log.info("Cursed Weapons Manager: Data saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
// Save all manor data
if (!Config.ALT_MANOR_SAVE_ALL_ACTIONS)
{
CastleManorManager.getInstance().storeMe();
_log.info("Castle Manor Manager: Data saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
}
CHSiegeManager.getInstance().onServerShutDown();
_log.info("CHSiegeManager: Siegable hall attacker lists saved!");
// Save all global (non-player specific) Quest data that needs to persist after reboot
QuestManager.getInstance().save();
_log.info("Quest Manager: Data saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
// Save all global variables data
GlobalVariablesManager.getInstance().storeMe();
_log.info("Global Variables Manager: Variables saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
// Save items on ground before closing
if (Config.SAVE_DROPPED_ITEM)
{
ItemsOnGroundManager.getInstance().saveInDb();
_log.info("Items On Ground Manager: Data saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
ItemsOnGroundManager.getInstance().cleanUp();
_log.info("Items On Ground Manager: Cleaned up(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
}
// Save bot reports to database
if (Config.BOTREPORT_ENABLE)
{
BotReportTable.getInstance().saveReportedCharData();
_log.info("Bot Report Table: Sucessfully saved reports to database!");
}
try
{
Thread.sleep(5000);
}
catch (InterruptedException e)
{
// never happens :p
}
}
/**
* This disconnects all clients from the server.
*/
private void disconnectAllCharacters()
{
for (L2PcInstance player : L2World.getInstance().getPlayers())
{
// Logout Character
try
{
L2GameClient client = player.getClient();
if ((client != null) && !client.isDetached())
{
client.close(ServerClose.STATIC_PACKET);
client.setActiveChar(null);
player.setClient(null);
}
player.deleteMe();
}
catch (Throwable t)
{
_log.log(Level.WARNING, "Failed logour char " + player, t);
}
}
}
/**
* A simple class used to track down the estimated time of method executions.<br>
* Once this class is created, it saves the start time, and when you want to get the estimated time, use the getEstimatedTime() method.
*/
private static final class TimeCounter
{
private long _startTime;
protected TimeCounter()
{
restartCounter();
}
protected void restartCounter()
{
_startTime = System.currentTimeMillis();
}
protected long getEstimatedTimeAndRestartCounter()
{
final long toReturn = System.currentTimeMillis() - _startTime;
restartCounter();
return toReturn;
}
protected long getEstimatedTime()
{
return System.currentTimeMillis() - _startTime;
}
}
/**
* Get the shutdown-hook instance the shutdown-hook instance is created by the first call of this function, but it has to be registered externally.<br>
* @return instance of Shutdown, to be used as shutdown hook
*/
public static Shutdown getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final Shutdown _instance = new Shutdown();
}
}

View File

@@ -0,0 +1,719 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import javolution.util.FastList;
import javolution.util.FastMap;
import javolution.util.FastSet;
import com.l2jserver.Config;
import com.l2jserver.util.StringUtil;
/**
* <p>
* This class is made to handle all the ThreadPools used in L2J.
* </p>
* <p>
* Scheduled Tasks can either be sent to a {@link #_generalScheduledThreadPool "general"} or {@link #_effectsScheduledThreadPool "effects"} {@link ScheduledThreadPoolExecutor ScheduledThreadPool}: The "effects" one is used for every effects (skills, hp/mp regen ...) while the "general" one is used
* for everything else that needs to be scheduled.<br>
* There also is an {@link #_aiScheduledThreadPool "ai"} {@link ScheduledThreadPoolExecutor ScheduledThreadPool} used for AI Tasks.
* </p>
* <p>
* Tasks can be sent to {@link ScheduledThreadPoolExecutor ScheduledThreadPool} either with:
* <ul>
* <li>{@link #scheduleEffect(Runnable, long, TimeUnit)} and {@link #scheduleEffect(Runnable, long)} : for effects Tasks that needs to be executed only once.</li>
* <li>{@link #scheduleGeneral(Runnable, long, TimeUnit)} and {@link #scheduleGeneral(Runnable, long)} : for scheduled Tasks that needs to be executed once.</li>
* <li>{@link #scheduleAi(Runnable, long, TimeUnit)} and {@link #scheduleAi(Runnable, long)} : for AI Tasks that needs to be executed once</li>
* </ul>
* or
* <ul>
* <li>{@link #scheduleEffectAtFixedRate(Runnable, long, long, TimeUnit)} and {@link #scheduleEffectAtFixedRate(Runnable, long, long)} : for effects Tasks that needs to be executed periodicaly.</li>
* <li>{@link #scheduleGeneralAtFixedRate(Runnable, long, long, TimeUnit)} and {@link #scheduleGeneralAtFixedRate(Runnable, long, long)} : for scheduled Tasks that needs to be executed periodicaly.</li>
* <li>{@link #scheduleAiAtFixedRate(Runnable, long, long, TimeUnit)} and {@link #scheduleAiAtFixedRate(Runnable, long, long)} : for AI Tasks that needs to be executed periodicaly</li>
* </ul>
* </p>
* <p>
* For all Tasks that should be executed with no delay asynchronously in a ThreadPool there also are usual {@link ThreadPoolExecutor ThreadPools} that can grow/shrink according to their load.:
* <ul>
* <li>{@link #_generalPacketsThreadPool GeneralPackets} where most packets handler are executed.</li>
* <li>{@link #_ioPacketsThreadPool I/O Packets} where all the i/o packets are executed.</li>
* <li>There will be an AI ThreadPool where AI events should be executed</li>
* <li>A general ThreadPool where everything else that needs to run asynchronously with no delay should be executed ({@link com.l2jserver.gameserver.model.actor.knownlist KnownList} updates, SQL updates/inserts...)?</li>
* </ul>
* </p>
* @author -Wooden-
*/
public class ThreadPoolManager
{
protected static final Logger _log = Logger.getLogger(ThreadPoolManager.class.getName());
private static final class RunnableWrapper implements Runnable
{
private final Runnable _r;
public RunnableWrapper(final Runnable r)
{
_r = r;
}
@Override
public final void run()
{
try
{
_r.run();
}
catch (final Throwable e)
{
final Thread t = Thread.currentThread();
final UncaughtExceptionHandler h = t.getUncaughtExceptionHandler();
if (h != null)
{
h.uncaughtException(t, e);
}
}
}
}
protected ScheduledThreadPoolExecutor _effectsScheduledThreadPool;
protected ScheduledThreadPoolExecutor _generalScheduledThreadPool;
protected ScheduledThreadPoolExecutor _aiScheduledThreadPool;
protected ScheduledThreadPoolExecutor _eventScheduledThreadPool;
private final ThreadPoolExecutor _generalPacketsThreadPool;
private final ThreadPoolExecutor _ioPacketsThreadPool;
private final ThreadPoolExecutor _generalThreadPool;
private final ThreadPoolExecutor _eventThreadPool;
private boolean _shutdown;
public static ThreadPoolManager getInstance()
{
return SingletonHolder._instance;
}
protected ThreadPoolManager()
{
_effectsScheduledThreadPool = new ScheduledThreadPoolExecutor(Config.THREAD_P_EFFECTS, new PriorityThreadFactory("EffectsSTPool", Thread.NORM_PRIORITY));
_generalScheduledThreadPool = new ScheduledThreadPoolExecutor(Config.THREAD_P_GENERAL, new PriorityThreadFactory("GeneralSTPool", Thread.NORM_PRIORITY));
_eventScheduledThreadPool = new ScheduledThreadPoolExecutor(Config.THREAD_E_EVENTS, new PriorityThreadFactory("EventSTPool", Thread.NORM_PRIORITY));
_ioPacketsThreadPool = new ThreadPoolExecutor(Config.IO_PACKET_THREAD_CORE_SIZE, Integer.MAX_VALUE, 5L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new PriorityThreadFactory("I/O Packet Pool", Thread.NORM_PRIORITY + 1));
_generalPacketsThreadPool = new ThreadPoolExecutor(Config.GENERAL_PACKET_THREAD_CORE_SIZE, Config.GENERAL_PACKET_THREAD_CORE_SIZE + 2, 15L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new PriorityThreadFactory("Normal Packet Pool", Thread.NORM_PRIORITY + 1));
_generalThreadPool = new ThreadPoolExecutor(Config.GENERAL_THREAD_CORE_SIZE, Config.GENERAL_THREAD_CORE_SIZE + 2, 5L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new PriorityThreadFactory("General Pool", Thread.NORM_PRIORITY));
_aiScheduledThreadPool = new ScheduledThreadPoolExecutor(Config.AI_MAX_THREAD, new PriorityThreadFactory("AISTPool", Thread.NORM_PRIORITY));
_eventThreadPool = new ThreadPoolExecutor(Config.EVENT_MAX_THREAD, Config.EVENT_MAX_THREAD + 2, 5L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new PriorityThreadFactory("Event Pool", Thread.NORM_PRIORITY));
scheduleGeneralAtFixedRate(new PurgeTask(), 10, 5, TimeUnit.MINUTES);
}
/**
* Schedules an effect task to be executed after the given delay.
* @param task the task to execute
* @param delay the delay in the given time unit
* @param unit the time unit of the delay parameter
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
*/
public ScheduledFuture<?> scheduleEffect(Runnable task, long delay, TimeUnit unit)
{
try
{
return _effectsScheduledThreadPool.schedule(new RunnableWrapper(task), delay, unit);
}
catch (RejectedExecutionException e)
{
return null;
}
}
/**
* Schedules an effect task to be executed after the given delay.
* @param task the task to execute
* @param delay the delay in milliseconds
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
*/
public ScheduledFuture<?> scheduleEffect(Runnable task, long delay)
{
return scheduleEffect(task, delay, TimeUnit.MILLISECONDS);
}
/**
* Schedules an effect task to be executed at fixed rate.
* @param task the task to execute
* @param initialDelay the initial delay in the given time unit
* @param period the period between executions in the given time unit
* @param unit the time unit of the initialDelay and period parameters
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
*/
public ScheduledFuture<?> scheduleEffectAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit)
{
try
{
return _effectsScheduledThreadPool.scheduleAtFixedRate(new RunnableWrapper(task), initialDelay, period, unit);
}
catch (RejectedExecutionException e)
{
return null; /* shutdown, ignore */
}
}
/**
* Schedules an effect task to be executed at fixed rate.
* @param task the task to execute
* @param initialDelay the initial delay in milliseconds
* @param period the period between executions in milliseconds
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
*/
public ScheduledFuture<?> scheduleEffectAtFixedRate(Runnable task, long initialDelay, long period)
{
return scheduleEffectAtFixedRate(task, initialDelay, period, TimeUnit.MILLISECONDS);
}
/**
* Schedules a general task to be executed after the given delay.
* @param task the task to execute
* @param delay the delay in the given time unit
* @param unit the time unit of the delay parameter
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
*/
public ScheduledFuture<?> scheduleGeneral(Runnable task, long delay, TimeUnit unit)
{
try
{
return _generalScheduledThreadPool.schedule(new RunnableWrapper(task), delay, unit);
}
catch (RejectedExecutionException e)
{
return null; /* shutdown, ignore */
}
}
/**
* Schedules a general task to be executed after the given delay.
* @param task the task to execute
* @param delay the delay in milliseconds
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
*/
public ScheduledFuture<?> scheduleGeneral(Runnable task, long delay)
{
return scheduleGeneral(task, delay, TimeUnit.MILLISECONDS);
}
/**
* Schedules a general task to be executed at fixed rate.
* @param task the task to execute
* @param initialDelay the initial delay in the given time unit
* @param period the period between executions in the given time unit
* @param unit the time unit of the initialDelay and period parameters
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
*/
public ScheduledFuture<?> scheduleGeneralAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit)
{
try
{
return _generalScheduledThreadPool.scheduleAtFixedRate(new RunnableWrapper(task), initialDelay, period, unit);
}
catch (RejectedExecutionException e)
{
return null; /* shutdown, ignore */
}
}
/**
* Schedules a event task to be executed after the given delay.
* @param task the task to execute
* @param delay the delay in the given time unit
* @param unit the time unit of the delay parameter
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
*/
public ScheduledFuture<?> scheduleEvent(Runnable task, long delay, TimeUnit unit)
{
try
{
return _eventScheduledThreadPool.schedule(new RunnableWrapper(task), delay, unit);
}
catch (RejectedExecutionException e)
{
return null; /* shutdown, ignore */
}
}
/**
* Schedules a event task to be executed after the given delay.
* @param task the task to execute
* @param delay the delay in milliseconds
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
*/
public ScheduledFuture<?> scheduleEvent(Runnable task, long delay)
{
return scheduleEvent(task, delay, TimeUnit.MILLISECONDS);
}
/**
* Schedules a event task to be executed at fixed rate.
* @param task the task to execute
* @param initialDelay the initial delay in the given time unit
* @param period the period between executions in the given time unit
* @param unit the time unit of the initialDelay and period parameters
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
*/
public ScheduledFuture<?> scheduleEventAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit)
{
try
{
return _eventScheduledThreadPool.scheduleAtFixedRate(new RunnableWrapper(task), initialDelay, period, unit);
}
catch (RejectedExecutionException e)
{
return null; /* shutdown, ignore */
}
}
/**
* Schedules a general task to be executed at fixed rate.
* @param task the task to execute
* @param initialDelay the initial delay in milliseconds
* @param period the period between executions in milliseconds
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
*/
public ScheduledFuture<?> scheduleGeneralAtFixedRate(Runnable task, long initialDelay, long period)
{
return scheduleGeneralAtFixedRate(task, initialDelay, period, TimeUnit.MILLISECONDS);
}
/**
* Schedules an AI task to be executed after the given delay.
* @param task the task to execute
* @param delay the delay in the given time unit
* @param unit the time unit of the delay parameter
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
*/
public ScheduledFuture<?> scheduleAi(Runnable task, long delay, TimeUnit unit)
{
try
{
return _aiScheduledThreadPool.schedule(new RunnableWrapper(task), delay, unit);
}
catch (RejectedExecutionException e)
{
return null; /* shutdown, ignore */
}
}
/**
* Schedules an AI task to be executed after the given delay.
* @param task the task to execute
* @param delay the delay in milliseconds
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
*/
public ScheduledFuture<?> scheduleAi(Runnable task, long delay)
{
return scheduleAi(task, delay, TimeUnit.MILLISECONDS);
}
/**
* Schedules a general task to be executed at fixed rate.
* @param task the task to execute
* @param initialDelay the initial delay in the given time unit
* @param period the period between executions in the given time unit
* @param unit the time unit of the initialDelay and period parameters
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
*/
public ScheduledFuture<?> scheduleAiAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit)
{
try
{
return _aiScheduledThreadPool.scheduleAtFixedRate(new RunnableWrapper(task), initialDelay, period, unit);
}
catch (RejectedExecutionException e)
{
return null; /* shutdown, ignore */
}
}
/**
* Schedules a general task to be executed at fixed rate.
* @param task the task to execute
* @param initialDelay the initial delay in milliseconds
* @param period the period between executions in milliseconds
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
*/
public ScheduledFuture<?> scheduleAiAtFixedRate(Runnable task, long initialDelay, long period)
{
return scheduleAiAtFixedRate(task, initialDelay, period, TimeUnit.MILLISECONDS);
}
/**
* Executes a packet task sometime in future in another thread.
* @param task the task to execute
*/
public void executePacket(Runnable task)
{
try
{
_generalPacketsThreadPool.execute(task);
}
catch (RejectedExecutionException e)
{
/* shutdown, ignore */
}
}
/**
* Executes an IO packet task sometime in future in another thread.
* @param task the task to execute
*/
public void executeIOPacket(Runnable task)
{
try
{
_ioPacketsThreadPool.execute(task);
}
catch (RejectedExecutionException e)
{
/* shutdown, ignore */
}
}
/**
* Executes a general task sometime in future in another thread.
* @param task the task to execute
*/
public void executeGeneral(Runnable task)
{
try
{
_generalThreadPool.execute(new RunnableWrapper(task));
}
catch (RejectedExecutionException e)
{
/* shutdown, ignore */
}
}
/**
* Executes an AI task sometime in future in another thread.
* @param task the task to execute
*/
public void executeAi(Runnable task)
{
try
{
_aiScheduledThreadPool.execute(new RunnableWrapper(task));
}
catch (RejectedExecutionException e)
{
/* shutdown, ignore */
}
}
/**
* Executes an Event task sometime in future in another thread.
* @param task the task to execute
*/
public void executeEvent(Runnable task)
{
try
{
_eventThreadPool.execute(new RunnableWrapper(task));
}
catch (RejectedExecutionException e)
{
/* shutdown, ignore */
}
}
public String[] getStats()
{
return new String[]
{
"STP:",
" + Effects:",
" |- ActiveThreads: " + _effectsScheduledThreadPool.getActiveCount(),
" |- getCorePoolSize: " + _effectsScheduledThreadPool.getCorePoolSize(),
" |- PoolSize: " + _effectsScheduledThreadPool.getPoolSize(),
" |- MaximumPoolSize: " + _effectsScheduledThreadPool.getMaximumPoolSize(),
" |- CompletedTasks: " + _effectsScheduledThreadPool.getCompletedTaskCount(),
" |- ScheduledTasks: " + _effectsScheduledThreadPool.getQueue().size(),
" | -------",
" + General:",
" |- ActiveThreads: " + _generalScheduledThreadPool.getActiveCount(),
" |- getCorePoolSize: " + _generalScheduledThreadPool.getCorePoolSize(),
" |- PoolSize: " + _generalScheduledThreadPool.getPoolSize(),
" |- MaximumPoolSize: " + _generalScheduledThreadPool.getMaximumPoolSize(),
" |- CompletedTasks: " + _generalScheduledThreadPool.getCompletedTaskCount(),
" |- ScheduledTasks: " + _generalScheduledThreadPool.getQueue().size(),
" | -------",
" + AI:",
" |- ActiveThreads: " + _aiScheduledThreadPool.getActiveCount(),
" |- getCorePoolSize: " + _aiScheduledThreadPool.getCorePoolSize(),
" |- PoolSize: " + _aiScheduledThreadPool.getPoolSize(),
" |- MaximumPoolSize: " + _aiScheduledThreadPool.getMaximumPoolSize(),
" |- CompletedTasks: " + _aiScheduledThreadPool.getCompletedTaskCount(),
" |- ScheduledTasks: " + _aiScheduledThreadPool.getQueue().size(),
" | -------",
" + Event:",
" |- ActiveThreads: " + _eventScheduledThreadPool.getActiveCount(),
" |- getCorePoolSize: " + _eventScheduledThreadPool.getCorePoolSize(),
" |- PoolSize: " + _eventScheduledThreadPool.getPoolSize(),
" |- MaximumPoolSize: " + _eventScheduledThreadPool.getMaximumPoolSize(),
" |- CompletedTasks: " + _eventScheduledThreadPool.getCompletedTaskCount(),
" |- ScheduledTasks: " + _eventScheduledThreadPool.getQueue().size(),
"TP:",
" + Packets:",
" |- ActiveThreads: " + _generalPacketsThreadPool.getActiveCount(),
" |- getCorePoolSize: " + _generalPacketsThreadPool.getCorePoolSize(),
" |- MaximumPoolSize: " + _generalPacketsThreadPool.getMaximumPoolSize(),
" |- LargestPoolSize: " + _generalPacketsThreadPool.getLargestPoolSize(),
" |- PoolSize: " + _generalPacketsThreadPool.getPoolSize(),
" |- CompletedTasks: " + _generalPacketsThreadPool.getCompletedTaskCount(),
" |- QueuedTasks: " + _generalPacketsThreadPool.getQueue().size(),
" | -------",
" + I/O Packets:",
" |- ActiveThreads: " + _ioPacketsThreadPool.getActiveCount(),
" |- getCorePoolSize: " + _ioPacketsThreadPool.getCorePoolSize(),
" |- MaximumPoolSize: " + _ioPacketsThreadPool.getMaximumPoolSize(),
" |- LargestPoolSize: " + _ioPacketsThreadPool.getLargestPoolSize(),
" |- PoolSize: " + _ioPacketsThreadPool.getPoolSize(),
" |- CompletedTasks: " + _ioPacketsThreadPool.getCompletedTaskCount(),
" |- QueuedTasks: " + _ioPacketsThreadPool.getQueue().size(),
" | -------",
" + General Tasks:",
" |- ActiveThreads: " + _generalThreadPool.getActiveCount(),
" |- getCorePoolSize: " + _generalThreadPool.getCorePoolSize(),
" |- MaximumPoolSize: " + _generalThreadPool.getMaximumPoolSize(),
" |- LargestPoolSize: " + _generalThreadPool.getLargestPoolSize(),
" |- PoolSize: " + _generalThreadPool.getPoolSize(),
" |- CompletedTasks: " + _generalThreadPool.getCompletedTaskCount(),
" |- QueuedTasks: " + _generalThreadPool.getQueue().size(),
" | -------",
" + Event Tasks:",
" |- ActiveThreads: " + _eventThreadPool.getActiveCount(),
" |- getCorePoolSize: " + _eventThreadPool.getCorePoolSize(),
" |- MaximumPoolSize: " + _eventThreadPool.getMaximumPoolSize(),
" |- LargestPoolSize: " + _eventThreadPool.getLargestPoolSize(),
" |- PoolSize: " + _eventThreadPool.getPoolSize(),
" |- CompletedTasks: " + _eventThreadPool.getCompletedTaskCount(),
" |- QueuedTasks: " + _eventThreadPool.getQueue().size(),
" | -------",
" + Javolution stats:",
" |- FastList: " + FastList.report(),
" |- FastMap: " + FastMap.report(),
" |- FastSet: " + FastSet.report(),
" | -------"
};
}
private static class PriorityThreadFactory implements ThreadFactory
{
private final int _prio;
private final String _name;
private final AtomicInteger _threadNumber = new AtomicInteger(1);
private final ThreadGroup _group;
public PriorityThreadFactory(String name, int prio)
{
_prio = prio;
_name = name;
_group = new ThreadGroup(_name);
}
@Override
public Thread newThread(Runnable r)
{
Thread t = new Thread(_group, r, _name + "-" + _threadNumber.getAndIncrement());
t.setPriority(_prio);
return t;
}
public ThreadGroup getGroup()
{
return _group;
}
}
public void shutdown()
{
_shutdown = true;
try
{
_effectsScheduledThreadPool.awaitTermination(1, TimeUnit.SECONDS);
_generalScheduledThreadPool.awaitTermination(1, TimeUnit.SECONDS);
_generalPacketsThreadPool.awaitTermination(1, TimeUnit.SECONDS);
_ioPacketsThreadPool.awaitTermination(1, TimeUnit.SECONDS);
_generalThreadPool.awaitTermination(1, TimeUnit.SECONDS);
_eventThreadPool.awaitTermination(1, TimeUnit.SECONDS);
_effectsScheduledThreadPool.shutdown();
_generalScheduledThreadPool.shutdown();
_generalPacketsThreadPool.shutdown();
_ioPacketsThreadPool.shutdown();
_generalThreadPool.shutdown();
_eventThreadPool.shutdown();
_log.info("All ThreadPools are now stopped");
}
catch (InterruptedException e)
{
_log.log(Level.WARNING, "", e);
}
}
public boolean isShutdown()
{
return _shutdown;
}
public void purge()
{
_effectsScheduledThreadPool.purge();
_generalScheduledThreadPool.purge();
_aiScheduledThreadPool.purge();
_eventScheduledThreadPool.purge();
_ioPacketsThreadPool.purge();
_generalPacketsThreadPool.purge();
_generalThreadPool.purge();
_eventThreadPool.purge();
}
public String getPacketStats()
{
final StringBuilder sb = new StringBuilder(1000);
ThreadFactory tf = _generalPacketsThreadPool.getThreadFactory();
if (tf instanceof PriorityThreadFactory)
{
PriorityThreadFactory ptf = (PriorityThreadFactory) tf;
int count = ptf.getGroup().activeCount();
Thread[] threads = new Thread[count + 2];
ptf.getGroup().enumerate(threads);
StringUtil.append(sb, "General Packet Thread Pool:" + Config.EOL + "Tasks in the queue: ", String.valueOf(_generalPacketsThreadPool.getQueue().size()), Config.EOL + "Showing threads stack trace:" + Config.EOL + "There should be ", String.valueOf(count), " Threads" + Config.EOL);
for (Thread t : threads)
{
if (t == null)
{
continue;
}
StringUtil.append(sb, t.getName(), Config.EOL);
for (StackTraceElement ste : t.getStackTrace())
{
StringUtil.append(sb, ste.toString(), Config.EOL);
}
}
}
sb.append("Packet Tp stack traces printed.");
sb.append(Config.EOL);
return sb.toString();
}
public String getIOPacketStats()
{
final StringBuilder sb = new StringBuilder(1000);
ThreadFactory tf = _ioPacketsThreadPool.getThreadFactory();
if (tf instanceof PriorityThreadFactory)
{
PriorityThreadFactory ptf = (PriorityThreadFactory) tf;
int count = ptf.getGroup().activeCount();
Thread[] threads = new Thread[count + 2];
ptf.getGroup().enumerate(threads);
StringUtil.append(sb, "I/O Packet Thread Pool:" + Config.EOL + "Tasks in the queue: ", String.valueOf(_ioPacketsThreadPool.getQueue().size()), Config.EOL + "Showing threads stack trace:" + Config.EOL + "There should be ", String.valueOf(count), " Threads" + Config.EOL);
for (Thread t : threads)
{
if (t == null)
{
continue;
}
StringUtil.append(sb, t.getName(), Config.EOL);
for (StackTraceElement ste : t.getStackTrace())
{
StringUtil.append(sb, ste.toString(), Config.EOL);
}
}
}
sb.append("Packet Tp stack traces printed." + Config.EOL);
return sb.toString();
}
public String getGeneralStats()
{
final StringBuilder sb = new StringBuilder(1000);
ThreadFactory tf = _generalThreadPool.getThreadFactory();
if (tf instanceof PriorityThreadFactory)
{
PriorityThreadFactory ptf = (PriorityThreadFactory) tf;
int count = ptf.getGroup().activeCount();
Thread[] threads = new Thread[count + 2];
ptf.getGroup().enumerate(threads);
StringUtil.append(sb, "General Thread Pool:" + Config.EOL + "Tasks in the queue: ", String.valueOf(_generalThreadPool.getQueue().size()), Config.EOL + "Showing threads stack trace:" + Config.EOL + "There should be ", String.valueOf(count), " Threads" + Config.EOL);
for (Thread t : threads)
{
if (t == null)
{
continue;
}
StringUtil.append(sb, t.getName(), Config.EOL);
for (StackTraceElement ste : t.getStackTrace())
{
StringUtil.append(sb, ste.toString(), Config.EOL);
}
}
}
sb.append("Packet Tp stack traces printed." + Config.EOL);
return sb.toString();
}
protected class PurgeTask implements Runnable
{
@Override
public void run()
{
_effectsScheduledThreadPool.purge();
_generalScheduledThreadPool.purge();
_aiScheduledThreadPool.purge();
_eventScheduledThreadPool.purge();
}
}
private static class SingletonHolder
{
protected static final ThreadPoolManager _instance = new ThreadPoolManager();
}
}

View File

@@ -0,0 +1,852 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.ai;
import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
import java.util.concurrent.Future;
import java.util.logging.Logger;
import com.l2jserver.gameserver.GameTimeController;
import com.l2jserver.gameserver.ThreadPoolManager;
import com.l2jserver.gameserver.model.L2Object;
import com.l2jserver.gameserver.model.Location;
import com.l2jserver.gameserver.model.actor.L2Character;
import com.l2jserver.gameserver.model.actor.L2Summon;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.skills.Skill;
import com.l2jserver.gameserver.network.serverpackets.ActionFailed;
import com.l2jserver.gameserver.network.serverpackets.AutoAttackStart;
import com.l2jserver.gameserver.network.serverpackets.AutoAttackStop;
import com.l2jserver.gameserver.network.serverpackets.Die;
import com.l2jserver.gameserver.network.serverpackets.MoveToLocation;
import com.l2jserver.gameserver.network.serverpackets.MoveToPawn;
import com.l2jserver.gameserver.network.serverpackets.StopMove;
import com.l2jserver.gameserver.network.serverpackets.StopRotation;
import com.l2jserver.gameserver.taskmanager.AttackStanceTaskManager;
/**
* Mother class of all objects AI in the world.<br>
* AbastractAI :<br>
* <li>L2CharacterAI</li>
*/
public abstract class AbstractAI implements Ctrl
{
protected final Logger _log = Logger.getLogger(getClass().getName());
private NextAction _nextAction;
/**
* @return the _nextAction
*/
public NextAction getNextAction()
{
return _nextAction;
}
/**
* @param nextAction the next action to set.
*/
public void setNextAction(NextAction nextAction)
{
_nextAction = nextAction;
}
private class FollowTask implements Runnable
{
protected int _range = 70;
public FollowTask()
{
}
public FollowTask(int range)
{
_range = range;
}
@Override
public void run()
{
try
{
if (_followTask == null)
{
return;
}
L2Character followTarget = _followTarget; // copy to prevent NPE
if (followTarget == null)
{
if (_actor instanceof L2Summon)
{
((L2Summon) _actor).setFollowStatus(false);
}
setIntention(AI_INTENTION_IDLE);
return;
}
if (!_actor.isInsideRadius(followTarget, _range, true, false))
{
if (!_actor.isInsideRadius(followTarget, 3000, true, false))
{
// if the target is too far (maybe also teleported)
if (_actor instanceof L2Summon)
{
((L2Summon) _actor).setFollowStatus(false);
}
setIntention(AI_INTENTION_IDLE);
return;
}
moveToPawn(followTarget, _range);
}
}
catch (Exception e)
{
_log.warning(getClass().getSimpleName() + ": Error: " + e.getMessage());
}
}
}
/** The character that this AI manages */
protected final L2Character _actor;
/** An accessor for private methods of the actor */
protected final L2Character.AIAccessor _accessor;
/** Current long-term intention */
protected CtrlIntention _intention = AI_INTENTION_IDLE;
/** Current long-term intention parameter */
protected Object _intentionArg0 = null;
/** Current long-term intention parameter */
protected Object _intentionArg1 = null;
/** Flags about client's state, in order to know which messages to send */
protected volatile boolean _clientMoving;
/** Flags about client's state, in order to know which messages to send */
protected volatile boolean _clientAutoAttacking;
/** Flags about client's state, in order to know which messages to send */
protected int _clientMovingToPawnOffset;
/** Different targets this AI maintains */
private L2Object _target;
private L2Character _castTarget;
protected L2Character _attackTarget;
protected L2Character _followTarget;
/** The skill we are currently casting by INTENTION_CAST */
Skill _skill;
/** Different internal state flags */
private int _moveToPawnTimeout;
protected Future<?> _followTask = null;
private static final int FOLLOW_INTERVAL = 1000;
private static final int ATTACK_FOLLOW_INTERVAL = 500;
/**
* Constructor of AbstractAI.
* @param accessor The AI accessor of the L2Character
*/
protected AbstractAI(L2Character.AIAccessor accessor)
{
_accessor = accessor;
// Get the L2Character managed by this Accessor AI
_actor = accessor.getActor();
}
/**
* @return the L2Character managed by this Accessor AI.
*/
@Override
public L2Character getActor()
{
return _actor;
}
/**
* @return the current Intention.
*/
@Override
public CtrlIntention getIntention()
{
return _intention;
}
protected void setCastTarget(L2Character target)
{
_castTarget = target;
}
/**
* @return the current cast target.
*/
public L2Character getCastTarget()
{
return _castTarget;
}
protected void setAttackTarget(L2Character target)
{
_attackTarget = target;
}
/**
* @return current attack target.
*/
@Override
public L2Character getAttackTarget()
{
return _attackTarget;
}
/**
* Set the Intention of this AbstractAI.<br>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : This method is USED by AI classes</B></FONT><B><U><br>
* Overridden in </U> : </B><BR>
* <B>L2AttackableAI</B> : Create an AI Task executed every 1s (if necessary)<BR>
* <B>L2PlayerAI</B> : Stores the current AI intention parameters to later restore it if necessary.
* @param intention The new Intention to set to the AI
* @param arg0 The first parameter of the Intention
* @param arg1 The second parameter of the Intention
*/
synchronized void changeIntention(CtrlIntention intention, Object arg0, Object arg1)
{
_intention = intention;
_intentionArg0 = arg0;
_intentionArg1 = arg1;
}
/**
* Launch the L2CharacterAI onIntention method corresponding to the new Intention.<br>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Stop the FOLLOW mode if necessary</B></FONT>
* @param intention The new Intention to set to the AI
*/
@Override
public final void setIntention(CtrlIntention intention)
{
setIntention(intention, null, null);
}
/**
* Launch the L2CharacterAI onIntention method corresponding to the new Intention.<br>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Stop the FOLLOW mode if necessary</B></FONT>
* @param intention The new Intention to set to the AI
* @param arg0 The first parameter of the Intention (optional target)
*/
@Override
public final void setIntention(CtrlIntention intention, Object arg0)
{
setIntention(intention, arg0, null);
}
@Override
public final void setIntention(CtrlIntention intention, Object arg0, Object arg1)
{
// Stop the follow mode if necessary
if ((intention != AI_INTENTION_FOLLOW) && (intention != AI_INTENTION_ATTACK))
{
stopFollow();
}
// Launch the onIntention method of the L2CharacterAI corresponding to the new Intention
switch (intention)
{
case AI_INTENTION_IDLE:
onIntentionIdle();
break;
case AI_INTENTION_ACTIVE:
onIntentionActive();
break;
case AI_INTENTION_REST:
onIntentionRest();
break;
case AI_INTENTION_ATTACK:
onIntentionAttack((L2Character) arg0);
break;
case AI_INTENTION_CAST:
onIntentionCast((Skill) arg0, (L2Object) arg1);
break;
case AI_INTENTION_MOVE_TO:
onIntentionMoveTo((Location) arg0);
break;
case AI_INTENTION_FOLLOW:
onIntentionFollow((L2Character) arg0);
break;
case AI_INTENTION_PICK_UP:
onIntentionPickUp((L2Object) arg0);
break;
case AI_INTENTION_INTERACT:
onIntentionInteract((L2Object) arg0);
break;
}
// If do move or follow intention drop next action.
if ((_nextAction != null) && _nextAction.getIntentions().contains(intention))
{
_nextAction = null;
}
}
/**
* Launch the L2CharacterAI onEvt method corresponding to the Event.<br>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : The current general intention won't be change (ex : If the character attack and is stunned, he will attack again after the stunned period)</B></FONT>
* @param evt The event whose the AI must be notified
*/
@Override
public final void notifyEvent(CtrlEvent evt)
{
notifyEvent(evt, null, null);
}
/**
* Launch the L2CharacterAI onEvt method corresponding to the Event. <FONT COLOR=#FF0000><B> <U>Caution</U> : The current general intention won't be change (ex : If the character attack and is stunned, he will attack again after the stunned period)</B></FONT>
* @param evt The event whose the AI must be notified
* @param arg0 The first parameter of the Event (optional target)
*/
@Override
public final void notifyEvent(CtrlEvent evt, Object arg0)
{
notifyEvent(evt, arg0, null);
}
/**
* Launch the L2CharacterAI onEvt method corresponding to the Event. <FONT COLOR=#FF0000><B> <U>Caution</U> : The current general intention won't be change (ex : If the character attack and is stunned, he will attack again after the stunned period)</B></FONT>
* @param evt The event whose the AI must be notified
* @param arg0 The first parameter of the Event (optional target)
* @param arg1 The second parameter of the Event (optional target)
*/
@Override
public final void notifyEvent(CtrlEvent evt, Object arg0, Object arg1)
{
if ((!_actor.isVisible() && !_actor.isTeleporting()) || !_actor.hasAI())
{
return;
}
switch (evt)
{
case EVT_THINK:
onEvtThink();
break;
case EVT_ATTACKED:
onEvtAttacked((L2Character) arg0);
break;
case EVT_AGGRESSION:
onEvtAggression((L2Character) arg0, ((Number) arg1).intValue());
break;
case EVT_STUNNED:
onEvtStunned((L2Character) arg0);
break;
case EVT_PARALYZED:
onEvtParalyzed((L2Character) arg0);
break;
case EVT_SLEEPING:
onEvtSleeping((L2Character) arg0);
break;
case EVT_ROOTED:
onEvtRooted((L2Character) arg0);
break;
case EVT_CONFUSED:
onEvtConfused((L2Character) arg0);
break;
case EVT_MUTED:
onEvtMuted((L2Character) arg0);
break;
case EVT_EVADED:
onEvtEvaded((L2Character) arg0);
break;
case EVT_READY_TO_ACT:
if (!_actor.isCastingNow() && !_actor.isCastingSimultaneouslyNow())
{
onEvtReadyToAct();
}
break;
case EVT_USER_CMD:
onEvtUserCmd(arg0, arg1);
break;
case EVT_ARRIVED:
// happens e.g. from stopmove but we don't process it if we're casting
if (!_actor.isCastingNow() && !_actor.isCastingSimultaneouslyNow())
{
onEvtArrived();
}
break;
case EVT_ARRIVED_REVALIDATE:
// this is disregarded if the char is not moving any more
if (_actor.isMoving())
{
onEvtArrivedRevalidate();
}
break;
case EVT_ARRIVED_BLOCKED:
onEvtArrivedBlocked((Location) arg0);
break;
case EVT_FORGET_OBJECT:
onEvtForgetObject((L2Object) arg0);
break;
case EVT_CANCEL:
onEvtCancel();
break;
case EVT_DEAD:
onEvtDead();
break;
case EVT_FAKE_DEATH:
onEvtFakeDeath();
break;
case EVT_FINISH_CASTING:
onEvtFinishCasting();
break;
}
// Do next action.
if ((_nextAction != null) && _nextAction.getEvents().contains(evt))
{
_nextAction.doAction();
}
}
protected abstract void onIntentionIdle();
protected abstract void onIntentionActive();
protected abstract void onIntentionRest();
protected abstract void onIntentionAttack(L2Character target);
protected abstract void onIntentionCast(Skill skill, L2Object target);
protected abstract void onIntentionMoveTo(Location destination);
protected abstract void onIntentionFollow(L2Character target);
protected abstract void onIntentionPickUp(L2Object item);
protected abstract void onIntentionInteract(L2Object object);
protected abstract void onEvtThink();
protected abstract void onEvtAttacked(L2Character attacker);
protected abstract void onEvtAggression(L2Character target, int aggro);
protected abstract void onEvtStunned(L2Character attacker);
protected abstract void onEvtParalyzed(L2Character attacker);
protected abstract void onEvtSleeping(L2Character attacker);
protected abstract void onEvtRooted(L2Character attacker);
protected abstract void onEvtConfused(L2Character attacker);
protected abstract void onEvtMuted(L2Character attacker);
protected abstract void onEvtEvaded(L2Character attacker);
protected abstract void onEvtReadyToAct();
protected abstract void onEvtUserCmd(Object arg0, Object arg1);
protected abstract void onEvtArrived();
protected abstract void onEvtArrivedRevalidate();
protected abstract void onEvtArrivedBlocked(Location blocked_at_pos);
protected abstract void onEvtForgetObject(L2Object object);
protected abstract void onEvtCancel();
protected abstract void onEvtDead();
protected abstract void onEvtFakeDeath();
protected abstract void onEvtFinishCasting();
/**
* Cancel action client side by sending Server->Client packet ActionFailed to the L2PcInstance actor. <FONT COLOR=#FF0000><B> <U>Caution</U> : Low level function, used by AI subclasses</B></FONT>
*/
protected void clientActionFailed()
{
if (_actor instanceof L2PcInstance)
{
_actor.sendPacket(ActionFailed.STATIC_PACKET);
}
}
/**
* Move the actor to Pawn server side AND client side by sending Server->Client packet MoveToPawn <I>(broadcast)</I>.<br>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Low level function, used by AI subclasses</B></FONT>
* @param pawn
* @param offset
*/
protected void moveToPawn(L2Object pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled())
{
if (offset < 10)
{
offset = 10;
}
// prevent possible extra calls to this function (there is none?),
// also don't send movetopawn packets too often
boolean sendPacket = true;
if (_clientMoving && (_target == pawn))
{
if (_clientMovingToPawnOffset == offset)
{
if (GameTimeController.getInstance().getGameTicks() < _moveToPawnTimeout)
{
return;
}
sendPacket = false;
}
else if (_actor.isOnGeodataPath())
{
// minimum time to calculate new route is 2 seconds
if (GameTimeController.getInstance().getGameTicks() < (_moveToPawnTimeout + 10))
{
return;
}
}
}
// Set AI movement data
_clientMoving = true;
_clientMovingToPawnOffset = offset;
_target = pawn;
_moveToPawnTimeout = GameTimeController.getInstance().getGameTicks();
_moveToPawnTimeout += 1000 / GameTimeController.MILLIS_IN_TICK;
if ((pawn == null) || (_accessor == null))
{
return;
}
// Calculate movement data for a move to location action and add the actor to movingObjects of GameTimeController
_accessor.moveTo(pawn.getX(), pawn.getY(), pawn.getZ(), offset);
if (!_actor.isMoving())
{
clientActionFailed();
return;
}
// Send a Server->Client packet MoveToPawn/CharMoveToLocation to the actor and all L2PcInstance in its _knownPlayers
if (pawn instanceof L2Character)
{
if (_actor.isOnGeodataPath())
{
_actor.broadcastPacket(new MoveToLocation(_actor));
_clientMovingToPawnOffset = 0;
}
else if (sendPacket)
{
_actor.broadcastPacket(new MoveToPawn(_actor, (L2Character) pawn, offset));
}
}
else
{
_actor.broadcastPacket(new MoveToLocation(_actor));
}
}
else
{
clientActionFailed();
}
}
/**
* Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation <I>(broadcast)</I>.<br>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Low level function, used by AI subclasses</B></FONT>
* @param x
* @param y
* @param z
*/
protected void moveTo(int x, int y, int z)
{
// Chek if actor can move
if (!_actor.isMovementDisabled())
{
// Set AI movement data
_clientMoving = true;
_clientMovingToPawnOffset = 0;
// Calculate movement data for a move to location action and add the actor to movingObjects of GameTimeController
_accessor.moveTo(x, y, z);
// Send a Server->Client packet CharMoveToLocation to the actor and all L2PcInstance in its _knownPlayers
_actor.broadcastPacket(new MoveToLocation(_actor));
}
else
{
clientActionFailed();
}
}
/**
* Stop the actor movement server side AND client side by sending Server->Client packet StopMove/StopRotation <I>(broadcast)</I>.<br>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Low level function, used by AI subclasses</B></FONT>
* @param loc
*/
protected void clientStopMoving(Location loc)
{
// Stop movement of the L2Character
if (_actor.isMoving())
{
_accessor.stopMove(loc);
}
_clientMovingToPawnOffset = 0;
if (_clientMoving || (loc != null))
{
_clientMoving = false;
// Send a Server->Client packet StopMove to the actor and all L2PcInstance in its _knownPlayers
_actor.broadcastPacket(new StopMove(_actor));
if (loc != null)
{
// Send a Server->Client packet StopRotation to the actor and all L2PcInstance in its _knownPlayers
_actor.broadcastPacket(new StopRotation(_actor.getObjectId(), loc.getHeading(), 0));
}
}
}
/**
* Client has already arrived to target, no need to force StopMove packet.
*/
protected void clientStoppedMoving()
{
if (_clientMovingToPawnOffset > 0) // movetoPawn needs to be stopped
{
_clientMovingToPawnOffset = 0;
_actor.broadcastPacket(new StopMove(_actor));
}
_clientMoving = false;
}
public boolean isAutoAttacking()
{
return _clientAutoAttacking;
}
public void setAutoAttacking(boolean isAutoAttacking)
{
if (_actor instanceof L2Summon)
{
L2Summon summon = (L2Summon) _actor;
if (summon.getOwner() != null)
{
summon.getOwner().getAI().setAutoAttacking(isAutoAttacking);
}
return;
}
_clientAutoAttacking = isAutoAttacking;
}
/**
* Start the actor Auto Attack client side by sending Server->Client packet AutoAttackStart <I>(broadcast)</I>.<br>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Low level function, used by AI subclasses</B></FONT>
*/
public void clientStartAutoAttack()
{
if (_actor instanceof L2Summon)
{
L2Summon summon = (L2Summon) _actor;
if (summon.getOwner() != null)
{
summon.getOwner().getAI().clientStartAutoAttack();
}
return;
}
if (!isAutoAttacking())
{
if (_actor.isPlayer() && _actor.hasSummon())
{
_actor.getSummon().broadcastPacket(new AutoAttackStart(_actor.getSummon().getObjectId()));
}
// Send a Server->Client packet AutoAttackStart to the actor and all L2PcInstance in its _knownPlayers
_actor.broadcastPacket(new AutoAttackStart(_actor.getObjectId()));
setAutoAttacking(true);
}
AttackStanceTaskManager.getInstance().addAttackStanceTask(_actor);
}
/**
* Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop <I>(broadcast)</I>.<br>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Low level function, used by AI subclasses</B></FONT>
*/
public void clientStopAutoAttack()
{
if (_actor instanceof L2Summon)
{
L2Summon summon = (L2Summon) _actor;
if (summon.getOwner() != null)
{
summon.getOwner().getAI().clientStopAutoAttack();
}
return;
}
if (_actor instanceof L2PcInstance)
{
if (!AttackStanceTaskManager.getInstance().hasAttackStanceTask(_actor) && isAutoAttacking())
{
AttackStanceTaskManager.getInstance().addAttackStanceTask(_actor);
}
}
else if (isAutoAttacking())
{
_actor.broadcastPacket(new AutoAttackStop(_actor.getObjectId()));
setAutoAttacking(false);
}
}
/**
* Kill the actor client side by sending Server->Client packet AutoAttackStop, StopMove/StopRotation, Die <I>(broadcast)</I>.<br>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Low level function, used by AI subclasses</B></FONT>
*/
protected void clientNotifyDead()
{
// Send a Server->Client packet Die to the actor and all L2PcInstance in its _knownPlayers
Die msg = new Die(_actor);
_actor.broadcastPacket(msg);
// Init AI
_intention = AI_INTENTION_IDLE;
_target = null;
_castTarget = null;
_attackTarget = null;
// Cancel the follow task if necessary
stopFollow();
}
/**
* Update the state of this actor client side by sending Server->Client packet MoveToPawn/CharMoveToLocation and AutoAttackStart to the L2PcInstance player.<br>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Low level function, used by AI subclasses</B></FONT>
* @param player The L2PcIstance to notify with state of this L2Character
*/
public void describeStateToPlayer(L2PcInstance player)
{
if (getActor().isVisibleFor(player))
{
if (_clientMoving)
{
if ((_clientMovingToPawnOffset != 0) && (_followTarget != null))
{
// Send a Server->Client packet MoveToPawn to the actor and all L2PcInstance in its _knownPlayers
player.sendPacket(new MoveToPawn(_actor, _followTarget, _clientMovingToPawnOffset));
}
else
{
// Send a Server->Client packet CharMoveToLocation to the actor and all L2PcInstance in its _knownPlayers
player.sendPacket(new MoveToLocation(_actor));
}
}
}
}
/**
* Create and Launch an AI Follow Task to execute every 1s.
* @param target The L2Character to follow
*/
public synchronized void startFollow(L2Character target)
{
if (_followTask != null)
{
_followTask.cancel(false);
_followTask = null;
}
// Create and Launch an AI Follow Task to execute every 1s
_followTarget = target;
_followTask = ThreadPoolManager.getInstance().scheduleAiAtFixedRate(new FollowTask(), 5, FOLLOW_INTERVAL);
}
/**
* Create and Launch an AI Follow Task to execute every 0.5s, following at specified range.
* @param target The L2Character to follow
* @param range
*/
public synchronized void startFollow(L2Character target, int range)
{
if (_followTask != null)
{
_followTask.cancel(false);
_followTask = null;
}
_followTarget = target;
_followTask = ThreadPoolManager.getInstance().scheduleAiAtFixedRate(new FollowTask(range), 5, ATTACK_FOLLOW_INTERVAL);
}
/**
* Stop an AI Follow Task.
*/
public synchronized void stopFollow()
{
if (_followTask != null)
{
// Stop the Follow Task
_followTask.cancel(false);
_followTask = null;
}
_followTarget = null;
}
protected L2Character getFollowTarget()
{
return _followTarget;
}
protected L2Object getTarget()
{
return _target;
}
protected void setTarget(L2Object target)
{
_target = target;
}
/**
* Stop all Ai tasks and futures.
*/
public void stopAITask()
{
stopFollow();
}
@Override
public String toString()
{
return "Actor: " + _actor;
}
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.ai;
import com.l2jserver.gameserver.model.actor.L2Character;
/**
* Interface of AI and client state.<br>
* To correctly send messages to client we need it's state.<br>
* For example, if we've sent 'StartAutoAttack' message, we need to send 'StopAutoAttack' message before any other action.<br>
* Or if we've sent 'MoveToPawn', we need to send 'StopMove' when the movement of a character is canceled (by Root spell or any other reason).<br>
* Thus, we need to know the state of client, i.e. which messages we've sent and how the client will show the scene.<br>
* Close to this task is the task of AI.<br>
* If a player's character is attacking a mob, his ATTACK may be interrupted by an event, that temporary disable attacking.<br>
* But when the possibility to ATTACK will be enabled, the character must continue the ATTACK.<br>
* For mobs it may be more complex, since we want them to decide when to use magic, or when to follow the player for physical combat, or when to escape, to help another mob, etc.<br>
* This interface is hiding complexity of server<->client interaction and multiple states of a character.<br>
* It allows to set a desired, simple "wish" of a character, and the implementation of this interface will take care about the rest.<br>
* The goal of a character may be like "ATTACK", "random walk" and so on.<br>
* To reach the goal implementation will split it into several small actions, several steps (possibly repeatable).<br>
* Like "run to target" then "hit it", then if target is not dead - repeat.<br>
* This flow of simpler steps may be interrupted by incoming events.<br>
* Like a character's movement was disabled (by Root spell, for instance).<br>
* Depending on character's ability AI may choose to wait, or to use magic ATTACK and so on.<br>
* Additionally incoming events are compared with client's state of the character,<br>
* and required network messages are sent to client's, i.e. if we have incoming event that character's movement was disabled, it causes changing if its behavior,<br>
* and if client's state for the character is "moving" we send messages to clients to stop the avatar/mob.
*/
public interface Ctrl
{
/**
* Gets the actor.
* @return the actor
*/
L2Character getActor();
/**
* Gets the intention.
* @return the intention
*/
CtrlIntention getIntention();
/**
* Gets the attack target.
* @return the attack target
*/
L2Character getAttackTarget();
/**
* Set general state/intention for AI, with optional data.
* @param intention the new intention
*/
void setIntention(CtrlIntention intention);
/**
* Sets the intention.
* @param intention the intention
* @param arg0 the arg0
*/
void setIntention(CtrlIntention intention, Object arg0);
/**
* Sets the intention.
* @param intention the intention
* @param arg0 the arg0
* @param arg1 the arg1
*/
void setIntention(CtrlIntention intention, Object arg0, Object arg1);
/**
* Event, that notifies about previous step result, or user command, that does not change current general intention.
* @param evt the event
*/
void notifyEvent(CtrlEvent evt);
/**
* Notify an event.
* @param evt the event
* @param arg0 the arg0
*/
void notifyEvent(CtrlEvent evt, Object arg0);
/**
* Notify an event.
* @param evt the event
* @param arg0 the arg0
* @param arg1 the arg1
*/
void notifyEvent(CtrlEvent evt, Object arg0, Object arg1);
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.ai;
/**
* This class contains an enum of each possibles events that can happen on an AI character.
*/
public enum CtrlEvent
{
/**
* Something has changed, usually a previous step has being completed or maybe was completed, the AI must thing on next action.
*/
EVT_THINK,
/**
* The actor was attacked. This event comes each time a physical or magical<br>
* attack was done on the actor. NPC may start attack in response, or ignore<br>
* this event if they already attack someone, or change target and so on.
*/
EVT_ATTACKED,
/** Increase/decrease aggression towards a target, or reduce global aggression if target is null */
EVT_AGGRESSION,
/** Actor is in stun state */
EVT_STUNNED,
/** Actor is paralyzed or petrified */
EVT_PARALYZED,
/** Actor starts/stops sleeping */
EVT_SLEEPING,
/** Actor is in rooted state (cannot move) */
EVT_ROOTED,
/** Actor evaded hit **/
EVT_EVADED,
/**
* An event that previous action was completed. The action may be an attempt to physically/magically hit an enemy, or an action that discarded attack attempt has finished.
*/
EVT_READY_TO_ACT,
/**
* User's command, like using a combat magic or changing weapon, etc. The command is not intended to change final goal
*/
EVT_USER_CMD,
/**
* The actor arrived to assigned location, or it's a time to modify movement destination (follow, interact, random move and others intentions).
*/
EVT_ARRIVED,
/**
* The actor arrived to an intermediate point, and needs to revalidate destination. This is sent when follow/move to pawn if destination is far away.
*/
EVT_ARRIVED_REVALIDATE,
/** The actor cannot move anymore. */
EVT_ARRIVED_BLOCKED,
/** Forgets an object (if it's used as attack target, follow target and so on */
EVT_FORGET_OBJECT,
/**
* Attempt to cancel current step execution, but not change the intention.<br>
* For example, the actor was put into a stun, so it's current attack<br>
* or movement has to be canceled. But after the stun state expired,<br>
* the actor may try to attack again. Another usage for CANCEL is a user's<br>
* attempt to cancel a cast/bow attack and so on.
*/
EVT_CANCEL,
/** The character is dead */
EVT_DEAD,
/** The character looks like dead */
EVT_FAKE_DEATH,
/** The character attack anyone randomly **/
EVT_CONFUSED,
/** The character cannot cast spells anymore **/
EVT_MUTED,
/** The character flee in random directions **/
EVT_AFRAID,
/** The character finish casting **/
EVT_FINISH_CASTING,
/** The character betrayed its master */
EVT_BETRAYED
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.ai;
/**
* Enumeration of generic intentions of an NPC/PC, an intention may require several steps to be completed.
*/
public enum CtrlIntention
{
/** Do nothing, disconnect AI of NPC if no players around */
AI_INTENTION_IDLE,
/** Alerted state without goal : scan attackable targets, random walk, etc */
AI_INTENTION_ACTIVE,
/** Rest (sit until attacked) */
AI_INTENTION_REST,
/** Attack target (cast combat magic, go to target, combat), may be ignored, if target is locked on another character or a peaceful zone and so on. */
AI_INTENTION_ATTACK,
/** Cast a spell, depending on the spell - may start or stop attacking */
AI_INTENTION_CAST,
/** Just move to another location */
AI_INTENTION_MOVE_TO,
/** Like move, but check target's movement and follow it */
AI_INTENTION_FOLLOW,
/** PickUp and item, (got to item, pickup it, become idle */
AI_INTENTION_PICK_UP,
/** Move to target, then interact */
AI_INTENTION_INTERACT;
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.ai;
import com.l2jserver.gameserver.model.Location;
import com.l2jserver.gameserver.model.actor.instance.L2AirShipInstance;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.network.serverpackets.ExMoveToLocationAirShip;
import com.l2jserver.gameserver.network.serverpackets.ExStopMoveAirShip;
/**
* @author DS
*/
public class L2AirShipAI extends L2VehicleAI
{
public L2AirShipAI(L2AirShipInstance.AIAccessor accessor)
{
super(accessor);
}
@Override
protected void moveTo(int x, int y, int z)
{
if (!_actor.isMovementDisabled())
{
_clientMoving = true;
_accessor.moveTo(x, y, z);
_actor.broadcastPacket(new ExMoveToLocationAirShip(getActor()));
}
}
@Override
protected void clientStopMoving(Location loc)
{
if (_actor.isMoving())
{
_accessor.stopMove(loc);
}
if (_clientMoving || (loc != null))
{
_clientMoving = false;
_actor.broadcastPacket(new ExStopMoveAirShip(getActor()));
}
}
@Override
public void describeStateToPlayer(L2PcInstance player)
{
if (_clientMoving)
{
player.sendPacket(new ExMoveToLocationAirShip(getActor()));
}
}
@Override
public L2AirShipInstance getActor()
{
return (L2AirShipInstance) _actor;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,84 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.ai;
import com.l2jserver.gameserver.model.Location;
import com.l2jserver.gameserver.model.actor.instance.L2BoatInstance;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.network.serverpackets.VehicleDeparture;
import com.l2jserver.gameserver.network.serverpackets.VehicleInfo;
import com.l2jserver.gameserver.network.serverpackets.VehicleStarted;
/**
* @author DS
*/
public class L2BoatAI extends L2VehicleAI
{
public L2BoatAI(L2BoatInstance.AIAccessor accessor)
{
super(accessor);
}
@Override
protected void moveTo(int x, int y, int z)
{
if (!_actor.isMovementDisabled())
{
if (!_clientMoving)
{
_actor.broadcastPacket(new VehicleStarted(getActor(), 1));
}
_clientMoving = true;
_accessor.moveTo(x, y, z);
_actor.broadcastPacket(new VehicleDeparture(getActor()));
}
}
@Override
protected void clientStopMoving(Location loc)
{
if (_actor.isMoving())
{
_accessor.stopMove(loc);
}
if (_clientMoving || (loc != null))
{
_clientMoving = false;
_actor.broadcastPacket(new VehicleStarted(getActor(), 0));
_actor.broadcastPacket(new VehicleInfo(getActor()));
}
}
@Override
public void describeStateToPlayer(L2PcInstance player)
{
if (_clientMoving)
{
player.sendPacket(new VehicleDeparture(getActor()));
}
}
@Override
public L2BoatInstance getActor()
{
return (L2BoatInstance) _actor;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,583 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.ai;
import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE;
import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import java.util.Collection;
import java.util.List;
import javolution.util.FastList;
import com.l2jserver.gameserver.model.L2Object;
import com.l2jserver.gameserver.model.MobGroup;
import com.l2jserver.gameserver.model.MobGroupTable;
import com.l2jserver.gameserver.model.actor.L2Attackable;
import com.l2jserver.gameserver.model.actor.L2Character;
import com.l2jserver.gameserver.model.actor.L2Character.AIAccessor;
import com.l2jserver.gameserver.model.actor.L2Npc;
import com.l2jserver.gameserver.model.actor.L2Playable;
import com.l2jserver.gameserver.model.actor.instance.L2ControllableMobInstance;
import com.l2jserver.gameserver.model.actor.instance.L2DoorInstance;
import com.l2jserver.gameserver.model.actor.instance.L2NpcInstance;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.skills.Skill;
import com.l2jserver.gameserver.util.Util;
import com.l2jserver.util.Rnd;
/**
* AI for controllable mobs
* @author littlecrow
*/
public final class L2ControllableMobAI extends L2AttackableAI
{
public static final int AI_IDLE = 1;
public static final int AI_NORMAL = 2;
public static final int AI_FORCEATTACK = 3;
public static final int AI_FOLLOW = 4;
public static final int AI_CAST = 5;
public static final int AI_ATTACK_GROUP = 6;
private int _alternateAI;
private boolean _isThinking; // to prevent thinking recursively
private boolean _isNotMoving;
private L2Character _forcedTarget;
private MobGroup _targetGroup;
protected void thinkFollow()
{
L2Attackable me = (L2Attackable) _actor;
if (!Util.checkIfInRange(MobGroupTable.FOLLOW_RANGE, me, getForcedTarget(), true))
{
int signX = (Rnd.nextInt(2) == 0) ? -1 : 1;
int signY = (Rnd.nextInt(2) == 0) ? -1 : 1;
int randX = Rnd.nextInt(MobGroupTable.FOLLOW_RANGE);
int randY = Rnd.nextInt(MobGroupTable.FOLLOW_RANGE);
moveTo(getForcedTarget().getX() + (signX * randX), getForcedTarget().getY() + (signY * randY), getForcedTarget().getZ());
}
}
@Override
protected void onEvtThink()
{
if (isThinking())
{
return;
}
setThinking(true);
try
{
switch (getAlternateAI())
{
case AI_IDLE:
if (getIntention() != CtrlIntention.AI_INTENTION_ACTIVE)
{
setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
}
break;
case AI_FOLLOW:
thinkFollow();
break;
case AI_CAST:
thinkCast();
break;
case AI_FORCEATTACK:
thinkForceAttack();
break;
case AI_ATTACK_GROUP:
thinkAttackGroup();
break;
default:
if (getIntention() == AI_INTENTION_ACTIVE)
{
thinkActive();
}
else if (getIntention() == AI_INTENTION_ATTACK)
{
thinkAttack();
}
break;
}
}
finally
{
setThinking(false);
}
}
@Override
protected void thinkCast()
{
L2Attackable npc = (L2Attackable) _actor;
if ((getAttackTarget() == null) || getAttackTarget().isAlikeDead())
{
setAttackTarget(findNextRndTarget());
clientStopMoving(null);
}
if (getAttackTarget() == null)
{
return;
}
npc.setTarget(getAttackTarget());
if (!_actor.isMuted())
{
int max_range = 0;
// check distant skills
for (Skill sk : _actor.getAllSkills())
{
if (Util.checkIfInRange(sk.getCastRange(), _actor, getAttackTarget(), true) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() > _actor.getStat().getMpConsume(sk)))
{
_accessor.doCast(sk);
return;
}
max_range = Math.max(max_range, sk.getCastRange());
}
if (!isNotMoving())
{
moveToPawn(getAttackTarget(), max_range);
}
return;
}
}
protected void thinkAttackGroup()
{
L2Character target = getForcedTarget();
if ((target == null) || target.isAlikeDead())
{
// try to get next group target
setForcedTarget(findNextGroupTarget());
clientStopMoving(null);
}
if (target == null)
{
return;
}
_actor.setTarget(target);
// as a response, we put the target in a forcedattack mode
L2ControllableMobInstance theTarget = (L2ControllableMobInstance) target;
L2ControllableMobAI ctrlAi = (L2ControllableMobAI) theTarget.getAI();
ctrlAi.forceAttack(_actor);
double dist2 = _actor.calculateDistance(target, false, true);
int range = _actor.getPhysicalAttackRange() + _actor.getTemplate().getCollisionRadius() + target.getTemplate().getCollisionRadius();
int max_range = range;
if (!_actor.isMuted() && (dist2 > ((range + 20) * (range + 20))))
{
// check distant skills
for (Skill sk : _actor.getAllSkills())
{
int castRange = sk.getCastRange();
if (((castRange * castRange) >= dist2) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() > _actor.getStat().getMpConsume(sk)))
{
_accessor.doCast(sk);
return;
}
max_range = Math.max(max_range, castRange);
}
if (!isNotMoving())
{
moveToPawn(target, range);
}
return;
}
_accessor.doAttack(target);
}
protected void thinkForceAttack()
{
if ((getForcedTarget() == null) || getForcedTarget().isAlikeDead())
{
clientStopMoving(null);
setIntention(AI_INTENTION_ACTIVE);
setAlternateAI(AI_IDLE);
}
_actor.setTarget(getForcedTarget());
double dist2 = _actor.calculateDistance(getForcedTarget(), false, true);
int range = _actor.getPhysicalAttackRange() + _actor.getTemplate().getCollisionRadius() + getForcedTarget().getTemplate().getCollisionRadius();
int max_range = range;
if (!_actor.isMuted() && (dist2 > ((range + 20) * (range + 20))))
{
// check distant skills
for (Skill sk : _actor.getAllSkills())
{
int castRange = sk.getCastRange();
if (((castRange * castRange) >= dist2) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() > _actor.getStat().getMpConsume(sk)))
{
_accessor.doCast(sk);
return;
}
max_range = Math.max(max_range, castRange);
}
if (!isNotMoving())
{
moveToPawn(getForcedTarget(), _actor.getPhysicalAttackRange()/* range */);
}
return;
}
_accessor.doAttack(getForcedTarget());
}
@Override
protected void thinkAttack()
{
if ((getAttackTarget() == null) || getAttackTarget().isAlikeDead())
{
if (getAttackTarget() != null)
{
// stop hating
L2Attackable npc = (L2Attackable) _actor;
npc.stopHating(getAttackTarget());
}
setIntention(AI_INTENTION_ACTIVE);
}
else
{
// notify aggression
if (((L2Npc) _actor).getTemplate().getClans() != null)
{
Collection<L2Object> objs = _actor.getKnownList().getKnownObjects().values();
for (L2Object obj : objs)
{
if (!(obj instanceof L2Npc))
{
continue;
}
L2Npc npc = (L2Npc) obj;
if (!npc.isInMyClan((L2Npc) _actor))
{
continue;
}
if (_actor.isInsideRadius(npc, npc.getTemplate().getClanHelpRange(), false, true) && (Math.abs(getAttackTarget().getZ() - npc.getZ()) < 200))
{
npc.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, getAttackTarget(), 1);
}
}
}
_actor.setTarget(getAttackTarget());
double dist2 = _actor.calculateDistance(getAttackTarget(), false, true);
int range = _actor.getPhysicalAttackRange() + _actor.getTemplate().getCollisionRadius() + getAttackTarget().getTemplate().getCollisionRadius();
int max_range = range;
if (!_actor.isMuted() && (dist2 > ((range + 20) * (range + 20))))
{
// check distant skills
for (Skill sk : _actor.getAllSkills())
{
int castRange = sk.getCastRange();
if (((castRange * castRange) >= dist2) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() > _actor.getStat().getMpConsume(sk)))
{
_accessor.doCast(sk);
return;
}
max_range = Math.max(max_range, castRange);
}
moveToPawn(getAttackTarget(), range);
return;
}
// Force mobs to attack anybody if confused.
L2Character hated;
if (_actor.isConfused())
{
hated = findNextRndTarget();
}
else
{
hated = getAttackTarget();
}
if (hated == null)
{
setIntention(AI_INTENTION_ACTIVE);
return;
}
if (hated != getAttackTarget())
{
setAttackTarget(hated);
}
if (!_actor.isMuted() && (Rnd.nextInt(5) == 3))
{
for (Skill sk : _actor.getAllSkills())
{
int castRange = sk.getCastRange();
if (((castRange * castRange) >= dist2) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() < _actor.getStat().getMpConsume(sk)))
{
_accessor.doCast(sk);
return;
}
}
}
_accessor.doAttack(getAttackTarget());
}
}
@Override
protected void thinkActive()
{
setAttackTarget(findNextRndTarget());
L2Character hated;
if (_actor.isConfused())
{
hated = findNextRndTarget();
}
else
{
hated = getAttackTarget();
}
if (hated != null)
{
_actor.setRunning();
setIntention(CtrlIntention.AI_INTENTION_ATTACK, hated);
}
}
private boolean checkAutoAttackCondition(L2Character target)
{
if ((target == null) || !(_actor instanceof L2Attackable))
{
return false;
}
L2Attackable me = (L2Attackable) _actor;
if ((target instanceof L2NpcInstance) || (target instanceof L2DoorInstance))
{
return false;
}
if (target.isAlikeDead() || !me.isInsideRadius(target, me.getAggroRange(), false, false) || (Math.abs(_actor.getZ() - target.getZ()) > 100))
{
return false;
}
// Check if the target isn't invulnerable
if (target.isInvul())
{
return false;
}
// Spawn protection (only against mobs)
if ((target instanceof L2PcInstance) && ((L2PcInstance) target).isSpawnProtected())
{
return false;
}
// Check if the target is a L2Playable
if (target.isPlayable())
{
// Check if the target isn't in silent move mode
if (((L2Playable) target).isSilentMovingAffected())
{
return false;
}
}
if (target instanceof L2Npc)
{
return false;
}
return me.isAggressive();
}
private L2Character findNextRndTarget()
{
int aggroRange = ((L2Attackable) _actor).getAggroRange();
L2Attackable npc = (L2Attackable) _actor;
int npcX, npcY, targetX, targetY;
double dy, dx;
double dblAggroRange = aggroRange * aggroRange;
List<L2Character> potentialTarget = new FastList<>();
Collection<L2Object> objs = npc.getKnownList().getKnownObjects().values();
for (L2Object obj : objs)
{
if (!(obj instanceof L2Character))
{
continue;
}
npcX = npc.getX();
npcY = npc.getY();
targetX = obj.getX();
targetY = obj.getY();
dx = npcX - targetX;
dy = npcY - targetY;
if (((dx * dx) + (dy * dy)) > dblAggroRange)
{
continue;
}
L2Character target = (L2Character) obj;
if (checkAutoAttackCondition(target))
{
potentialTarget.add(target);
}
}
if (potentialTarget.isEmpty())
{
return null;
}
// we choose a random target
int choice = Rnd.nextInt(potentialTarget.size());
L2Character target = potentialTarget.get(choice);
return target;
}
private L2ControllableMobInstance findNextGroupTarget()
{
return getGroupTarget().getRandomMob();
}
public L2ControllableMobAI(AIAccessor accessor)
{
super(accessor);
setAlternateAI(AI_IDLE);
}
public int getAlternateAI()
{
return _alternateAI;
}
public void setAlternateAI(int _alternateai)
{
_alternateAI = _alternateai;
}
public void forceAttack(L2Character target)
{
setAlternateAI(AI_FORCEATTACK);
setForcedTarget(target);
}
public void forceAttackGroup(MobGroup group)
{
setForcedTarget(null);
setGroupTarget(group);
setAlternateAI(AI_ATTACK_GROUP);
}
public void stop()
{
setAlternateAI(AI_IDLE);
clientStopMoving(null);
}
public void move(int x, int y, int z)
{
moveTo(x, y, z);
}
public void follow(L2Character target)
{
setAlternateAI(AI_FOLLOW);
setForcedTarget(target);
}
public boolean isThinking()
{
return _isThinking;
}
public boolean isNotMoving()
{
return _isNotMoving;
}
public void setNotMoving(boolean isNotMoving)
{
_isNotMoving = isNotMoving;
}
public void setThinking(boolean isThinking)
{
_isThinking = isThinking;
}
private L2Character getForcedTarget()
{
return _forcedTarget;
}
private MobGroup getGroupTarget()
{
return _targetGroup;
}
private void setForcedTarget(L2Character forcedTarget)
{
_forcedTarget = forcedTarget;
}
private void setGroupTarget(MobGroup targetGroup)
{
_targetGroup = targetGroup;
}
}

View File

@@ -0,0 +1,181 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.ai;
import com.l2jserver.gameserver.ThreadPoolManager;
import com.l2jserver.gameserver.model.L2Object;
import com.l2jserver.gameserver.model.Location;
import com.l2jserver.gameserver.model.actor.L2Character;
import com.l2jserver.gameserver.model.actor.instance.L2DefenderInstance;
import com.l2jserver.gameserver.model.actor.instance.L2DoorInstance;
import com.l2jserver.gameserver.model.skills.Skill;
/**
* @author mkizub
*/
public class L2DoorAI extends L2CharacterAI
{
public L2DoorAI(L2DoorInstance.AIAccessor accessor)
{
super(accessor);
}
// rather stupid AI... well, it's for doors :D
@Override
protected void onIntentionIdle()
{
}
@Override
protected void onIntentionActive()
{
}
@Override
protected void onIntentionRest()
{
}
@Override
protected void onIntentionAttack(L2Character target)
{
}
@Override
protected void onIntentionCast(Skill skill, L2Object target)
{
}
@Override
protected void onIntentionMoveTo(Location destination)
{
}
@Override
protected void onIntentionFollow(L2Character target)
{
}
@Override
protected void onIntentionPickUp(L2Object item)
{
}
@Override
protected void onIntentionInteract(L2Object object)
{
}
@Override
protected void onEvtThink()
{
}
@Override
protected void onEvtAttacked(L2Character attacker)
{
L2DoorInstance me = (L2DoorInstance) _actor;
ThreadPoolManager.getInstance().executeGeneral(new onEventAttackedDoorTask(me, attacker));
}
@Override
protected void onEvtAggression(L2Character target, int aggro)
{
}
@Override
protected void onEvtStunned(L2Character attacker)
{
}
@Override
protected void onEvtSleeping(L2Character attacker)
{
}
@Override
protected void onEvtRooted(L2Character attacker)
{
}
@Override
protected void onEvtReadyToAct()
{
}
@Override
protected void onEvtUserCmd(Object arg0, Object arg1)
{
}
@Override
protected void onEvtArrived()
{
}
@Override
protected void onEvtArrivedRevalidate()
{
}
@Override
protected void onEvtArrivedBlocked(Location blocked_at_loc)
{
}
@Override
protected void onEvtForgetObject(L2Object object)
{
}
@Override
protected void onEvtCancel()
{
}
@Override
protected void onEvtDead()
{
}
private class onEventAttackedDoorTask implements Runnable
{
private final L2DoorInstance _door;
private final L2Character _attacker;
public onEventAttackedDoorTask(L2DoorInstance door, L2Character attacker)
{
_door = door;
_attacker = attacker;
}
@Override
public void run()
{
for (L2DefenderInstance guard : _door.getKnownDefenders())
{
if (_actor.isInsideRadius(guard, guard.getTemplate().getClanHelpRange(), false, true) && (Math.abs(_attacker.getZ() - guard.getZ()) < 200))
{
guard.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, _attacker, 15);
}
}
}
}
}

View File

@@ -0,0 +1,969 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.ai;
import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE;
import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
import java.util.Collection;
import java.util.concurrent.Future;
import com.l2jserver.Config;
import com.l2jserver.gameserver.GameTimeController;
import com.l2jserver.gameserver.GeoData;
import com.l2jserver.gameserver.ThreadPoolManager;
import com.l2jserver.gameserver.model.L2Object;
import com.l2jserver.gameserver.model.actor.L2Attackable;
import com.l2jserver.gameserver.model.actor.L2Character;
import com.l2jserver.gameserver.model.actor.L2Npc;
import com.l2jserver.gameserver.model.actor.L2Playable;
import com.l2jserver.gameserver.model.actor.L2Summon;
import com.l2jserver.gameserver.model.actor.instance.L2DefenderInstance;
import com.l2jserver.gameserver.model.actor.instance.L2DoorInstance;
import com.l2jserver.gameserver.model.actor.instance.L2FortCommanderInstance;
import com.l2jserver.gameserver.model.actor.instance.L2NpcInstance;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.effects.L2EffectType;
import com.l2jserver.gameserver.model.skills.Skill;
import com.l2jserver.gameserver.util.Util;
import com.l2jserver.util.Rnd;
/**
* This class manages AI of L2Attackable.
*/
public class L2FortSiegeGuardAI extends L2CharacterAI implements Runnable
{
private static final int MAX_ATTACK_TIMEOUT = 300; // int ticks, i.e. 30 seconds
/** The L2Attackable AI task executed every 1s (call onEvtThink method) */
private Future<?> _aiTask;
/** For attack AI, analysis of mob and its targets */
private final SelfAnalysis _selfAnalysis = new SelfAnalysis();
/** The delay after which the attacked is stopped */
private int _attackTimeout;
/** The L2Attackable aggro counter */
private int _globalAggro;
/** The flag used to indicate that a thinking action is in progress */
private boolean _thinking; // to prevent recursive thinking
private final int _attackRange;
/**
* Constructor of L2AttackableAI.
* @param accessor The AI accessor of the L2Character
*/
public L2FortSiegeGuardAI(L2Character.AIAccessor accessor)
{
super(accessor);
_selfAnalysis.init();
_attackTimeout = Integer.MAX_VALUE;
_globalAggro = -10; // 10 seconds timeout of ATTACK after respawn
_attackRange = _actor.getPhysicalAttackRange();
}
@Override
public void run()
{
// Launch actions corresponding to the Event Think
onEvtThink();
}
/**
* <B><U> Actor is a L2GuardInstance</U> :</B>
* <ul>
* <li>The target isn't a Folk or a Door</li>
* <li>The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)</li>
* <li>The target is in the actor Aggro range and is at the same height</li>
* <li>The L2PcInstance target has karma (=PK)</li>
* <li>The L2MonsterInstance target is aggressive</li>
* </ul>
* <B><U> Actor is a L2SiegeGuardInstance</U> :</B>
* <ul>
* <li>The target isn't a Folk or a Door</li>
* <li>The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)</li>
* <li>The target is in the actor Aggro range and is at the same height</li>
* <li>A siege is in progress</li>
* <li>The L2PcInstance target isn't a Defender</li>
* </ul>
* <B><U> Actor is a L2FriendlyMobInstance</U> :</B>
* <ul>
* <li>The target isn't a Folk, a Door or another L2NpcInstance</li>
* <li>The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)</li>
* <li>The target is in the actor Aggro range and is at the same height</li>
* <li>The L2PcInstance target has karma (=PK)</li>
* </ul>
* <B><U> Actor is a L2MonsterInstance</U> :</B>
* <ul>
* <li>The target isn't a Folk, a Door or another L2NpcInstance</li>
* <li>The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)</li>
* <li>The target is in the actor Aggro range and is at the same height</li>
* <li>The actor is Aggressive</li>
* </ul>
* @param target The targeted L2Object
* @return True if the target is autoattackable (depends on the actor type).
*/
private boolean autoAttackCondition(L2Character target)
{
// Check if the target isn't another guard, folk or a door
if ((target == null) || (target instanceof L2DefenderInstance) || (target instanceof L2NpcInstance) || (target instanceof L2DoorInstance) || target.isAlikeDead() || (target instanceof L2FortCommanderInstance) || (target instanceof L2Playable))
{
L2PcInstance player = null;
if (target instanceof L2PcInstance)
{
player = ((L2PcInstance) target);
}
else if (target instanceof L2Summon)
{
player = ((L2Summon) target).getOwner();
}
if ((player == null) || ((player.getClan() != null) && (player.getClan().getFortId() == ((L2Npc) _actor).getFort().getResidenceId())))
{
return false;
}
}
// Check if the target isn't invulnerable
if ((target != null) && target.isInvul())
{
// However EffectInvincible requires to check GMs specially
if ((target instanceof L2PcInstance) && target.isGM())
{
return false;
}
if ((target instanceof L2Summon) && ((L2Summon) target).getOwner().isGM())
{
return false;
}
}
// Get the owner if the target is a summon
if (target instanceof L2Summon)
{
L2PcInstance owner = ((L2Summon) target).getOwner();
if (_actor.isInsideRadius(owner, 1000, true, false))
{
target = owner;
}
}
// Check if the target is a L2PcInstance
if (target instanceof L2Playable)
{
// Check if the target isn't in silent move mode AND too far (>100)
if (((L2Playable) target).isSilentMovingAffected() && !_actor.isInsideRadius(target, 250, false, false))
{
return false;
}
}
// Los Check Here
return (_actor.isAutoAttackable(target) && GeoData.getInstance().canSeeTarget(_actor, target));
}
/**
* Set the Intention of this L2CharacterAI and create an AI Task executed every 1s (call onEvtThink method) for this L2Attackable.<br>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : If actor _knowPlayer isn't EMPTY, AI_INTENTION_IDLE will be change in AI_INTENTION_ACTIVE</B></FONT>
* @param intention The new Intention to set to the AI
* @param arg0 The first parameter of the Intention
* @param arg1 The second parameter of the Intention
*/
@Override
synchronized void changeIntention(CtrlIntention intention, Object arg0, Object arg1)
{
if (Config.DEBUG)
{
_log.warning(getClass().getSimpleName() + ": changeIntention(" + intention + ", " + arg0 + ", " + arg1 + ")");
}
if (intention == AI_INTENTION_IDLE /* || intention == AI_INTENTION_ACTIVE */) // active becomes idle if only a summon is present
{
// Check if actor is not dead
if (!_actor.isAlikeDead())
{
L2Attackable npc = (L2Attackable) _actor;
// If its _knownPlayer isn't empty set the Intention to AI_INTENTION_ACTIVE
if (!npc.getKnownList().getKnownPlayers().isEmpty())
{
intention = AI_INTENTION_ACTIVE;
}
else
{
intention = AI_INTENTION_IDLE;
}
}
if (intention == AI_INTENTION_IDLE)
{
// Set the Intention of this L2AttackableAI to AI_INTENTION_IDLE
super.changeIntention(AI_INTENTION_IDLE, null, null);
// Stop AI task and detach AI from NPC
if (_aiTask != null)
{
_aiTask.cancel(true);
_aiTask = null;
}
// Cancel the AI
_accessor.detachAI();
return;
}
}
// Set the Intention of this L2AttackableAI to intention
super.changeIntention(intention, arg0, arg1);
// If not idle - create an AI task (schedule onEvtThink repeatedly)
if (_aiTask == null)
{
_aiTask = ThreadPoolManager.getInstance().scheduleAiAtFixedRate(this, 1000, 1000);
}
}
/**
* Manage the Attack Intention : Stop current Attack (if necessary), Calculate attack timeout, Start a new Attack and Launch Think Event.
* @param target The L2Character to attack
*/
@Override
protected void onIntentionAttack(L2Character target)
{
// Calculate the attack timeout
_attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
// Manage the Attack Intention : Stop current Attack (if necessary), Start a new Attack and Launch Think Event
// if (_actor.getTarget() != null)
super.onIntentionAttack(target);
}
/**
* Manage AI standard thinks of a L2Attackable (called by onEvtThink).<br>
* <B><U> Actions</U> :</B>
* <ul>
* <li>Update every 1s the _globalAggro counter to come close to 0</li>
* <li>If the actor is Aggressive and can attack, add all autoAttackable L2Character in its Aggro Range to its _aggroList, chose a target and order to attack it</li>
* <li>If the actor can't attack, order to it to return to its home location</li>
* </ul>
*/
private void thinkActive()
{
L2Attackable npc = (L2Attackable) _actor;
// Update every 1s the _globalAggro counter to come close to 0
if (_globalAggro != 0)
{
if (_globalAggro < 0)
{
_globalAggro++;
}
else
{
_globalAggro--;
}
}
// Add all autoAttackable L2Character in L2Attackable Aggro Range to its _aggroList with 0 damage and 1 hate
// A L2Attackable isn't aggressive during 10s after its spawn because _globalAggro is set to -10
if (_globalAggro >= 0)
{
for (L2Character target : npc.getKnownList().getKnownCharactersInRadius(_attackRange))
{
if (target == null)
{
continue;
}
if (autoAttackCondition(target)) // check aggression
{
// Get the hate level of the L2Attackable against this L2Character target contained in _aggroList
int hating = npc.getHating(target);
// Add the attacker to the L2Attackable _aggroList with 0 damage and 1 hate
if (hating == 0)
{
npc.addDamageHate(target, 0, 1);
}
}
}
// Chose a target from its aggroList
L2Character hated;
if (_actor.isConfused())
{
hated = getAttackTarget(); // Force mobs to attack anybody if confused
}
else
{
hated = npc.getMostHated();
// _mostHatedAnalysis.Update(hated);
}
// Order to the L2Attackable to attack the target
if (hated != null)
{
// Get the hate level of the L2Attackable against this L2Character target contained in _aggroList
int aggro = npc.getHating(hated);
if ((aggro + _globalAggro) > 0)
{
// Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
if (!_actor.isRunning())
{
_actor.setRunning();
}
// Set the AI Intention to AI_INTENTION_ATTACK
setIntention(CtrlIntention.AI_INTENTION_ATTACK, hated, null);
}
return;
}
}
// Order to the L2SiegeGuardInstance to return to its home location because there's no target to attack
if (_actor.getWalkSpeed() >= 0)
{
if (_actor instanceof L2DefenderInstance)
{
((L2DefenderInstance) _actor).returnHome();
}
else
{
((L2FortCommanderInstance) _actor).returnHome();
}
}
}
/**
* Manage AI attack thinks of a L2Attackable (called by onEvtThink).<br>
* <B><U> Actions</U> :</B>
* <ul>
* <li>Update the attack timeout if actor is running</li>
* <li>If target is dead or timeout is expired, stop this attack and set the Intention to AI_INTENTION_ACTIVE</li>
* <li>Call all L2Object of its Faction inside the Faction Range</li>
* <li>Chose a target and order to attack it with magic skill or physical attack</li>
* </ul>
* TODO: Manage casting rules to healer mobs (like Ant Nurses)
*/
private void thinkAttack()
{
if (Config.DEBUG)
{
_log.warning(getClass().getSimpleName() + ": thinkAttack(); timeout=" + (_attackTimeout - GameTimeController.getInstance().getGameTicks()));
}
if (_attackTimeout < GameTimeController.getInstance().getGameTicks())
{
// Check if the actor is running
if (_actor.isRunning())
{
// Set the actor movement type to walk and send Server->Client packet ChangeMoveType to all others L2PcInstance
_actor.setWalking();
// Calculate a new attack timeout
_attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
}
}
L2Character attackTarget = getAttackTarget();
// Check if target is dead or if timeout is expired to stop this attack
if ((attackTarget == null) || attackTarget.isAlikeDead() || (_attackTimeout < GameTimeController.getInstance().getGameTicks()))
{
// Stop hating this target after the attack timeout or if target is dead
if (attackTarget != null)
{
L2Attackable npc = (L2Attackable) _actor;
npc.stopHating(attackTarget);
}
// Cancel target and timeout
_attackTimeout = Integer.MAX_VALUE;
setAttackTarget(null);
// Set the AI Intention to AI_INTENTION_ACTIVE
setIntention(AI_INTENTION_ACTIVE, null, null);
_actor.setWalking();
return;
}
factionNotifyAndSupport();
attackPrepare();
}
private final void factionNotifyAndSupport()
{
L2Character target = getAttackTarget();
// Call all L2Object of its Faction inside the Faction Range
if ((((L2Npc) _actor).getTemplate().getClans() == null) || (target == null))
{
return;
}
if (target.isInvul())
{
return; // speeding it up for siege guards
}
// Go through all L2Character that belong to its faction
// for (L2Character cha : _actor.getKnownList().getKnownCharactersInRadius(((L2NpcInstance) _actor).getFactionRange()+_actor.getTemplate().collisionRadius))
for (L2Character cha : _actor.getKnownList().getKnownCharactersInRadius(1000))
{
if (cha == null)
{
continue;
}
if (!(cha instanceof L2Npc))
{
if (_selfAnalysis.hasHealOrResurrect && (cha instanceof L2PcInstance) && ((L2Npc) _actor).getFort().getSiege().checkIsDefender(((L2PcInstance) cha).getClan()))
{
// heal friends
if (!_actor.isAttackingDisabled() && (cha.getCurrentHp() < (cha.getMaxHp() * 0.6)) && (_actor.getCurrentHp() > (_actor.getMaxHp() / 2)) && (_actor.getCurrentMp() > (_actor.getMaxMp() / 2)) && cha.isInCombat())
{
for (Skill sk : _selfAnalysis.healSkills)
{
if (_actor.getCurrentMp() < sk.getMpConsume())
{
continue;
}
if (_actor.isSkillDisabled(sk))
{
continue;
}
if (!Util.checkIfInRange(sk.getCastRange(), _actor, cha, true))
{
continue;
}
int chance = 5;
if (chance >= Rnd.get(100))
{
continue;
}
if (!GeoData.getInstance().canSeeTarget(_actor, cha))
{
break;
}
L2Object OldTarget = _actor.getTarget();
_actor.setTarget(cha);
clientStopMoving(null);
_accessor.doCast(sk);
_actor.setTarget(OldTarget);
return;
}
}
}
continue;
}
L2Npc npc = (L2Npc) cha;
if (!npc.isInMyClan((L2Npc) _actor))
{
continue;
}
if (npc.getAI() != null) // TODO: possibly check not needed
{
if (!npc.isDead() && (Math.abs(target.getZ() - npc.getZ()) < 600)
// && _actor.getAttackByList().contains(getAttackTarget())
&& ((npc.getAI()._intention == CtrlIntention.AI_INTENTION_IDLE) || (npc.getAI()._intention == CtrlIntention.AI_INTENTION_ACTIVE))
// limiting aggro for siege guards
&& target.isInsideRadius(npc, 1500, true, false) && GeoData.getInstance().canSeeTarget(npc, target))
{
// Notify the L2Object AI with EVT_AGGRESSION
npc.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, getAttackTarget(), 1);
return;
}
// heal friends
if (_selfAnalysis.hasHealOrResurrect && !_actor.isAttackingDisabled() && (npc.getCurrentHp() < (npc.getMaxHp() * 0.6)) && (_actor.getCurrentHp() > (_actor.getMaxHp() / 2)) && (_actor.getCurrentMp() > (_actor.getMaxMp() / 2)) && npc.isInCombat())
{
for (Skill sk : _selfAnalysis.healSkills)
{
if (_actor.getCurrentMp() < sk.getMpConsume())
{
continue;
}
if (_actor.isSkillDisabled(sk))
{
continue;
}
if (!Util.checkIfInRange(sk.getCastRange(), _actor, npc, true))
{
continue;
}
int chance = 4;
if (chance >= Rnd.get(100))
{
continue;
}
if (!GeoData.getInstance().canSeeTarget(_actor, npc))
{
break;
}
L2Object OldTarget = _actor.getTarget();
_actor.setTarget(npc);
clientStopMoving(null);
_accessor.doCast(sk);
_actor.setTarget(OldTarget);
return;
}
}
}
}
}
private void attackPrepare()
{
// Get all information needed to choose between physical or magical attack
Collection<Skill> skills = null;
double dist_2 = 0;
int range = 0;
L2DefenderInstance sGuard;
if (_actor instanceof L2FortCommanderInstance)
{
sGuard = (L2FortCommanderInstance) _actor;
}
else
{
sGuard = (L2DefenderInstance) _actor;
}
L2Character attackTarget = getAttackTarget();
try
{
_actor.setTarget(attackTarget);
skills = _actor.getAllSkills();
dist_2 = _actor.calculateDistance(attackTarget, false, true);
range = _actor.getPhysicalAttackRange() + _actor.getTemplate().getCollisionRadius() + attackTarget.getTemplate().getCollisionRadius();
if (attackTarget.isMoving())
{
range += 50;
}
}
catch (NullPointerException e)
{
// _log.warning("AttackableAI: Attack target is NULL.");
_actor.setTarget(null);
setIntention(AI_INTENTION_IDLE, null, null);
return;
}
// never attack defenders
if ((attackTarget instanceof L2PcInstance) && sGuard.getFort().getSiege().checkIsDefender(((L2PcInstance) attackTarget).getClan()))
{
// Cancel the target
sGuard.stopHating(attackTarget);
_actor.setTarget(null);
setIntention(AI_INTENTION_IDLE, null, null);
return;
}
if (!GeoData.getInstance().canSeeTarget(_actor, attackTarget))
{
// Siege guards differ from normal mobs currently:
// If target cannot seen, don't attack any more
sGuard.stopHating(attackTarget);
_actor.setTarget(null);
setIntention(AI_INTENTION_IDLE, null, null);
return;
}
// Check if the actor isn't muted and if it is far from target
if (!_actor.isMuted() && (dist_2 > (range * range)))
{
// check for long ranged skills and heal/buff skills
for (Skill sk : skills)
{
int castRange = sk.getCastRange();
if ((dist_2 <= (castRange * castRange)) && (castRange > 70) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !sk.isPassive())
{
L2Object OldTarget = _actor.getTarget();
if ((sk.isContinuous() && !sk.isDebuff()) || (sk.hasEffectType(L2EffectType.HEAL)))
{
boolean useSkillSelf = true;
if ((sk.hasEffectType(L2EffectType.HEAL)) && (_actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5)))
{
useSkillSelf = false;
break;
}
if ((sk.isContinuous() && !sk.isDebuff()) && _actor.isAffectedBySkill(sk.getId()))
{
useSkillSelf = false;
}
if (useSkillSelf)
{
_actor.setTarget(_actor);
}
}
clientStopMoving(null);
_accessor.doCast(sk);
_actor.setTarget(OldTarget);
return;
}
}
// Check if the L2SiegeGuardInstance is attacking, knows the target and can't run
if (!(_actor.isAttackingNow()) && (_actor.getRunSpeed() == 0) && (_actor.getKnownList().knowsObject(attackTarget)))
{
// Cancel the target
_actor.getKnownList().removeKnownObject(attackTarget);
_actor.setTarget(null);
setIntention(AI_INTENTION_IDLE, null, null);
}
else
{
double dx = _actor.getX() - attackTarget.getX();
double dy = _actor.getY() - attackTarget.getY();
double dz = _actor.getZ() - attackTarget.getZ();
double homeX = attackTarget.getX() - sGuard.getSpawn().getX();
double homeY = attackTarget.getY() - sGuard.getSpawn().getY();
// Check if the L2SiegeGuardInstance isn't too far from it's home location
if ((((dx * dx) + (dy * dy)) > 10000) && (((homeX * homeX) + (homeY * homeY)) > 3240000) // 1800 * 1800
&& (_actor.getKnownList().knowsObject(attackTarget)))
{
// Cancel the target
_actor.getKnownList().removeKnownObject(attackTarget);
_actor.setTarget(null);
setIntention(AI_INTENTION_IDLE, null, null);
}
else
// Move the actor to Pawn server side AND client side by sending Server->Client packet MoveToPawn (broadcast)
{
// Temporary hack for preventing guards jumping off towers,
// before replacing this with effective geodata checks and AI modification
if ((dz * dz) < (170 * 170)) // normally 130 if guard z coordinates correct
{
if (_selfAnalysis.isMage)
{
range = _selfAnalysis.maxCastRange - 50;
}
if (_actor.getWalkSpeed() <= 0)
{
return;
}
if (attackTarget.isMoving())
{
moveToPawn(attackTarget, range - 70);
}
else
{
moveToPawn(attackTarget, range);
}
}
}
}
return;
}
// Else, if the actor is muted and far from target, just "move to pawn"
else if (_actor.isMuted() && (dist_2 > (range * range)))
{
// Temporary hack for preventing guards jumping off towers,
// before replacing this with effective geodata checks and AI modification
double dz = _actor.getZ() - attackTarget.getZ();
if ((dz * dz) < (170 * 170)) // normally 130 if guard z coordinates correct
{
if (_selfAnalysis.isMage)
{
range = _selfAnalysis.maxCastRange - 50;
}
if (_actor.getWalkSpeed() <= 0)
{
return;
}
if (attackTarget.isMoving())
{
moveToPawn(attackTarget, range - 70);
}
else
{
moveToPawn(attackTarget, range);
}
}
return;
}
// Else, if this is close enough to attack
else if (dist_2 <= (range * range))
{
// Force mobs to attack anybody if confused
L2Character hated = null;
if (_actor.isConfused())
{
hated = attackTarget;
}
else
{
hated = ((L2Attackable) _actor).getMostHated();
}
if (hated == null)
{
setIntention(AI_INTENTION_ACTIVE, null, null);
return;
}
if (hated != attackTarget)
{
attackTarget = hated;
}
_attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
// check for close combat skills && heal/buff skills
if (!_actor.isMuted() && (Rnd.nextInt(100) <= 5))
{
for (Skill sk : skills)
{
int castRange = sk.getCastRange();
if (((castRange * castRange) >= dist_2) && !sk.isPassive() && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !_actor.isSkillDisabled(sk))
{
L2Object OldTarget = _actor.getTarget();
if ((sk.isContinuous() && !sk.isDebuff()) || (sk.hasEffectType(L2EffectType.HEAL)))
{
boolean useSkillSelf = true;
if ((sk.hasEffectType(L2EffectType.HEAL)) && (_actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5)))
{
useSkillSelf = false;
break;
}
if ((sk.isContinuous() && !sk.isDebuff()) && _actor.isAffectedBySkill(sk.getId()))
{
useSkillSelf = false;
}
if (useSkillSelf)
{
_actor.setTarget(_actor);
}
}
clientStopMoving(null);
_accessor.doCast(sk);
_actor.setTarget(OldTarget);
return;
}
}
}
// Finally, do the physical attack itself
_accessor.doAttack(attackTarget);
}
}
/**
* Manage AI thinking actions of a L2Attackable.
*/
@Override
protected void onEvtThink()
{
// if(getIntention() != AI_INTENTION_IDLE && (!_actor.isVisible() || !_actor.hasAI() || !_actor.isKnownPlayers()))
// setIntention(AI_INTENTION_IDLE);
// Check if the actor can't use skills and if a thinking action isn't already in progress
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
return;
}
// Start thinking action
_thinking = true;
try
{
// Manage AI thinks of a L2Attackable
if (getIntention() == AI_INTENTION_ACTIVE)
{
thinkActive();
}
else if (getIntention() == AI_INTENTION_ATTACK)
{
thinkAttack();
}
}
finally
{
// Stop thinking action
_thinking = false;
}
}
/**
* Launch actions corresponding to the Event Attacked.<br>
* <B><U> Actions</U> :</B>
* <ul>
* <li>Init the attack : Calculate the attack timeout, Set the _globalAggro to 0, Add the attacker to the actor _aggroList</li>
* <li>Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance</li>
* <li>Set the Intention to AI_INTENTION_ATTACK</li>
* </ul>
* @param attacker The L2Character that attacks the actor
*/
@Override
protected void onEvtAttacked(L2Character attacker)
{
// Calculate the attack timeout
_attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
// Set the _globalAggro to 0 to permit attack even just after spawn
if (_globalAggro < 0)
{
_globalAggro = 0;
}
// Add the attacker to the _aggroList of the actor
((L2Attackable) _actor).addDamageHate(attacker, 0, 1);
// Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
if (!_actor.isRunning())
{
_actor.setRunning();
}
// Set the Intention to AI_INTENTION_ATTACK
if (getIntention() != AI_INTENTION_ATTACK)
{
setIntention(CtrlIntention.AI_INTENTION_ATTACK, attacker, null);
}
super.onEvtAttacked(attacker);
}
/**
* Launch actions corresponding to the Event Aggression.<br>
* <B><U> Actions</U> :</B>
* <ul>
* <li>Add the target to the actor _aggroList or update hate if already present</li>
* <li>Set the actor Intention to AI_INTENTION_ATTACK (if actor is L2GuardInstance check if it isn't too far from its home location)</li>
* </ul>
* @param aggro The value of hate to add to the actor against the target
*/
@Override
protected void onEvtAggression(L2Character target, int aggro)
{
if (_actor == null)
{
return;
}
L2Attackable me = (L2Attackable) _actor;
if (target != null)
{
// Add the target to the actor _aggroList or update hate if already present
me.addDamageHate(target, 0, aggro);
// Get the hate of the actor against the target
aggro = me.getHating(target);
if (aggro <= 0)
{
if (me.getMostHated() == null)
{
_globalAggro = -25;
me.clearAggroList();
setIntention(AI_INTENTION_IDLE, null, null);
}
return;
}
// Set the actor AI Intention to AI_INTENTION_ATTACK
if (getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
{
// Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
if (!_actor.isRunning())
{
_actor.setRunning();
}
L2DefenderInstance sGuard;
if (_actor instanceof L2FortCommanderInstance)
{
sGuard = (L2FortCommanderInstance) _actor;
}
else
{
sGuard = (L2DefenderInstance) _actor;
}
double homeX = target.getX() - sGuard.getSpawn().getX();
double homeY = target.getY() - sGuard.getSpawn().getY();
// Check if the L2SiegeGuardInstance is not too far from its home location
if (((homeX * homeX) + (homeY * homeY)) < 3240000)
{
setIntention(CtrlIntention.AI_INTENTION_ATTACK, target, null);
}
}
}
else
{
// currently only for setting lower general aggro
if (aggro >= 0)
{
return;
}
L2Character mostHated = me.getMostHated();
if (mostHated == null)
{
_globalAggro = -25;
return;
}
for (L2Character aggroed : me.getAggroList().keySet())
{
me.addDamageHate(aggroed, 0, aggro);
}
aggro = me.getHating(mostHated);
if (aggro <= 0)
{
_globalAggro = -25;
me.clearAggroList();
setIntention(AI_INTENTION_IDLE, null, null);
}
}
}
@Override
public void stopAITask()
{
if (_aiTask != null)
{
_aiTask.cancel(false);
_aiTask = null;
}
_accessor.detachAI();
super.stopAITask();
}
}

View File

@@ -0,0 +1,121 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.ai;
import com.l2jserver.gameserver.model.L2Object;
import com.l2jserver.gameserver.model.actor.L2Character;
import com.l2jserver.gameserver.model.actor.L2Character.AIAccessor;
import com.l2jserver.gameserver.model.actor.L2Playable;
import com.l2jserver.gameserver.model.skills.Skill;
import com.l2jserver.gameserver.model.zone.ZoneId;
import com.l2jserver.gameserver.network.SystemMessageId;
/**
* This class manages AI of L2Playable.<br>
* L2PlayableAI : <li>L2SummonAI</li> <li>L2PlayerAI</li>
* @author JIV
*/
public abstract class L2PlayableAI extends L2CharacterAI
{
/**
* @param accessor
*/
public L2PlayableAI(AIAccessor accessor)
{
super(accessor);
}
@Override
protected void onIntentionAttack(L2Character target)
{
if (target instanceof L2Playable)
{
if (target.getActingPlayer().isProtectionBlessingAffected() && ((_actor.getActingPlayer().getLevel() - target.getActingPlayer().getLevel()) >= 10) && (_actor.getActingPlayer().getKarma() > 0) && !(target.isInsideZone(ZoneId.PVP)))
{
// If attacker have karma and have level >= 10 than his target and target have
// Newbie Protection Buff,
_actor.getActingPlayer().sendPacket(SystemMessageId.THAT_IS_AN_INCORRECT_TARGET);
clientActionFailed();
return;
}
if (_actor.getActingPlayer().isProtectionBlessingAffected() && ((target.getActingPlayer().getLevel() - _actor.getActingPlayer().getLevel()) >= 10) && (target.getActingPlayer().getKarma() > 0) && !(target.isInsideZone(ZoneId.PVP)))
{
// If target have karma and have level >= 10 than his target and actor have
// Newbie Protection Buff,
_actor.getActingPlayer().sendPacket(SystemMessageId.THAT_IS_AN_INCORRECT_TARGET);
clientActionFailed();
return;
}
if (target.getActingPlayer().isCursedWeaponEquipped() && (_actor.getActingPlayer().getLevel() <= 20))
{
_actor.getActingPlayer().sendPacket(SystemMessageId.THAT_IS_AN_INCORRECT_TARGET);
clientActionFailed();
return;
}
if (_actor.getActingPlayer().isCursedWeaponEquipped() && (target.getActingPlayer().getLevel() <= 20))
{
_actor.getActingPlayer().sendPacket(SystemMessageId.THAT_IS_AN_INCORRECT_TARGET);
clientActionFailed();
return;
}
}
super.onIntentionAttack(target);
}
@Override
protected void onIntentionCast(Skill skill, L2Object target)
{
if ((target instanceof L2Playable) && skill.isBad())
{
if (target.getActingPlayer().isProtectionBlessingAffected() && ((_actor.getActingPlayer().getLevel() - target.getActingPlayer().getLevel()) >= 10) && (_actor.getActingPlayer().getKarma() > 0) && !target.isInsideZone(ZoneId.PVP))
{
// If attacker have karma and have level >= 10 than his target and target have
// Newbie Protection Buff,
_actor.getActingPlayer().sendPacket(SystemMessageId.THAT_IS_AN_INCORRECT_TARGET);
clientActionFailed();
_actor.setIsCastingNow(false);
return;
}
if (_actor.getActingPlayer().isProtectionBlessingAffected() && ((target.getActingPlayer().getLevel() - _actor.getActingPlayer().getLevel()) >= 10) && (target.getActingPlayer().getKarma() > 0) && !target.isInsideZone(ZoneId.PVP))
{
// If target have karma and have level >= 10 than his target and actor have
// Newbie Protection Buff,
_actor.getActingPlayer().sendPacket(SystemMessageId.THAT_IS_AN_INCORRECT_TARGET);
clientActionFailed();
_actor.setIsCastingNow(false);
return;
}
if (target.getActingPlayer().isCursedWeaponEquipped() && ((_actor.getActingPlayer().getLevel() <= 20) || (target.getActingPlayer().getLevel() <= 20)))
{
_actor.getActingPlayer().sendPacket(SystemMessageId.THAT_IS_AN_INCORRECT_TARGET);
clientActionFailed();
_actor.setIsCastingNow(false);
return;
}
}
super.onIntentionCast(skill, target);
}
}

View File

@@ -0,0 +1,358 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.ai;
import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_CAST;
import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_INTERACT;
import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_MOVE_TO;
import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_PICK_UP;
import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_REST;
import com.l2jserver.gameserver.model.L2Object;
import com.l2jserver.gameserver.model.Location;
import com.l2jserver.gameserver.model.actor.L2Character;
import com.l2jserver.gameserver.model.actor.L2Character.AIAccessor;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.actor.instance.L2StaticObjectInstance;
import com.l2jserver.gameserver.model.skills.Skill;
import com.l2jserver.gameserver.model.skills.targets.L2TargetType;
public class L2PlayerAI extends L2PlayableAI
{
private boolean _thinking; // to prevent recursive thinking
IntentionCommand _nextIntention = null;
public L2PlayerAI(AIAccessor accessor)
{
super(accessor);
}
void saveNextIntention(CtrlIntention intention, Object arg0, Object arg1)
{
_nextIntention = new IntentionCommand(intention, arg0, arg1);
}
@Override
public IntentionCommand getNextIntention()
{
return _nextIntention;
}
/**
* Saves the current Intention for this L2PlayerAI if necessary and calls changeIntention in AbstractAI.
* @param intention The new Intention to set to the AI
* @param arg0 The first parameter of the Intention
* @param arg1 The second parameter of the Intention
*/
@Override
protected synchronized void changeIntention(CtrlIntention intention, Object arg0, Object arg1)
{
// do nothing unless CAST intention
// however, forget interrupted actions when starting to use an offensive skill
if ((intention != AI_INTENTION_CAST) || ((arg0 != null) && ((Skill) arg0).isBad()))
{
_nextIntention = null;
super.changeIntention(intention, arg0, arg1);
return;
}
// do nothing if next intention is same as current one.
if ((intention == _intention) && (arg0 == _intentionArg0) && (arg1 == _intentionArg1))
{
super.changeIntention(intention, arg0, arg1);
return;
}
// save current intention so it can be used after cast
saveNextIntention(_intention, _intentionArg0, _intentionArg1);
super.changeIntention(intention, arg0, arg1);
}
/**
* Launch actions corresponding to the Event ReadyToAct.<br>
* <B><U> Actions</U> :</B>
* <ul>
* <li>Launch actions corresponding to the Event Think</li>
* </ul>
*/
@Override
protected void onEvtReadyToAct()
{
// Launch actions corresponding to the Event Think
if (_nextIntention != null)
{
setIntention(_nextIntention._crtlIntention, _nextIntention._arg0, _nextIntention._arg1);
_nextIntention = null;
}
super.onEvtReadyToAct();
}
/**
* Launch actions corresponding to the Event Cancel.<br>
* <B><U> Actions</U> :</B>
* <ul>
* <li>Stop an AI Follow Task</li>
* <li>Launch actions corresponding to the Event Think</li>
* </ul>
*/
@Override
protected void onEvtCancel()
{
_nextIntention = null;
super.onEvtCancel();
}
/**
* Finalize the casting of a skill. This method overrides L2CharacterAI method.<br>
* <B>What it does:</B><br>
* Check if actual intention is set to CAST and, if so, retrieves latest intention before the actual CAST and set it as the current intention for the player.
*/
@Override
protected void onEvtFinishCasting()
{
if (getIntention() == AI_INTENTION_CAST)
{
// run interrupted or next intention
IntentionCommand nextIntention = _nextIntention;
if (nextIntention != null)
{
if (nextIntention._crtlIntention != AI_INTENTION_CAST) // previous state shouldn't be casting
{
setIntention(nextIntention._crtlIntention, nextIntention._arg0, nextIntention._arg1);
}
else
{
setIntention(AI_INTENTION_IDLE);
}
}
else
{
// set intention to idle if skill doesn't change intention.
setIntention(AI_INTENTION_IDLE);
}
}
}
@Override
protected void onIntentionRest()
{
if (getIntention() != AI_INTENTION_REST)
{
changeIntention(AI_INTENTION_REST, null, null);
setTarget(null);
if (getAttackTarget() != null)
{
setAttackTarget(null);
}
clientStopMoving(null);
}
}
@Override
protected void onIntentionActive()
{
setIntention(AI_INTENTION_IDLE);
}
/**
* Manage the Move To Intention : Stop current Attack and Launch a Move to Location Task.<br>
* <B><U> Actions</U> : </B>
* <ul>
* <li>Stop the actor auto-attack server side AND client side by sending Server->Client packet AutoAttackStop (broadcast)</li>
* <li>Set the Intention of this AI to AI_INTENTION_MOVE_TO</li>
* <li>Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast)</li>
* </ul>
*/
@Override
protected void onIntentionMoveTo(Location loc)
{
if (getIntention() == AI_INTENTION_REST)
{
// Cancel action client side by sending Server->Client packet ActionFailed to the L2PcInstance actor
clientActionFailed();
return;
}
if (_actor.isAllSkillsDisabled() || _actor.isCastingNow() || _actor.isAttackingNow())
{
clientActionFailed();
saveNextIntention(AI_INTENTION_MOVE_TO, loc, null);
return;
}
// Set the Intention of this AbstractAI to AI_INTENTION_MOVE_TO
changeIntention(AI_INTENTION_MOVE_TO, loc, null);
// Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast)
clientStopAutoAttack();
// Abort the attack of the L2Character and send Server->Client ActionFailed packet
_actor.abortAttack();
// Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast)
moveTo(loc.getX(), loc.getY(), loc.getZ());
}
@Override
protected void clientNotifyDead()
{
_clientMovingToPawnOffset = 0;
_clientMoving = false;
super.clientNotifyDead();
}
private void thinkAttack()
{
L2Character target = getAttackTarget();
if (target == null)
{
return;
}
if (checkTargetLostOrDead(target))
{
// Notify the target
setAttackTarget(null);
return;
}
if (maybeMoveToPawn(target, _actor.getPhysicalAttackRange()))
{
return;
}
_accessor.doAttack(target);
}
private void thinkCast()
{
L2Character target = getCastTarget();
if ((_skill.getTargetType() == L2TargetType.GROUND) && (_actor instanceof L2PcInstance))
{
if (maybeMoveToPosition(((L2PcInstance) _actor).getCurrentSkillWorldPosition(), _actor.getMagicalAttackRange(_skill)))
{
_actor.setIsCastingNow(false);
return;
}
}
else
{
if (checkTargetLost(target))
{
if (_skill.isBad() && (getAttackTarget() != null))
{
// Notify the target
setCastTarget(null);
}
_actor.setIsCastingNow(false);
return;
}
if ((target != null) && maybeMoveToPawn(target, _actor.getMagicalAttackRange(_skill)))
{
_actor.setIsCastingNow(false);
return;
}
}
if ((_skill.getHitTime() > 50) && !_skill.isSimultaneousCast())
{
clientStopMoving(null);
}
_accessor.doCast(_skill);
}
private void thinkPickUp()
{
if (_actor.isAllSkillsDisabled() || _actor.isCastingNow())
{
return;
}
L2Object target = getTarget();
if (checkTargetLost(target))
{
return;
}
if (maybeMoveToPawn(target, 36))
{
return;
}
setIntention(AI_INTENTION_IDLE);
((L2PcInstance.AIAccessor) _accessor).doPickupItem(target);
}
private void thinkInteract()
{
if (_actor.isAllSkillsDisabled() || _actor.isCastingNow())
{
return;
}
L2Object target = getTarget();
if (checkTargetLost(target))
{
return;
}
if (maybeMoveToPawn(target, 36))
{
return;
}
if (!(target instanceof L2StaticObjectInstance))
{
((L2PcInstance.AIAccessor) _accessor).doInteract((L2Character) target);
}
setIntention(AI_INTENTION_IDLE);
}
@Override
protected void onEvtThink()
{
if (_thinking && (getIntention() != AI_INTENTION_CAST))
{
return;
}
_thinking = true;
try
{
if (getIntention() == AI_INTENTION_ATTACK)
{
thinkAttack();
}
else if (getIntention() == AI_INTENTION_CAST)
{
thinkCast();
}
else if (getIntention() == AI_INTENTION_PICK_UP)
{
thinkPickUp();
}
else if (getIntention() == AI_INTENTION_INTERACT)
{
thinkInteract();
}
}
finally
{
_thinking = false;
}
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.ai;
import com.l2jserver.gameserver.model.actor.instance.L2AirShipInstance;
import com.l2jserver.gameserver.model.actor.instance.L2ShuttleInstance;
import com.l2jserver.gameserver.network.serverpackets.shuttle.ExShuttleMove;
/**
* @author UnAfraid
*/
public class L2ShuttleAI extends L2VehicleAI
{
public L2ShuttleAI(L2AirShipInstance.AIAccessor accessor)
{
super(accessor);
}
@Override
public void moveTo(int x, int y, int z)
{
if (!_actor.isMovementDisabled())
{
_clientMoving = true;
_accessor.moveTo(x, y, z);
_actor.broadcastPacket(new ExShuttleMove(getActor(), x, y, z));
}
}
@Override
public L2ShuttleInstance getActor()
{
return (L2ShuttleInstance) _actor;
}
}

View File

@@ -0,0 +1,933 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.ai;
import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE;
import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
import java.util.Collection;
import java.util.concurrent.Future;
import com.l2jserver.Config;
import com.l2jserver.gameserver.GameTimeController;
import com.l2jserver.gameserver.GeoData;
import com.l2jserver.gameserver.ThreadPoolManager;
import com.l2jserver.gameserver.model.L2Object;
import com.l2jserver.gameserver.model.actor.L2Attackable;
import com.l2jserver.gameserver.model.actor.L2Character;
import com.l2jserver.gameserver.model.actor.L2Npc;
import com.l2jserver.gameserver.model.actor.L2Playable;
import com.l2jserver.gameserver.model.actor.L2Summon;
import com.l2jserver.gameserver.model.actor.instance.L2DefenderInstance;
import com.l2jserver.gameserver.model.actor.instance.L2DoorInstance;
import com.l2jserver.gameserver.model.actor.instance.L2NpcInstance;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.effects.L2EffectType;
import com.l2jserver.gameserver.model.skills.Skill;
import com.l2jserver.gameserver.util.Util;
import com.l2jserver.util.Rnd;
/**
* This class manages AI of L2Attackable.
*/
public class L2SiegeGuardAI extends L2CharacterAI implements Runnable
{
private static final int MAX_ATTACK_TIMEOUT = 300; // int ticks, i.e. 30 seconds
/** The L2Attackable AI task executed every 1s (call onEvtThink method) */
private Future<?> _aiTask;
/** For attack AI, analysis of mob and its targets */
private final SelfAnalysis _selfAnalysis = new SelfAnalysis();
// private TargetAnalysis _mostHatedAnalysis = new TargetAnalysis();
/** The delay after which the attacked is stopped */
private int _attackTimeout;
/** The L2Attackable aggro counter */
private int _globalAggro;
/** The flag used to indicate that a thinking action is in progress */
private boolean _thinking; // to prevent recursive thinking
private final int _attackRange;
/**
* Constructor of L2AttackableAI.
* @param accessor The AI accessor of the L2Character
*/
public L2SiegeGuardAI(L2Character.AIAccessor accessor)
{
super(accessor);
_selfAnalysis.init();
_attackTimeout = Integer.MAX_VALUE;
_globalAggro = -10; // 10 seconds timeout of ATTACK after respawn
_attackRange = _actor.getPhysicalAttackRange();
}
@Override
public void run()
{
// Launch actions corresponding to the Event Think
onEvtThink();
}
/**
* <B><U> Actor is a L2GuardInstance</U> :</B>
* <ul>
* <li>The target isn't a Folk or a Door</li>
* <li>The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)</li>
* <li>The target is in the actor Aggro range and is at the same height</li>
* <li>The L2PcInstance target has karma (=PK)</li>
* <li>The L2MonsterInstance target is aggressive</li>
* </ul>
* <B><U> Actor is a L2SiegeGuardInstance</U> :</B>
* <ul>
* <li>The target isn't a Folk or a Door</li>
* <li>The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)</li>
* <li>The target is in the actor Aggro range and is at the same height</li>
* <li>A siege is in progress</li>
* <li>The L2PcInstance target isn't a Defender</li>
* </ul>
* <B><U> Actor is a L2FriendlyMobInstance</U> :</B>
* <ul>
* <li>The target isn't a Folk, a Door or another L2NpcInstance</li>
* <li>The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)</li>
* <li>The target is in the actor Aggro range and is at the same height</li>
* <li>The L2PcInstance target has karma (=PK)</li>
* </ul>
* <B><U> Actor is a L2MonsterInstance</U> :</B>
* <ul>
* <li>The target isn't a Folk, a Door or another L2NpcInstance</li>
* <li>The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)</li>
* <li>The target is in the actor Aggro range and is at the same height</li>
* <li>The actor is Aggressive</li>
* </ul>
* @param target The targeted L2Object
* @return True if the target is autoattackable (depends on the actor type).
*/
protected boolean autoAttackCondition(L2Character target)
{
// Check if the target isn't another guard, folk or a door
if ((target == null) || (target instanceof L2DefenderInstance) || (target instanceof L2NpcInstance) || (target instanceof L2DoorInstance) || target.isAlikeDead())
{
return false;
}
// Check if the target isn't invulnerable
if (target.isInvul())
{
// However EffectInvincible requires to check GMs specially
if ((target instanceof L2PcInstance) && target.isGM())
{
return false;
}
if ((target instanceof L2Summon) && ((L2Summon) target).getOwner().isGM())
{
return false;
}
}
// Get the owner if the target is a summon
if (target instanceof L2Summon)
{
L2PcInstance owner = ((L2Summon) target).getOwner();
if (_actor.isInsideRadius(owner, 1000, true, false))
{
target = owner;
}
}
// Check if the target is a L2PcInstance
if (target instanceof L2Playable)
{
// Check if the target isn't in silent move mode AND too far (>100)
if (((L2Playable) target).isSilentMovingAffected() && !_actor.isInsideRadius(target, 250, false, false))
{
return false;
}
}
// Los Check Here
return (_actor.isAutoAttackable(target) && GeoData.getInstance().canSeeTarget(_actor, target));
}
/**
* Set the Intention of this L2CharacterAI and create an AI Task executed every 1s (call onEvtThink method) for this L2Attackable.<br>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : If actor _knowPlayer isn't EMPTY, AI_INTENTION_IDLE will be change in AI_INTENTION_ACTIVE</B></FONT>
* @param intention The new Intention to set to the AI
* @param arg0 The first parameter of the Intention
* @param arg1 The second parameter of the Intention
*/
@Override
synchronized void changeIntention(CtrlIntention intention, Object arg0, Object arg1)
{
if (Config.DEBUG)
{
_log.info(getClass().getSimpleName() + ": changeIntention(" + intention + ", " + arg0 + ", " + arg1 + ")");
}
if (intention == AI_INTENTION_IDLE /* || intention == AI_INTENTION_ACTIVE */) // active becomes idle if only a summon is present
{
// Check if actor is not dead
if (!_actor.isAlikeDead())
{
L2Attackable npc = (L2Attackable) _actor;
// If its _knownPlayer isn't empty set the Intention to AI_INTENTION_ACTIVE
if (!npc.getKnownList().getKnownPlayers().isEmpty())
{
intention = AI_INTENTION_ACTIVE;
}
else
{
intention = AI_INTENTION_IDLE;
}
}
if (intention == AI_INTENTION_IDLE)
{
// Set the Intention of this L2AttackableAI to AI_INTENTION_IDLE
super.changeIntention(AI_INTENTION_IDLE, null, null);
// Stop AI task and detach AI from NPC
if (_aiTask != null)
{
_aiTask.cancel(true);
_aiTask = null;
}
// Cancel the AI
_accessor.detachAI();
return;
}
}
// Set the Intention of this L2AttackableAI to intention
super.changeIntention(intention, arg0, arg1);
// If not idle - create an AI task (schedule onEvtThink repeatedly)
if (_aiTask == null)
{
_aiTask = ThreadPoolManager.getInstance().scheduleAiAtFixedRate(this, 1000, 1000);
}
}
/**
* Manage the Attack Intention : Stop current Attack (if necessary), Calculate attack timeout, Start a new Attack and Launch Think Event.
* @param target The L2Character to attack
*/
@Override
protected void onIntentionAttack(L2Character target)
{
// Calculate the attack timeout
_attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
// Manage the Attack Intention : Stop current Attack (if necessary), Start a new Attack and Launch Think Event
// if (_actor.getTarget() != null)
super.onIntentionAttack(target);
}
/**
* Manage AI standard thinks of a L2Attackable (called by onEvtThink).<br>
* <B><U> Actions</U> :</B>
* <ul>
* <li>Update every 1s the _globalAggro counter to come close to 0</li>
* <li>If the actor is Aggressive and can attack, add all autoAttackable L2Character in its Aggro Range to its _aggroList, chose a target and order to attack it</li>
* <li>If the actor can't attack, order to it to return to its home location</li>
* </ul>
*/
private void thinkActive()
{
L2Attackable npc = (L2Attackable) _actor;
// Update every 1s the _globalAggro counter to come close to 0
if (_globalAggro != 0)
{
if (_globalAggro < 0)
{
_globalAggro++;
}
else
{
_globalAggro--;
}
}
// Add all autoAttackable L2Character in L2Attackable Aggro Range to its _aggroList with 0 damage and 1 hate
// A L2Attackable isn't aggressive during 10s after its spawn because _globalAggro is set to -10
if (_globalAggro >= 0)
{
for (L2Character target : npc.getKnownList().getKnownCharactersInRadius(_attackRange))
{
if (target == null)
{
continue;
}
if (autoAttackCondition(target)) // check aggression
{
// Get the hate level of the L2Attackable against this L2Character target contained in _aggroList
int hating = npc.getHating(target);
// Add the attacker to the L2Attackable _aggroList with 0 damage and 1 hate
if (hating == 0)
{
npc.addDamageHate(target, 0, 1);
}
}
}
// Chose a target from its aggroList
L2Character hated;
if (_actor.isConfused())
{
hated = getAttackTarget(); // Force mobs to attack anybody if confused
}
else
{
hated = npc.getMostHated();
// _mostHatedAnalysis.Update(hated);
}
// Order to the L2Attackable to attack the target
if (hated != null)
{
// Get the hate level of the L2Attackable against this L2Character target contained in _aggroList
int aggro = npc.getHating(hated);
if ((aggro + _globalAggro) > 0)
{
// Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
if (!_actor.isRunning())
{
_actor.setRunning();
}
// Set the AI Intention to AI_INTENTION_ATTACK
setIntention(CtrlIntention.AI_INTENTION_ATTACK, hated, null);
}
return;
}
}
// Order to the L2DefenderInstance to return to its home location because there's no target to attack
((L2DefenderInstance) _actor).returnHome();
}
/**
* Manage AI attack thinks of a L2Attackable (called by onEvtThink).<br>
* <B><U> Actions</U> :</B>
* <ul>
* <li>Update the attack timeout if actor is running</li>
* <li>If target is dead or timeout is expired, stop this attack and set the Intention to AI_INTENTION_ACTIVE</li>
* <li>Call all L2Object of its Faction inside the Faction Range</li>
* <li>Chose a target and order to attack it with magic skill or physical attack</li>
* </ul>
* TODO: Manage casting rules to healer mobs (like Ant Nurses)
*/
private void thinkAttack()
{
if (Config.DEBUG)
{
_log.info(getClass().getSimpleName() + ": thinkAttack(); timeout=" + (_attackTimeout - GameTimeController.getInstance().getGameTicks()));
}
if (_attackTimeout < GameTimeController.getInstance().getGameTicks())
{
// Check if the actor is running
if (_actor.isRunning())
{
// Set the actor movement type to walk and send Server->Client packet ChangeMoveType to all others L2PcInstance
_actor.setWalking();
// Calculate a new attack timeout
_attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
}
}
L2Character attackTarget = getAttackTarget();
// Check if target is dead or if timeout is expired to stop this attack
if ((attackTarget == null) || attackTarget.isAlikeDead() || (_attackTimeout < GameTimeController.getInstance().getGameTicks()))
{
// Stop hating this target after the attack timeout or if target is dead
if (attackTarget != null)
{
L2Attackable npc = (L2Attackable) _actor;
npc.stopHating(attackTarget);
}
// Cancel target and timeout
_attackTimeout = Integer.MAX_VALUE;
setAttackTarget(null);
// Set the AI Intention to AI_INTENTION_ACTIVE
setIntention(AI_INTENTION_ACTIVE, null, null);
_actor.setWalking();
return;
}
factionNotifyAndSupport();
attackPrepare();
}
private final void factionNotifyAndSupport()
{
L2Character target = getAttackTarget();
// Call all L2Object of its Faction inside the Faction Range
if ((((L2Npc) _actor).getTemplate().getClans() == null) || (target == null))
{
return;
}
if (target.isInvul())
{
return; // speeding it up for siege guards
}
// Go through all L2Character that belong to its faction
// for (L2Character cha : _actor.getKnownList().getKnownCharactersInRadius(((L2NpcInstance) _actor).getFactionRange()+_actor.getTemplate().collisionRadius))
for (L2Character cha : _actor.getKnownList().getKnownCharactersInRadius(1000))
{
if (cha == null)
{
continue;
}
if (!(cha instanceof L2Npc))
{
if (_selfAnalysis.hasHealOrResurrect && (cha instanceof L2PcInstance) && (((L2Npc) _actor).getCastle().getSiege().checkIsDefender(((L2PcInstance) cha).getClan())))
{
// heal friends
if (!_actor.isAttackingDisabled() && (cha.getCurrentHp() < (cha.getMaxHp() * 0.6)) && (_actor.getCurrentHp() > (_actor.getMaxHp() / 2)) && (_actor.getCurrentMp() > (_actor.getMaxMp() / 2)) && cha.isInCombat())
{
for (Skill sk : _selfAnalysis.healSkills)
{
if (_actor.getCurrentMp() < sk.getMpConsume())
{
continue;
}
if (_actor.isSkillDisabled(sk))
{
continue;
}
if (!Util.checkIfInRange(sk.getCastRange(), _actor, cha, true))
{
continue;
}
int chance = 5;
if (chance >= Rnd.get(100))
{
continue;
}
if (!GeoData.getInstance().canSeeTarget(_actor, cha))
{
break;
}
L2Object OldTarget = _actor.getTarget();
_actor.setTarget(cha);
clientStopMoving(null);
_accessor.doCast(sk);
_actor.setTarget(OldTarget);
return;
}
}
}
continue;
}
L2Npc npc = (L2Npc) cha;
if (!npc.isInMyClan((L2Npc) _actor))
{
continue;
}
if (npc.getAI() != null) // TODO: possibly check not needed
{
if (!npc.isDead() && (Math.abs(target.getZ() - npc.getZ()) < 600)
// && _actor.getAttackByList().contains(getAttackTarget())
&& ((npc.getAI()._intention == CtrlIntention.AI_INTENTION_IDLE) || (npc.getAI()._intention == CtrlIntention.AI_INTENTION_ACTIVE))
// limiting aggro for siege guards
&& target.isInsideRadius(npc, 1500, true, false) && GeoData.getInstance().canSeeTarget(npc, target))
{
// Notify the L2Object AI with EVT_AGGRESSION
npc.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, getAttackTarget(), 1);
return;
}
// heal friends
if (_selfAnalysis.hasHealOrResurrect && !_actor.isAttackingDisabled() && (npc.getCurrentHp() < (npc.getMaxHp() * 0.6)) && (_actor.getCurrentHp() > (_actor.getMaxHp() / 2)) && (_actor.getCurrentMp() > (_actor.getMaxMp() / 2)) && npc.isInCombat())
{
for (Skill sk : _selfAnalysis.healSkills)
{
if (_actor.getCurrentMp() < sk.getMpConsume())
{
continue;
}
if (_actor.isSkillDisabled(sk))
{
continue;
}
if (!Util.checkIfInRange(sk.getCastRange(), _actor, npc, true))
{
continue;
}
int chance = 4;
if (chance >= Rnd.get(100))
{
continue;
}
if (!GeoData.getInstance().canSeeTarget(_actor, npc))
{
break;
}
L2Object OldTarget = _actor.getTarget();
_actor.setTarget(npc);
clientStopMoving(null);
_accessor.doCast(sk);
_actor.setTarget(OldTarget);
return;
}
}
}
}
}
private void attackPrepare()
{
// Get all information needed to choose between physical or magical attack
Collection<Skill> skills = null;
double dist_2 = 0;
int range = 0;
L2DefenderInstance sGuard = (L2DefenderInstance) _actor;
L2Character attackTarget = getAttackTarget();
try
{
_actor.setTarget(attackTarget);
skills = _actor.getAllSkills();
dist_2 = _actor.calculateDistance(attackTarget, false, true);
range = _actor.getPhysicalAttackRange() + _actor.getTemplate().getCollisionRadius() + attackTarget.getTemplate().getCollisionRadius();
if (attackTarget.isMoving())
{
range += 50;
}
}
catch (NullPointerException e)
{
_actor.setTarget(null);
setIntention(AI_INTENTION_IDLE, null, null);
return;
}
// never attack defenders
if (attackTarget instanceof L2PcInstance)
{
if ((sGuard.getConquerableHall() == null) && sGuard.getCastle().getSiege().checkIsDefender(((L2PcInstance) attackTarget).getClan()))
{
// Cancel the target
sGuard.stopHating(attackTarget);
_actor.setTarget(null);
setIntention(AI_INTENTION_IDLE, null, null);
return;
}
}
if (!GeoData.getInstance().canSeeTarget(_actor, attackTarget))
{
// Siege guards differ from normal mobs currently:
// If target cannot seen, don't attack any more
sGuard.stopHating(attackTarget);
_actor.setTarget(null);
setIntention(AI_INTENTION_IDLE, null, null);
return;
}
// Check if the actor isn't muted and if it is far from target
if (!_actor.isMuted() && (dist_2 > (range * range)))
{
// check for long ranged skills and heal/buff skills
for (Skill sk : skills)
{
int castRange = sk.getCastRange();
if ((dist_2 <= (castRange * castRange)) && (castRange > 70) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !sk.isPassive())
{
L2Object OldTarget = _actor.getTarget();
if ((sk.isContinuous() && !sk.isDebuff()) || (sk.hasEffectType(L2EffectType.HEAL)))
{
boolean useSkillSelf = true;
if ((sk.hasEffectType(L2EffectType.HEAL)) && (_actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5)))
{
useSkillSelf = false;
break;
}
if ((sk.isContinuous() && !sk.isDebuff()) && _actor.isAffectedBySkill(sk.getId()))
{
useSkillSelf = false;
}
if (useSkillSelf)
{
_actor.setTarget(_actor);
}
}
clientStopMoving(null);
_accessor.doCast(sk);
_actor.setTarget(OldTarget);
return;
}
}
// Check if the L2SiegeGuardInstance is attacking, knows the target and can't run
if (!(_actor.isAttackingNow()) && (_actor.getRunSpeed() == 0) && (_actor.getKnownList().knowsObject(attackTarget)))
{
// Cancel the target
_actor.getKnownList().removeKnownObject(attackTarget);
_actor.setTarget(null);
setIntention(AI_INTENTION_IDLE, null, null);
}
else
{
double dx = _actor.getX() - attackTarget.getX();
double dy = _actor.getY() - attackTarget.getY();
double dz = _actor.getZ() - attackTarget.getZ();
double homeX = attackTarget.getX() - sGuard.getSpawn().getX();
double homeY = attackTarget.getY() - sGuard.getSpawn().getY();
// Check if the L2SiegeGuardInstance isn't too far from it's home location
if ((((dx * dx) + (dy * dy)) > 10000) && (((homeX * homeX) + (homeY * homeY)) > 3240000) // 1800 * 1800
&& (_actor.getKnownList().knowsObject(attackTarget)))
{
// Cancel the target
_actor.getKnownList().removeKnownObject(attackTarget);
_actor.setTarget(null);
setIntention(AI_INTENTION_IDLE, null, null);
}
else
// Move the actor to Pawn server side AND client side by sending Server->Client packet MoveToPawn (broadcast)
{
// Temporary hack for preventing guards jumping off towers,
// before replacing this with effective geodata checks and AI modification
if ((dz * dz) < (170 * 170)) // normally 130 if guard z coordinates correct
{
if (_selfAnalysis.isHealer)
{
return;
}
if (_selfAnalysis.isMage)
{
range = _selfAnalysis.maxCastRange - 50;
}
if (attackTarget.isMoving())
{
moveToPawn(attackTarget, range - 70);
}
else
{
moveToPawn(attackTarget, range);
}
}
}
}
return;
}
// Else, if the actor is muted and far from target, just "move to pawn"
else if (_actor.isMuted() && (dist_2 > (range * range)) && !_selfAnalysis.isHealer)
{
// Temporary hack for preventing guards jumping off towers,
// before replacing this with effective geodata checks and AI modification
double dz = _actor.getZ() - attackTarget.getZ();
if ((dz * dz) < (170 * 170)) // normally 130 if guard z coordinates correct
{
if (_selfAnalysis.isMage)
{
range = _selfAnalysis.maxCastRange - 50;
}
if (attackTarget.isMoving())
{
moveToPawn(attackTarget, range - 70);
}
else
{
moveToPawn(attackTarget, range);
}
}
return;
}
// Else, if this is close enough to attack
else if (dist_2 <= (range * range))
{
// Force mobs to attack anybody if confused
L2Character hated = null;
if (_actor.isConfused())
{
hated = attackTarget;
}
else
{
hated = ((L2Attackable) _actor).getMostHated();
}
if (hated == null)
{
setIntention(AI_INTENTION_ACTIVE, null, null);
return;
}
if (hated != attackTarget)
{
attackTarget = hated;
}
_attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
// check for close combat skills && heal/buff skills
if (!_actor.isMuted() && (Rnd.nextInt(100) <= 5))
{
for (Skill sk : skills)
{
int castRange = sk.getCastRange();
if (((castRange * castRange) >= dist_2) && !sk.isPassive() && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !_actor.isSkillDisabled(sk))
{
L2Object OldTarget = _actor.getTarget();
if ((sk.isContinuous() && !sk.isDebuff()) || (sk.hasEffectType(L2EffectType.HEAL)))
{
boolean useSkillSelf = true;
if ((sk.hasEffectType(L2EffectType.HEAL)) && (_actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5)))
{
useSkillSelf = false;
break;
}
if ((sk.isContinuous() && !sk.isDebuff()) && _actor.isAffectedBySkill(sk.getId()))
{
useSkillSelf = false;
}
if (useSkillSelf)
{
_actor.setTarget(_actor);
}
}
clientStopMoving(null);
_accessor.doCast(sk);
_actor.setTarget(OldTarget);
return;
}
}
}
// Finally, do the physical attack itself
if (!_selfAnalysis.isHealer)
{
_accessor.doAttack(attackTarget);
}
}
}
/**
* Manage AI thinking actions of a L2Attackable.
*/
@Override
protected void onEvtThink()
{
// if(getIntention() != AI_INTENTION_IDLE && (!_actor.isVisible() || !_actor.hasAI() || !_actor.isKnownPlayers()))
// setIntention(AI_INTENTION_IDLE);
// Check if the thinking action is already in progress
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
return;
}
// Start thinking action
_thinking = true;
try
{
// Manage AI thinks of a L2Attackable
if (getIntention() == AI_INTENTION_ACTIVE)
{
thinkActive();
}
else if (getIntention() == AI_INTENTION_ATTACK)
{
thinkAttack();
}
}
finally
{
// Stop thinking action
_thinking = false;
}
}
/**
* Launch actions corresponding to the Event Attacked.<br>
* <B><U> Actions</U> :</B>
* <ul>
* <li>Init the attack : Calculate the attack timeout, Set the _globalAggro to 0, Add the attacker to the actor _aggroList</li>
* <li>Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance</li>
* <li>Set the Intention to AI_INTENTION_ATTACK</li>
* </ul>
* @param attacker The L2Character that attacks the actor
*/
@Override
protected void onEvtAttacked(L2Character attacker)
{
// Calculate the attack timeout
_attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
// Set the _globalAggro to 0 to permit attack even just after spawn
if (_globalAggro < 0)
{
_globalAggro = 0;
}
// Add the attacker to the _aggroList of the actor
((L2Attackable) _actor).addDamageHate(attacker, 0, 1);
// Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
if (!_actor.isRunning())
{
_actor.setRunning();
}
// Set the Intention to AI_INTENTION_ATTACK
if (getIntention() != AI_INTENTION_ATTACK)
{
setIntention(CtrlIntention.AI_INTENTION_ATTACK, attacker, null);
}
super.onEvtAttacked(attacker);
}
/**
* Launch actions corresponding to the Event Aggression.<br>
* <B><U> Actions</U> :</B>
* <ul>
* <li>Add the target to the actor _aggroList or update hate if already present</li>
* <li>Set the actor Intention to AI_INTENTION_ATTACK (if actor is L2GuardInstance check if it isn't too far from its home location)</li>
* </ul>
* @param aggro The value of hate to add to the actor against the target
*/
@Override
protected void onEvtAggression(L2Character target, int aggro)
{
if (_actor == null)
{
return;
}
L2Attackable me = (L2Attackable) _actor;
if (target != null)
{
// Add the target to the actor _aggroList or update hate if already present
me.addDamageHate(target, 0, aggro);
// Get the hate of the actor against the target
aggro = me.getHating(target);
if (aggro <= 0)
{
if (me.getMostHated() == null)
{
_globalAggro = -25;
me.clearAggroList();
setIntention(AI_INTENTION_IDLE, null, null);
}
return;
}
// Set the actor AI Intention to AI_INTENTION_ATTACK
if (getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
{
// Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
if (!_actor.isRunning())
{
_actor.setRunning();
}
L2DefenderInstance sGuard = (L2DefenderInstance) _actor;
double homeX = target.getX() - sGuard.getSpawn().getX();
double homeY = target.getY() - sGuard.getSpawn().getY();
// Check if the L2SiegeGuardInstance is not too far from its home location
if (((homeX * homeX) + (homeY * homeY)) < 3240000)
{
setIntention(CtrlIntention.AI_INTENTION_ATTACK, target, null);
}
}
}
else
{
// currently only for setting lower general aggro
if (aggro >= 0)
{
return;
}
L2Character mostHated = me.getMostHated();
if (mostHated == null)
{
_globalAggro = -25;
return;
}
for (L2Character aggroed : me.getAggroList().keySet())
{
me.addDamageHate(aggroed, 0, aggro);
}
aggro = me.getHating(mostHated);
if (aggro <= 0)
{
_globalAggro = -25;
me.clearAggroList();
setIntention(AI_INTENTION_IDLE, null, null);
}
}
}
@Override
public void stopAITask()
{
if (_aiTask != null)
{
_aiTask.cancel(false);
_aiTask = null;
}
_accessor.detachAI();
super.stopAITask();
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.ai;
import java.util.ArrayList;
import com.l2jserver.gameserver.model.actor.L2Character;
import com.l2jserver.gameserver.model.actor.L2Character.AIAccessor;
/**
* @author BiggBoss
*/
public final class L2SpecialSiegeGuardAI extends L2SiegeGuardAI
{
private final ArrayList<Integer> _allied;
/**
* @param accessor
*/
public L2SpecialSiegeGuardAI(AIAccessor accessor)
{
super(accessor);
_allied = new ArrayList<>();
}
public ArrayList<Integer> getAlly()
{
return _allied;
}
@Override
protected boolean autoAttackCondition(L2Character target)
{
if (_allied.contains(target.getObjectId()))
{
return false;
}
return super.autoAttackCondition(target);
}
}

View File

@@ -0,0 +1,303 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.ai;
import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
import java.util.concurrent.Future;
import com.l2jserver.Config;
import com.l2jserver.gameserver.GeoData;
import com.l2jserver.gameserver.ThreadPoolManager;
import com.l2jserver.gameserver.model.L2Object;
import com.l2jserver.gameserver.model.actor.L2Character;
import com.l2jserver.gameserver.model.actor.L2Character.AIAccessor;
import com.l2jserver.gameserver.model.actor.L2Summon;
import com.l2jserver.gameserver.model.skills.Skill;
import com.l2jserver.util.Rnd;
public class L2SummonAI extends L2PlayableAI implements Runnable
{
private static final int AVOID_RADIUS = 70;
private volatile boolean _thinking; // to prevent recursive thinking
private volatile boolean _startFollow = ((L2Summon) _actor).getFollowStatus();
private L2Character _lastAttack = null;
private volatile boolean _startAvoid = false;
private Future<?> _avoidTask = null;
public L2SummonAI(AIAccessor accessor)
{
super(accessor);
}
@Override
protected void onIntentionIdle()
{
stopFollow();
_startFollow = false;
onIntentionActive();
}
@Override
protected void onIntentionActive()
{
L2Summon summon = (L2Summon) _actor;
if (_startFollow)
{
setIntention(AI_INTENTION_FOLLOW, summon.getOwner());
}
else
{
super.onIntentionActive();
}
}
@Override
synchronized void changeIntention(CtrlIntention intention, Object arg0, Object arg1)
{
switch (intention)
{
case AI_INTENTION_ACTIVE:
case AI_INTENTION_FOLLOW:
startAvoidTask();
break;
default:
stopAvoidTask();
}
super.changeIntention(intention, arg0, arg1);
}
private void thinkAttack()
{
if (checkTargetLostOrDead(getAttackTarget()))
{
setAttackTarget(null);
return;
}
if (maybeMoveToPawn(getAttackTarget(), _actor.getPhysicalAttackRange()))
{
return;
}
clientStopMoving(null);
_accessor.doAttack(getAttackTarget());
}
private void thinkCast()
{
L2Summon summon = (L2Summon) _actor;
if (checkTargetLost(getCastTarget()))
{
setCastTarget(null);
return;
}
boolean val = _startFollow;
if (maybeMoveToPawn(getCastTarget(), _actor.getMagicalAttackRange(_skill)))
{
return;
}
clientStopMoving(null);
summon.setFollowStatus(false);
setIntention(AI_INTENTION_IDLE);
_startFollow = val;
_accessor.doCast(_skill);
}
private void thinkPickUp()
{
if (checkTargetLost(getTarget()))
{
return;
}
if (maybeMoveToPawn(getTarget(), 36))
{
return;
}
setIntention(AI_INTENTION_IDLE);
((L2Summon.AIAccessor) _accessor).doPickupItem(getTarget());
}
private void thinkInteract()
{
if (checkTargetLost(getTarget()))
{
return;
}
if (maybeMoveToPawn(getTarget(), 36))
{
return;
}
setIntention(AI_INTENTION_IDLE);
}
@Override
protected void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
return;
}
_thinking = true;
try
{
switch (getIntention())
{
case AI_INTENTION_ATTACK:
thinkAttack();
break;
case AI_INTENTION_CAST:
thinkCast();
break;
case AI_INTENTION_PICK_UP:
thinkPickUp();
break;
case AI_INTENTION_INTERACT:
thinkInteract();
break;
}
}
finally
{
_thinking = false;
}
}
@Override
protected void onEvtFinishCasting()
{
if (_lastAttack == null)
{
((L2Summon) _actor).setFollowStatus(_startFollow);
}
else
{
setIntention(CtrlIntention.AI_INTENTION_ATTACK, _lastAttack);
_lastAttack = null;
}
}
@Override
protected void onEvtAttacked(L2Character attacker)
{
super.onEvtAttacked(attacker);
avoidAttack(attacker);
}
@Override
protected void onEvtEvaded(L2Character attacker)
{
super.onEvtEvaded(attacker);
avoidAttack(attacker);
}
private void avoidAttack(L2Character attacker)
{
// trying to avoid if summon near owner
if ((((L2Summon) _actor).getOwner() != null) && (((L2Summon) _actor).getOwner() != attacker) && ((L2Summon) _actor).getOwner().isInsideRadius(_actor, 2 * AVOID_RADIUS, true, false))
{
_startAvoid = true;
}
}
@Override
public void run()
{
if (_startAvoid)
{
_startAvoid = false;
if (!_clientMoving && !_actor.isDead() && !_actor.isMovementDisabled())
{
final int ownerX = ((L2Summon) _actor).getOwner().getX();
final int ownerY = ((L2Summon) _actor).getOwner().getY();
final double angle = Math.toRadians(Rnd.get(-90, 90)) + Math.atan2(ownerY - _actor.getY(), ownerX - _actor.getX());
final int targetX = ownerX + (int) (AVOID_RADIUS * Math.cos(angle));
final int targetY = ownerY + (int) (AVOID_RADIUS * Math.sin(angle));
if ((Config.GEODATA == 0) || GeoData.getInstance().canMove(_actor.getX(), _actor.getY(), _actor.getZ(), targetX, targetY, _actor.getZ(), _actor.getInstanceId()))
{
moveTo(targetX, targetY, _actor.getZ());
}
}
}
}
public void notifyFollowStatusChange()
{
_startFollow = !_startFollow;
switch (getIntention())
{
case AI_INTENTION_ACTIVE:
case AI_INTENTION_FOLLOW:
case AI_INTENTION_IDLE:
case AI_INTENTION_MOVE_TO:
case AI_INTENTION_PICK_UP:
((L2Summon) _actor).setFollowStatus(_startFollow);
}
}
public void setStartFollowController(boolean val)
{
_startFollow = val;
}
@Override
protected void onIntentionCast(Skill skill, L2Object target)
{
if (getIntention() == AI_INTENTION_ATTACK)
{
_lastAttack = getAttackTarget();
}
else
{
_lastAttack = null;
}
super.onIntentionCast(skill, target);
}
private void startAvoidTask()
{
if (_avoidTask == null)
{
_avoidTask = ThreadPoolManager.getInstance().scheduleAiAtFixedRate(this, 100, 100);
}
}
private void stopAvoidTask()
{
if (_avoidTask != null)
{
_avoidTask.cancel(false);
_avoidTask = null;
}
}
@Override
public void stopAITask()
{
stopAvoidTask();
super.stopAITask();
}
}

View File

@@ -0,0 +1,129 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.ai;
import com.l2jserver.gameserver.model.L2Object;
import com.l2jserver.gameserver.model.actor.L2Character;
import com.l2jserver.gameserver.model.actor.L2Vehicle;
import com.l2jserver.gameserver.model.skills.Skill;
/**
* @author DS
*/
public abstract class L2VehicleAI extends L2CharacterAI
{
/**
* Simple AI for vehicles
* @param accessor
*/
public L2VehicleAI(L2Vehicle.AIAccessor accessor)
{
super(accessor);
}
@Override
protected void onIntentionAttack(L2Character target)
{
}
@Override
protected void onIntentionCast(Skill skill, L2Object target)
{
}
@Override
protected void onIntentionFollow(L2Character target)
{
}
@Override
protected void onIntentionPickUp(L2Object item)
{
}
@Override
protected void onIntentionInteract(L2Object object)
{
}
@Override
protected void onEvtAttacked(L2Character attacker)
{
}
@Override
protected void onEvtAggression(L2Character target, int aggro)
{
}
@Override
protected void onEvtStunned(L2Character attacker)
{
}
@Override
protected void onEvtSleeping(L2Character attacker)
{
}
@Override
protected void onEvtRooted(L2Character attacker)
{
}
@Override
protected void onEvtForgetObject(L2Object object)
{
}
@Override
protected void onEvtCancel()
{
}
@Override
protected void onEvtDead()
{
}
@Override
protected void onEvtFakeDeath()
{
}
@Override
protected void onEvtFinishCasting()
{
}
@Override
protected void clientActionFailed()
{
}
@Override
protected void moveToPawn(L2Object pawn, int offset)
{
}
@Override
protected void clientStoppedMoving()
{
}
}

View File

@@ -0,0 +1,206 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.ai;
import java.util.ArrayList;
/**
* Class for AI action after some event.<br>
* Has 2 array list for "work" and "break".
* @author Yaroslav
*/
public class NextAction
{
public interface NextActionCallback
{
public void doWork();
}
private ArrayList<CtrlEvent> _events;
private ArrayList<CtrlIntention> _intentions;
private NextActionCallback _callback;
/**
* Main constructor.
* @param events
* @param intentions
* @param callback
*/
public NextAction(ArrayList<CtrlEvent> events, ArrayList<CtrlIntention> intentions, NextActionCallback callback)
{
_events = events;
_intentions = intentions;
setCallback(callback);
}
/**
* Single constructor.
* @param event
* @param intention
* @param callback
*/
public NextAction(CtrlEvent event, CtrlIntention intention, NextActionCallback callback)
{
if (_events == null)
{
_events = new ArrayList<>();
}
if (_intentions == null)
{
_intentions = new ArrayList<>();
}
if (event != null)
{
_events.add(event);
}
if (intention != null)
{
_intentions.add(intention);
}
setCallback(callback);
}
/**
* Do action.
*/
public void doAction()
{
if (_callback != null)
{
_callback.doWork();
}
}
/**
* @return the _event
*/
public ArrayList<CtrlEvent> getEvents()
{
// If null return empty list.
if (_events == null)
{
_events = new ArrayList<>();
}
return _events;
}
/**
* @param event the event to set.
*/
public void setEvents(ArrayList<CtrlEvent> event)
{
_events = event;
}
/**
* @param event
*/
public void addEvent(CtrlEvent event)
{
if (_events == null)
{
_events = new ArrayList<>();
}
if (event != null)
{
_events.add(event);
}
}
/**
* @param event
*/
public void removeEvent(CtrlEvent event)
{
if (_events == null)
{
return;
}
_events.remove(event);
}
/**
* @return the _callback
*/
public NextActionCallback getCallback()
{
return _callback;
}
/**
* @param callback the callback to set.
*/
public void setCallback(NextActionCallback callback)
{
_callback = callback;
}
/**
* @return the _intentions
*/
public ArrayList<CtrlIntention> getIntentions()
{
// If null return empty list.
if (_intentions == null)
{
_intentions = new ArrayList<>();
}
return _intentions;
}
/**
* @param intentions the intention to set.
*/
public void setIntentions(ArrayList<CtrlIntention> intentions)
{
_intentions = intentions;
}
/**
* @param intention
*/
public void addIntention(CtrlIntention intention)
{
if (_intentions == null)
{
_intentions = new ArrayList<>();
}
if (intention != null)
{
_intentions.add(intention);
}
}
/**
* @param intention
*/
public void removeIntention(CtrlIntention intention)
{
if (_intentions == null)
{
return;
}
_intentions.remove(intention);
}
}

View File

@@ -0,0 +1,216 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.cache;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jserver.Config;
import com.l2jserver.gameserver.util.Util;
import com.l2jserver.util.file.filter.HTMLFilter;
/**
* @author Layane
*/
public class HtmCache
{
private static final Logger _log = Logger.getLogger(HtmCache.class.getName());
private static final HTMLFilter htmlFilter = new HTMLFilter();
private static final Map<String, String> _cache = Config.LAZY_CACHE ? new ConcurrentHashMap<>() : new HashMap<>();
private int _loadedFiles;
private long _bytesBuffLen;
protected HtmCache()
{
reload();
}
public void reload()
{
reload(Config.DATAPACK_ROOT);
}
public void reload(File f)
{
if (!Config.LAZY_CACHE)
{
_log.info("Html cache start...");
parseDir(f);
_log.info("Cache[HTML]: " + String.format("%.3f", getMemoryUsage()) + " megabytes on " + getLoadedFiles() + " files loaded");
}
else
{
_cache.clear();
_loadedFiles = 0;
_bytesBuffLen = 0;
_log.info("Cache[HTML]: Running lazy cache");
}
}
public void reloadPath(File f)
{
parseDir(f);
_log.info("Cache[HTML]: Reloaded specified path.");
}
public double getMemoryUsage()
{
return ((float) _bytesBuffLen / 1048576);
}
public int getLoadedFiles()
{
return _loadedFiles;
}
private void parseDir(File dir)
{
final File[] files = dir.listFiles();
for (File file : files)
{
if (!file.isDirectory())
{
loadFile(file);
}
else
{
parseDir(file);
}
}
}
public String loadFile(File file)
{
if (!htmlFilter.accept(file))
{
return null;
}
final String relpath = Util.getRelativePath(Config.DATAPACK_ROOT, file);
String content = null;
try (FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis))
{
final int bytes = bis.available();
byte[] raw = new byte[bytes];
bis.read(raw);
content = new String(raw, "UTF-8");
content = content.replaceAll("(?s)<!--.*?-->", ""); // Remove html comments
String oldContent = _cache.put(relpath, content);
if (oldContent == null)
{
_bytesBuffLen += bytes;
_loadedFiles++;
}
else
{
_bytesBuffLen = (_bytesBuffLen - oldContent.length()) + bytes;
}
}
catch (Exception e)
{
_log.log(Level.WARNING, "Problem with htm file " + e.getMessage(), e);
}
return content;
}
public String getHtmForce(String prefix, String path)
{
String content = getHtm(prefix, path);
if (content == null)
{
content = "<html><body>My text is missing:<br>" + path + "</body></html>";
_log.warning("Cache[HTML]: Missing HTML page: " + path);
}
return content;
}
public String getHtm(String prefix, String path)
{
String newPath = null;
String content;
if ((prefix != null) && !prefix.isEmpty())
{
newPath = prefix + path;
content = getHtm(newPath);
if (content != null)
{
return content;
}
}
content = getHtm(path);
if ((content != null) && (newPath != null))
{
_cache.put(newPath, content);
}
return content;
}
private String getHtm(String path)
{
if ((path == null) || path.isEmpty())
{
return ""; // avoid possible NPE
}
String content = _cache.get(path);
if (Config.LAZY_CACHE && (content == null))
{
content = loadFile(new File(Config.DATAPACK_ROOT, path));
}
return content;
}
public boolean contains(String path)
{
return _cache.containsKey(path);
}
/**
* @param path The path to the HTM
* @return {@code true} if the path targets a HTM or HTML file, {@code false} otherwise.
*/
public boolean isLoadable(String path)
{
return htmlFilter.accept(new File(Config.DATAPACK_ROOT, path));
}
public static HtmCache getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final HtmCache _instance = new HtmCache();
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.cache;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.l2jserver.Config;
import com.l2jserver.gameserver.ThreadPoolManager;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
/**
* @author -Nemesiss-
*/
public class WarehouseCacheManager
{
protected final Map<L2PcInstance, Long> _cachedWh = new ConcurrentHashMap<>();
protected final long _cacheTime = Config.WAREHOUSE_CACHE_TIME * 60000L;
protected WarehouseCacheManager()
{
ThreadPoolManager.getInstance().scheduleAiAtFixedRate(new CacheScheduler(), 120000, 60000);
}
public void addCacheTask(L2PcInstance pc)
{
_cachedWh.put(pc, System.currentTimeMillis());
}
public void remCacheTask(L2PcInstance pc)
{
_cachedWh.remove(pc);
}
public class CacheScheduler implements Runnable
{
@Override
public void run()
{
long cTime = System.currentTimeMillis();
for (L2PcInstance pc : _cachedWh.keySet())
{
if ((cTime - _cachedWh.get(pc)) > _cacheTime)
{
pc.clearWarehouse();
_cachedWh.remove(pc);
}
}
}
}
public static WarehouseCacheManager getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final WarehouseCacheManager _instance = new WarehouseCacheManager();
}
}

View File

@@ -0,0 +1,260 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.communitybbs.BB;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javolution.util.FastList;
import javolution.util.FastMap;
import com.l2jserver.L2DatabaseFactory;
import com.l2jserver.gameserver.communitybbs.Manager.ForumsBBSManager;
import com.l2jserver.gameserver.communitybbs.Manager.TopicBBSManager;
public class Forum
{
private static final Logger _log = Logger.getLogger(Forum.class.getName());
// type
public static final int ROOT = 0;
public static final int NORMAL = 1;
public static final int CLAN = 2;
public static final int MEMO = 3;
public static final int MAIL = 4;
// perm
public static final int INVISIBLE = 0;
public static final int ALL = 1;
public static final int CLANMEMBERONLY = 2;
public static final int OWNERONLY = 3;
private final List<Forum> _children;
private final Map<Integer, Topic> _topic;
private final int _forumId;
private String _forumName;
private int _forumType;
private int _forumPost;
private int _forumPerm;
private final Forum _fParent;
private int _ownerID;
private boolean _loaded = false;
/**
* Creates new instance of Forum. When you create new forum, use {@link com.l2jserver.gameserver.communitybbs.Manager.ForumsBBSManager# addForum(com.l2jserver.gameserver.communitybbs.BB.Forum)} to add forum to the forums manager.
* @param Forumid
* @param FParent
*/
public Forum(int Forumid, Forum FParent)
{
_forumId = Forumid;
_fParent = FParent;
_children = new FastList<>();
_topic = new FastMap<>();
}
/**
* @param name
* @param parent
* @param type
* @param perm
* @param OwnerID
*/
public Forum(String name, Forum parent, int type, int perm, int OwnerID)
{
_forumName = name;
_forumId = ForumsBBSManager.getInstance().getANewID();
_forumType = type;
_forumPost = 0;
_forumPerm = perm;
_fParent = parent;
_ownerID = OwnerID;
_children = new FastList<>();
_topic = new FastMap<>();
parent._children.add(this);
ForumsBBSManager.getInstance().addForum(this);
_loaded = true;
}
private void load()
{
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM forums WHERE forum_id=?"))
{
ps.setInt(1, _forumId);
try (ResultSet rs = ps.executeQuery())
{
if (rs.next())
{
_forumName = rs.getString("forum_name");
_forumPost = rs.getInt("forum_post");
_forumType = rs.getInt("forum_type");
_forumPerm = rs.getInt("forum_perm");
_ownerID = rs.getInt("forum_owner_id");
}
}
}
catch (Exception e)
{
_log.log(Level.WARNING, "Data error on Forum " + _forumId + " : " + e.getMessage(), e);
}
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM topic WHERE topic_forum_id=? ORDER BY topic_id DESC"))
{
ps.setInt(1, _forumId);
try (ResultSet rs = ps.executeQuery())
{
while (rs.next())
{
Topic t = new Topic(Topic.ConstructorType.RESTORE, rs.getInt("topic_id"), rs.getInt("topic_forum_id"), rs.getString("topic_name"), rs.getLong("topic_date"), rs.getString("topic_ownername"), rs.getInt("topic_ownerid"), rs.getInt("topic_type"), rs.getInt("topic_reply"));
_topic.put(t.getID(), t);
if (t.getID() > TopicBBSManager.getInstance().getMaxID(this))
{
TopicBBSManager.getInstance().setMaxID(t.getID(), this);
}
}
}
}
catch (Exception e)
{
_log.log(Level.WARNING, "Data error on Forum " + _forumId + " : " + e.getMessage(), e);
}
}
private void getChildren()
{
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement ps = con.prepareStatement("SELECT forum_id FROM forums WHERE forum_parent=?"))
{
ps.setInt(1, _forumId);
try (ResultSet rs = ps.executeQuery())
{
while (rs.next())
{
Forum f = new Forum(rs.getInt("forum_id"), this);
_children.add(f);
ForumsBBSManager.getInstance().addForum(f);
}
}
}
catch (Exception e)
{
_log.log(Level.WARNING, "Data error on Forum (children): " + e.getMessage(), e);
}
}
public int getTopicSize()
{
vload();
return _topic.size();
}
public Topic getTopic(int j)
{
vload();
return _topic.get(j);
}
public void addTopic(Topic t)
{
vload();
_topic.put(t.getID(), t);
}
/**
* @return the forum Id
*/
public int getID()
{
return _forumId;
}
public String getName()
{
vload();
return _forumName;
}
public int getType()
{
vload();
return _forumType;
}
/**
* @param name the forum name
* @return the forum for the given name
*/
public Forum getChildByName(String name)
{
vload();
for (Forum f : _children)
{
if (f.getName().equals(name))
{
return f;
}
}
return null;
}
/**
* @param id
*/
public void rmTopicByID(int id)
{
_topic.remove(id);
}
public void insertIntoDb()
{
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement ps = con.prepareStatement("INSERT INTO forums (forum_id,forum_name,forum_parent,forum_post,forum_type,forum_perm,forum_owner_id) VALUES (?,?,?,?,?,?,?)"))
{
ps.setInt(1, _forumId);
ps.setString(2, _forumName);
ps.setInt(3, _fParent.getID());
ps.setInt(4, _forumPost);
ps.setInt(5, _forumType);
ps.setInt(6, _forumPerm);
ps.setInt(7, _ownerID);
ps.execute();
}
catch (Exception e)
{
_log.log(Level.WARNING, "Error while saving new Forum to db " + e.getMessage(), e);
}
}
public void vload()
{
if (!_loaded)
{
load();
getChildren();
_loaded = true;
}
}
}

View File

@@ -0,0 +1,184 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.communitybbs.BB;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javolution.util.FastList;
import com.l2jserver.L2DatabaseFactory;
import com.l2jserver.gameserver.communitybbs.Manager.PostBBSManager;
/**
* @author Maktakien
*/
public class Post
{
private static Logger _log = Logger.getLogger(Post.class.getName());
public static class CPost
{
public int postId;
public String postOwner;
public int postOwnerId;
public long postDate;
public int postTopicId;
public int postForumId;
public String postTxt;
}
private final List<CPost> _post;
/**
* @param _PostOwner
* @param _PostOwnerID
* @param date
* @param tid
* @param _PostForumID
* @param txt
*/
public Post(String _PostOwner, int _PostOwnerID, long date, int tid, int _PostForumID, String txt)
{
_post = new FastList<>();
CPost cp = new CPost();
cp.postId = 0;
cp.postOwner = _PostOwner;
cp.postOwnerId = _PostOwnerID;
cp.postDate = date;
cp.postTopicId = tid;
cp.postForumId = _PostForumID;
cp.postTxt = txt;
_post.add(cp);
insertindb(cp);
}
public void insertindb(CPost cp)
{
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement ps = con.prepareStatement("INSERT INTO posts (post_id,post_owner_name,post_ownerid,post_date,post_topic_id,post_forum_id,post_txt) values (?,?,?,?,?,?,?)"))
{
ps.setInt(1, cp.postId);
ps.setString(2, cp.postOwner);
ps.setInt(3, cp.postOwnerId);
ps.setLong(4, cp.postDate);
ps.setInt(5, cp.postTopicId);
ps.setInt(6, cp.postForumId);
ps.setString(7, cp.postTxt);
ps.execute();
}
catch (Exception e)
{
_log.log(Level.WARNING, "Error while saving new Post to db " + e.getMessage(), e);
}
}
public Post(Topic t)
{
_post = new FastList<>();
load(t);
}
public CPost getCPost(int id)
{
int i = 0;
for (CPost cp : _post)
{
if (i++ == id)
{
return cp;
}
}
return null;
}
public void deleteme(Topic t)
{
PostBBSManager.getInstance().delPostByTopic(t);
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement ps = con.prepareStatement("DELETE FROM posts WHERE post_forum_id=? AND post_topic_id=?"))
{
ps.setInt(1, t.getForumID());
ps.setInt(2, t.getID());
ps.execute();
}
catch (Exception e)
{
_log.log(Level.WARNING, "Error while deleting post: " + e.getMessage(), e);
}
}
/**
* @param t
*/
private void load(Topic t)
{
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM posts WHERE post_forum_id=? AND post_topic_id=? ORDER BY post_id ASC"))
{
ps.setInt(1, t.getForumID());
ps.setInt(2, t.getID());
try (ResultSet rs = ps.executeQuery())
{
while (rs.next())
{
CPost cp = new CPost();
cp.postId = rs.getInt("post_id");
cp.postOwner = rs.getString("post_owner_name");
cp.postOwnerId = rs.getInt("post_ownerid");
cp.postDate = rs.getLong("post_date");
cp.postTopicId = rs.getInt("post_topic_id");
cp.postForumId = rs.getInt("post_forum_id");
cp.postTxt = rs.getString("post_txt");
_post.add(cp);
}
}
}
catch (Exception e)
{
_log.log(Level.WARNING, "Data error on Post " + t.getForumID() + "/" + t.getID() + " : " + e.getMessage(), e);
}
}
/**
* @param i
*/
public void updatetxt(int i)
{
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement ps = con.prepareStatement("UPDATE posts SET post_txt=? WHERE post_id=? AND post_topic_id=? AND post_forum_id=?"))
{
CPost cp = getCPost(i);
ps.setString(1, cp.postTxt);
ps.setInt(2, cp.postId);
ps.setInt(3, cp.postTopicId);
ps.setInt(4, cp.postForumId);
ps.execute();
}
catch (Exception e)
{
_log.log(Level.WARNING, "Error while saving new Post to db " + e.getMessage(), e);
}
}
}

View File

@@ -0,0 +1,155 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.communitybbs.BB;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jserver.L2DatabaseFactory;
import com.l2jserver.gameserver.communitybbs.Manager.TopicBBSManager;
public class Topic
{
private static final Logger _log = Logger.getLogger(Topic.class.getName());
public static final int MORMAL = 0;
public static final int MEMO = 1;
private final int _id;
private final int _forumId;
private final String _topicName;
private final long _date;
private final String _ownerName;
private final int _ownerId;
private final int _type;
private final int _cReply;
/**
* @param ct
* @param id
* @param fid
* @param name
* @param date
* @param oname
* @param oid
* @param type
* @param Creply
*/
public Topic(ConstructorType ct, int id, int fid, String name, long date, String oname, int oid, int type, int Creply)
{
_id = id;
_forumId = fid;
_topicName = name;
_date = date;
_ownerName = oname;
_ownerId = oid;
_type = type;
_cReply = Creply;
TopicBBSManager.getInstance().addTopic(this);
if (ct == ConstructorType.CREATE)
{
insertindb();
}
}
public void insertindb()
{
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement ps = con.prepareStatement("INSERT INTO topic (topic_id,topic_forum_id,topic_name,topic_date,topic_ownername,topic_ownerid,topic_type,topic_reply) values (?,?,?,?,?,?,?,?)"))
{
ps.setInt(1, _id);
ps.setInt(2, _forumId);
ps.setString(3, _topicName);
ps.setLong(4, _date);
ps.setString(5, _ownerName);
ps.setInt(6, _ownerId);
ps.setInt(7, _type);
ps.setInt(8, _cReply);
ps.execute();
}
catch (Exception e)
{
_log.log(Level.WARNING, "Error while saving new Topic to db " + e.getMessage(), e);
}
}
public enum ConstructorType
{
RESTORE,
CREATE
}
/**
* @return the topic Id
*/
public int getID()
{
return _id;
}
public int getForumID()
{
return _forumId;
}
/**
* @return the topic name
*/
public String getName()
{
return _topicName;
}
public String getOwnerName()
{
return _ownerName;
}
/**
* @param f
*/
public void deleteme(Forum f)
{
TopicBBSManager.getInstance().delTopic(this);
f.rmTopicByID(getID());
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement ps = con.prepareStatement("DELETE FROM topic WHERE topic_id=? AND topic_forum_id=?"))
{
ps.setInt(1, getID());
ps.setInt(2, f.getID());
ps.execute();
}
catch (Exception e)
{
_log.log(Level.WARNING, "Error while deleting topic: " + e.getMessage(), e);
}
}
/**
* @return the topic date
*/
public long getDate()
{
return _date;
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.communitybbs.Manager;
import java.util.List;
import javolution.util.FastList;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.network.serverpackets.ShowBoard;
public abstract class BaseBBSManager
{
public abstract void parsecmd(String command, L2PcInstance activeChar);
public abstract void parsewrite(String ar1, String ar2, String ar3, String ar4, String ar5, L2PcInstance activeChar);
/**
* @param html
* @param acha
*/
protected void send1001(String html, L2PcInstance acha)
{
if (html.length() < 8192)
{
acha.sendPacket(new ShowBoard(html, "1001"));
}
}
/**
* @param acha
*/
protected void send1002(L2PcInstance acha)
{
send1002(acha, " ", " ", "0");
}
/**
* @param activeChar
* @param string
* @param string2
* @param string3
*/
protected void send1002(L2PcInstance activeChar, String string, String string2, String string3)
{
List<String> _arg = new FastList<>();
_arg.add("0");
_arg.add("0");
_arg.add("0");
_arg.add("0");
_arg.add("0");
_arg.add("0");
_arg.add(activeChar.getName());
_arg.add(Integer.toString(activeChar.getObjectId()));
_arg.add(activeChar.getAccountName());
_arg.add("9");
_arg.add(string2); // subject?
_arg.add(string2); // subject?
_arg.add(string); // text
_arg.add(string3); // date?
_arg.add(string3); // date?
_arg.add("0");
_arg.add("0");
activeChar.sendPacket(new ShowBoard(_arg));
}
}

View File

@@ -0,0 +1,177 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.communitybbs.Manager;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javolution.util.FastList;
import com.l2jserver.L2DatabaseFactory;
import com.l2jserver.gameserver.communitybbs.BB.Forum;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
public class ForumsBBSManager extends BaseBBSManager
{
private static Logger _log = Logger.getLogger(ForumsBBSManager.class.getName());
private final List<Forum> _table;
private int _lastid = 1;
/**
* Instantiates a new forums bbs manager.
*/
protected ForumsBBSManager()
{
_table = new FastList<>();
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
Statement s = con.createStatement();
ResultSet rs = s.executeQuery("SELECT forum_id FROM forums WHERE forum_type = 0"))
{
while (rs.next())
{
int forumId = rs.getInt("forum_id");
Forum f = new Forum(forumId, null);
addForum(f);
}
}
catch (Exception e)
{
_log.log(Level.WARNING, "Data error on Forum (root): " + e.getMessage(), e);
}
}
/**
* Inits the root.
*/
public void initRoot()
{
for (Forum f : _table)
{
f.vload();
}
_log.info("Loaded " + _table.size() + " forums. Last forum id used: " + _lastid);
}
/**
* Adds the forum.
* @param ff the forum
*/
public void addForum(Forum ff)
{
if (ff == null)
{
return;
}
_table.add(ff);
if (ff.getID() > _lastid)
{
_lastid = ff.getID();
}
}
@Override
public void parsecmd(String command, L2PcInstance activeChar)
{
}
/**
* Gets the forum by name.
* @param name the forum name
* @return the forum by name
*/
public Forum getForumByName(String name)
{
for (Forum f : _table)
{
if (f.getName().equals(name))
{
return f;
}
}
return null;
}
/**
* Creates the new forum.
* @param name the forum name
* @param parent the parent forum
* @param type the forum type
* @param perm the perm
* @param oid the oid
* @return the new forum
*/
public Forum createNewForum(String name, Forum parent, int type, int perm, int oid)
{
Forum forum = new Forum(name, parent, type, perm, oid);
forum.insertIntoDb();
return forum;
}
/**
* Gets the a new Id.
* @return the a new Id
*/
public int getANewID()
{
return ++_lastid;
}
/**
* Gets the forum by Id.
* @param idf the the forum Id
* @return the forum by Id
*/
public Forum getForumByID(int idf)
{
for (Forum f : _table)
{
if (f.getID() == idf)
{
return f;
}
}
return null;
}
@Override
public void parsewrite(String ar1, String ar2, String ar3, String ar4, String ar5, L2PcInstance activeChar)
{
}
/**
* Gets the single instance of ForumsBBSManager.
* @return single instance of ForumsBBSManager
*/
public static ForumsBBSManager getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final ForumsBBSManager _instance = new ForumsBBSManager();
}
}

View File

@@ -0,0 +1,208 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.communitybbs.Manager;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
import javolution.util.FastMap;
import com.l2jserver.gameserver.communitybbs.BB.Forum;
import com.l2jserver.gameserver.communitybbs.BB.Post;
import com.l2jserver.gameserver.communitybbs.BB.Post.CPost;
import com.l2jserver.gameserver.communitybbs.BB.Topic;
import com.l2jserver.gameserver.handler.CommunityBoardHandler;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.util.StringUtil;
public class PostBBSManager extends BaseBBSManager
{
private final Map<Topic, Post> _postByTopic = new FastMap<>();
public Post getGPosttByTopic(Topic t)
{
Post post = _postByTopic.get(t);
if (post == null)
{
post = new Post(t);
_postByTopic.put(t, post);
}
return post;
}
public void delPostByTopic(Topic t)
{
_postByTopic.remove(t);
}
public void addPostByTopic(Post p, Topic t)
{
if (_postByTopic.get(t) == null)
{
_postByTopic.put(t, p);
}
}
@Override
public void parsecmd(String command, L2PcInstance activeChar)
{
if (command.startsWith("_bbsposts;read;"))
{
StringTokenizer st = new StringTokenizer(command, ";");
st.nextToken();
st.nextToken();
int idf = Integer.parseInt(st.nextToken());
int idp = Integer.parseInt(st.nextToken());
String index = null;
if (st.hasMoreTokens())
{
index = st.nextToken();
}
int ind = 0;
if (index == null)
{
ind = 1;
}
else
{
ind = Integer.parseInt(index);
}
showPost((TopicBBSManager.getInstance().getTopicByID(idp)), ForumsBBSManager.getInstance().getForumByID(idf), activeChar, ind);
}
else if (command.startsWith("_bbsposts;edit;"))
{
StringTokenizer st = new StringTokenizer(command, ";");
st.nextToken();
st.nextToken();
int idf = Integer.parseInt(st.nextToken());
int idt = Integer.parseInt(st.nextToken());
int idp = Integer.parseInt(st.nextToken());
showEditPost((TopicBBSManager.getInstance().getTopicByID(idt)), ForumsBBSManager.getInstance().getForumByID(idf), activeChar, idp);
}
else
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the command: " + command + " is not implemented yet</center><br><br></body></html>", activeChar);
}
}
private void showEditPost(Topic topic, Forum forum, L2PcInstance activeChar, int idp)
{
Post p = getGPosttByTopic(topic);
if ((forum == null) || (topic == null) || (p == null))
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>Error, this forum, topic or post does not exit !</center><br><br></body></html>", activeChar);
}
else
{
showHtmlEditPost(topic, activeChar, forum, p);
}
}
private void showPost(Topic topic, Forum forum, L2PcInstance activeChar, int ind)
{
if ((forum == null) || (topic == null))
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>Error, this forum is not implemented yet</center><br><br></body></html>", activeChar);
}
else if (forum.getType() == Forum.MEMO)
{
showMemoPost(topic, activeChar, forum);
}
else
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the forum: " + forum.getName() + " is not implemented yet</center><br><br></body></html>", activeChar);
}
}
private void showHtmlEditPost(Topic topic, L2PcInstance activeChar, Forum forum, Post p)
{
final String html = StringUtil.concat("<html><body><br><br><table border=0 width=610><tr><td width=10></td><td width=600 align=left><a action=\"bypass _bbshome\">HOME</a>&nbsp;>&nbsp;<a action=\"bypass _bbsmemo\">", forum.getName(), " Form</a></td></tr></table><img src=\"L2UI.squareblank\" width=\"1\" height=\"10\"><center><table border=0 cellspacing=0 cellpadding=0><tr><td width=610><img src=\"sek.cbui355\" width=\"610\" height=\"1\"><br1><img src=\"sek.cbui355\" width=\"610\" height=\"1\"></td></tr></table><table fixwidth=610 border=0 cellspacing=0 cellpadding=0><tr><td><img src=\"l2ui.mini_logo\" width=5 height=20></td></tr><tr><td><img src=\"l2ui.mini_logo\" width=5 height=1></td><td align=center FIXWIDTH=60 height=29>&$413;</td><td FIXWIDTH=540>", topic.getName(), "</td><td><img src=\"l2ui.mini_logo\" width=5 height=1></td></tr></table><table fixwidth=610 border=0 cellspacing=0 cellpadding=0><tr><td><img src=\"l2ui.mini_logo\" width=5 height=10></td></tr><tr><td><img src=\"l2ui.mini_logo\" width=5 height=1></td><td align=center FIXWIDTH=60 height=29 valign=top>&$427;</td><td align=center FIXWIDTH=540><MultiEdit var =\"Content\" width=535 height=313></td><td><img src=\"l2ui.mini_logo\" width=5 height=1></td></tr><tr><td><img src=\"l2ui.mini_logo\" width=5 height=10></td></tr></table><table fixwidth=610 border=0 cellspacing=0 cellpadding=0><tr><td><img src=\"l2ui.mini_logo\" width=5 height=10></td></tr><tr><td><img src=\"l2ui.mini_logo\" width=5 height=1></td><td align=center FIXWIDTH=60 height=29>&nbsp;</td><td align=center FIXWIDTH=70><button value=\"&$140;\" action=\"Write Post ", String.valueOf(forum.getID()), ";", String.valueOf(topic.getID()), ";0 _ Content Content Content\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\" ></td><td align=center FIXWIDTH=70><button value = \"&$141;\" action=\"bypass _bbsmemo\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\"> </td><td align=center FIXWIDTH=400>&nbsp;</td><td><img src=\"l2ui.mini_logo\" width=5 height=1></td></tr></table></center></body></html>");
send1001(html, activeChar);
send1002(activeChar, p.getCPost(0).postTxt, topic.getName(), DateFormat.getInstance().format(new Date(topic.getDate())));
}
private void showMemoPost(Topic topic, L2PcInstance activeChar, Forum forum)
{
//
Post p = getGPosttByTopic(topic);
Locale locale = Locale.getDefault();
DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.FULL, locale);
String mes = p.getCPost(0).postTxt.replace(">", "&gt;");
mes = mes.replace("<", "&lt;");
final String html = StringUtil.concat("<html><body><br><br><table border=0 width=610><tr><td width=10></td><td width=600 align=left><a action=\"bypass _bbshome\">HOME</a>&nbsp;>&nbsp;<a action=\"bypass _bbsmemo\">Memo Form</a></td></tr></table><img src=\"L2UI.squareblank\" width=\"1\" height=\"10\"><center><table border=0 cellspacing=0 cellpadding=0 bgcolor=333333><tr><td height=10></td></tr><tr><td fixWIDTH=55 align=right valign=top>&$413; : &nbsp;</td><td fixWIDTH=380 valign=top>", topic.getName(), "</td><td fixwidth=5></td><td fixwidth=50></td><td fixWIDTH=120></td></tr><tr><td height=10></td></tr><tr><td align=right><font color=\"AAAAAA\" >&$417; : &nbsp;</font></td><td><font color=\"AAAAAA\">", topic.getOwnerName() + "</font></td><td></td><td><font color=\"AAAAAA\">&$418; :</font></td><td><font color=\"AAAAAA\">", dateFormat.format(p.getCPost(0).postDate), "</font></td></tr><tr><td height=10></td></tr></table><br><table border=0 cellspacing=0 cellpadding=0><tr><td fixwidth=5></td><td FIXWIDTH=600 align=left>", mes, "</td><td fixqqwidth=5></td></tr></table><br><img src=\"L2UI.squareblank\" width=\"1\" height=\"5\"><img src=\"L2UI.squaregray\" width=\"610\" height=\"1\"><img src=\"L2UI.squareblank\" width=\"1\" height=\"5\"><table border=0 cellspacing=0 cellpadding=0 FIXWIDTH=610><tr><td width=50><button value=\"&$422;\" action=\"bypass _bbsmemo\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\"></td><td width=560 align=right><table border=0 cellspacing=0><tr><td FIXWIDTH=300></td><td><button value = \"&$424;\" action=\"bypass _bbsposts;edit;", String.valueOf(forum.getID()), ";", String.valueOf(topic.getID()), ";0\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\" ></td>&nbsp;<td><button value = \"&$425;\" action=\"bypass _bbstopics;del;", String.valueOf(forum.getID()), ";", String.valueOf(topic.getID()), "\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\" ></td>&nbsp;<td><button value = \"&$421;\" action=\"bypass _bbstopics;crea;", String.valueOf(forum.getID()), "\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\" ></td>&nbsp;</tr></table></td></tr></table><br><br><br></center></body></html>");
CommunityBoardHandler.separateAndSend(html, activeChar);
}
@Override
public void parsewrite(String ar1, String ar2, String ar3, String ar4, String ar5, L2PcInstance activeChar)
{
StringTokenizer st = new StringTokenizer(ar1, ";");
int idf = Integer.parseInt(st.nextToken());
int idt = Integer.parseInt(st.nextToken());
int idp = Integer.parseInt(st.nextToken());
Forum f = ForumsBBSManager.getInstance().getForumByID(idf);
if (f == null)
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the forum: " + idf + " does not exist !</center><br><br></body></html>", activeChar);
}
else
{
Topic t = f.getTopic(idt);
if (t == null)
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the topic: " + idt + " does not exist !</center><br><br></body></html>", activeChar);
}
else
{
final Post p = getGPosttByTopic(t);
if (p != null)
{
final CPost cp = p.getCPost(idp);
if (cp == null)
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the post: " + idp + " does not exist !</center><br><br></body></html>", activeChar);
}
else
{
p.getCPost(idp).postTxt = ar4;
p.updatetxt(idp);
parsecmd("_bbsposts;read;" + f.getID() + ";" + t.getID(), activeChar);
}
}
}
}
}
public static PostBBSManager getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final PostBBSManager _instance = new PostBBSManager();
}
}

View File

@@ -0,0 +1,328 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.communitybbs.Manager;
import java.text.DateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import javolution.util.FastList;
import javolution.util.FastMap;
import com.l2jserver.gameserver.communitybbs.BB.Forum;
import com.l2jserver.gameserver.communitybbs.BB.Post;
import com.l2jserver.gameserver.communitybbs.BB.Topic;
import com.l2jserver.gameserver.datatables.ClanTable;
import com.l2jserver.gameserver.handler.CommunityBoardHandler;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.util.StringUtil;
public class TopicBBSManager extends BaseBBSManager
{
private final List<Topic> _table;
private final Map<Forum, Integer> _maxId;
protected TopicBBSManager()
{
_table = new FastList<>();
_maxId = new FastMap<Forum, Integer>().shared();
}
public void addTopic(Topic tt)
{
_table.add(tt);
}
public void delTopic(Topic topic)
{
_table.remove(topic);
}
public void setMaxID(int id, Forum f)
{
_maxId.put(f, id);
}
public int getMaxID(Forum f)
{
Integer i = _maxId.get(f);
if (i == null)
{
return 0;
}
return i;
}
public Topic getTopicByID(int idf)
{
for (Topic t : _table)
{
if (t.getID() == idf)
{
return t;
}
}
return null;
}
@Override
public void parsewrite(String ar1, String ar2, String ar3, String ar4, String ar5, L2PcInstance activeChar)
{
if (ar1.equals("crea"))
{
Forum f = ForumsBBSManager.getInstance().getForumByID(Integer.parseInt(ar2));
if (f == null)
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the forum: " + ar2 + " is not implemented yet</center><br><br></body></html>", activeChar);
}
else
{
f.vload();
Topic t = new Topic(Topic.ConstructorType.CREATE, TopicBBSManager.getInstance().getMaxID(f) + 1, Integer.parseInt(ar2), ar5, Calendar.getInstance().getTimeInMillis(), activeChar.getName(), activeChar.getObjectId(), Topic.MEMO, 0);
f.addTopic(t);
TopicBBSManager.getInstance().setMaxID(t.getID(), f);
Post p = new Post(activeChar.getName(), activeChar.getObjectId(), Calendar.getInstance().getTimeInMillis(), t.getID(), f.getID(), ar4);
PostBBSManager.getInstance().addPostByTopic(p, t);
parsecmd("_bbsmemo", activeChar);
}
}
else if (ar1.equals("del"))
{
Forum f = ForumsBBSManager.getInstance().getForumByID(Integer.parseInt(ar2));
if (f == null)
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the forum: " + ar2 + " does not exist !</center><br><br></body></html>", activeChar);
}
else
{
Topic t = f.getTopic(Integer.parseInt(ar3));
if (t == null)
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the topic: " + ar3 + " does not exist !</center><br><br></body></html>", activeChar);
}
else
{
// CPost cp = null;
Post p = PostBBSManager.getInstance().getGPosttByTopic(t);
if (p != null)
{
p.deleteme(t);
}
t.deleteme(f);
parsecmd("_bbsmemo", activeChar);
}
}
}
else
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the command: " + ar1 + " is not implemented yet</center><br><br></body></html>", activeChar);
}
}
@Override
public void parsecmd(String command, L2PcInstance activeChar)
{
if (command.equals("_bbsmemo"))
{
showTopics(activeChar.getMemo(), activeChar, 1, activeChar.getMemo().getID());
}
else if (command.startsWith("_bbstopics;read"))
{
StringTokenizer st = new StringTokenizer(command, ";");
st.nextToken();
st.nextToken();
int idf = Integer.parseInt(st.nextToken());
String index = null;
if (st.hasMoreTokens())
{
index = st.nextToken();
}
int ind = 0;
if (index == null)
{
ind = 1;
}
else
{
ind = Integer.parseInt(index);
}
showTopics(ForumsBBSManager.getInstance().getForumByID(idf), activeChar, ind, idf);
}
else if (command.startsWith("_bbstopics;crea"))
{
StringTokenizer st = new StringTokenizer(command, ";");
st.nextToken();
st.nextToken();
int idf = Integer.parseInt(st.nextToken());
showNewTopic(ForumsBBSManager.getInstance().getForumByID(idf), activeChar, idf);
}
else if (command.startsWith("_bbstopics;del"))
{
StringTokenizer st = new StringTokenizer(command, ";");
st.nextToken();
st.nextToken();
int idf = Integer.parseInt(st.nextToken());
int idt = Integer.parseInt(st.nextToken());
Forum f = ForumsBBSManager.getInstance().getForumByID(idf);
if (f == null)
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the forum: " + idf + " does not exist !</center><br><br></body></html>", activeChar);
}
else
{
Topic t = f.getTopic(idt);
if (t == null)
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the topic: " + idt + " does not exist !</center><br><br></body></html>", activeChar);
}
else
{
// CPost cp = null;
Post p = PostBBSManager.getInstance().getGPosttByTopic(t);
if (p != null)
{
p.deleteme(t);
}
t.deleteme(f);
parsecmd("_bbsmemo", activeChar);
}
}
}
else
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the command: " + command + " is not implemented yet</center><br><br></body></html>", activeChar);
}
}
private void showNewTopic(Forum forum, L2PcInstance activeChar, int idf)
{
if (forum == null)
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the forum: " + idf + " is not implemented yet</center><br><br></body></html>", activeChar);
}
else if (forum.getType() == Forum.MEMO)
{
showMemoNewTopics(forum, activeChar);
}
else
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the forum: " + forum.getName() + " is not implemented yet</center><br><br></body></html>", activeChar);
}
}
private void showMemoNewTopics(Forum forum, L2PcInstance activeChar)
{
final String html = StringUtil.concat("<html><body><br><br><table border=0 width=610><tr><td width=10></td><td width=600 align=left><a action=\"bypass _bbshome\">HOME</a>&nbsp;>&nbsp;<a action=\"bypass _bbsmemo\">Memo Form</a></td></tr></table><img src=\"L2UI.squareblank\" width=\"1\" height=\"10\"><center><table border=0 cellspacing=0 cellpadding=0><tr><td width=610><img src=\"sek.cbui355\" width=\"610\" height=\"1\"><br1><img src=\"sek.cbui355\" width=\"610\" height=\"1\"></td></tr></table><table fixwidth=610 border=0 cellspacing=0 cellpadding=0><tr><td><img src=\"l2ui.mini_logo\" width=5 height=20></td></tr><tr><td><img src=\"l2ui.mini_logo\" width=5 height=1></td><td align=center FIXWIDTH=60 height=29>&$413;</td><td FIXWIDTH=540><edit var = \"Title\" width=540 height=13></td><td><img src=\"l2ui.mini_logo\" width=5 height=1></td></tr></table><table fixwidth=610 border=0 cellspacing=0 cellpadding=0><tr><td><img src=\"l2ui.mini_logo\" width=5 height=10></td></tr><tr><td><img src=\"l2ui.mini_logo\" width=5 height=1></td><td align=center FIXWIDTH=60 height=29 valign=top>&$427;</td><td align=center FIXWIDTH=540><MultiEdit var =\"Content\" width=535 height=313></td><td><img src=\"l2ui.mini_logo\" width=5 height=1></td></tr><tr><td><img src=\"l2ui.mini_logo\" width=5 height=10></td></tr></table><table fixwidth=610 border=0 cellspacing=0 cellpadding=0><tr><td><img src=\"l2ui.mini_logo\" width=5 height=10></td></tr><tr><td><img src=\"l2ui.mini_logo\" width=5 height=1></td><td align=center FIXWIDTH=60 height=29>&nbsp;</td><td align=center FIXWIDTH=70><button value=\"&$140;\" action=\"Write Topic crea ", String.valueOf(forum.getID()), " Title Content Title\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\" ></td><td align=center FIXWIDTH=70><button value = \"&$141;\" action=\"bypass _bbsmemo\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\"> </td><td align=center FIXWIDTH=400>&nbsp;</td><td><img src=\"l2ui.mini_logo\" width=5 height=1></td></tr></table></center></body></html>");
send1001(html, activeChar);
send1002(activeChar);
}
private void showTopics(Forum forum, L2PcInstance activeChar, int index, int idf)
{
if (forum == null)
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the forum: " + idf + " is not implemented yet</center><br><br></body></html>", activeChar);
}
else if (forum.getType() == Forum.MEMO)
{
showMemoTopics(forum, activeChar, index);
}
else
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the forum: " + forum.getName() + " is not implemented yet</center><br><br></body></html>", activeChar);
}
}
private void showMemoTopics(Forum forum, L2PcInstance activeChar, int index)
{
forum.vload();
final StringBuilder html = StringUtil.startAppend(2000, "<html><body><br><br><table border=0 width=610><tr><td width=10></td><td width=600 align=left><a action=\"bypass _bbshome\">HOME</a>&nbsp;>&nbsp;<a action=\"bypass _bbsmemo\">Memo Form</a></td></tr></table><img src=\"L2UI.squareblank\" width=\"1\" height=\"10\"><center><table border=0 cellspacing=0 cellpadding=2 bgcolor=888888 width=610><tr><td FIXWIDTH=5></td><td FIXWIDTH=415 align=center>&$413;</td><td FIXWIDTH=120 align=center></td><td FIXWIDTH=70 align=center>&$418;</td></tr></table>");
final DateFormat dateFormat = DateFormat.getInstance();
for (int i = 0, j = getMaxID(forum) + 1; i < (12 * index); j--)
{
if (j < 0)
{
break;
}
Topic t = forum.getTopic(j);
if (t != null)
{
if (i++ >= (12 * (index - 1)))
{
StringUtil.append(html, "<table border=0 cellspacing=0 cellpadding=5 WIDTH=610><tr><td FIXWIDTH=5></td><td FIXWIDTH=415><a action=\"bypass _bbsposts;read;", String.valueOf(forum.getID()), ";", String.valueOf(t.getID()), "\">", t.getName(), "</a></td><td FIXWIDTH=120 align=center></td><td FIXWIDTH=70 align=center>", dateFormat.format(new Date(t.getDate())), "</td></tr></table><img src=\"L2UI.Squaregray\" width=\"610\" height=\"1\">");
}
}
}
html.append("<br><table width=610 cellspace=0 cellpadding=0><tr><td width=50><button value=\"&$422;\" action=\"bypass _bbsmemo\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\"></td><td width=510 align=center><table border=0><tr>");
if (index == 1)
{
html.append("<td><button action=\"\" back=\"l2ui_ch3.prev1_down\" fore=\"l2ui_ch3.prev1\" width=16 height=16 ></td>");
}
else
{
StringUtil.append(html, "<td><button action=\"bypass _bbstopics;read;", String.valueOf(forum.getID()), ";", String.valueOf(index - 1), "\" back=\"l2ui_ch3.prev1_down\" fore=\"l2ui_ch3.prev1\" width=16 height=16 ></td>");
}
int nbp;
nbp = forum.getTopicSize() / 8;
if ((nbp * 8) != ClanTable.getInstance().getClans().length)
{
nbp++;
}
for (int i = 1; i <= nbp; i++)
{
if (i == index)
{
StringUtil.append(html, "<td> ", String.valueOf(i), " </td>");
}
else
{
StringUtil.append(html, "<td><a action=\"bypass _bbstopics;read;", String.valueOf(forum.getID()), ";", String.valueOf(i), "\"> ", String.valueOf(i), " </a></td>");
}
}
if (index == nbp)
{
html.append("<td><button action=\"\" back=\"l2ui_ch3.next1_down\" fore=\"l2ui_ch3.next1\" width=16 height=16 ></td>");
}
else
{
StringUtil.append(html, "<td><button action=\"bypass _bbstopics;read;", String.valueOf(forum.getID()), ";", String.valueOf(index + 1), "\" back=\"l2ui_ch3.next1_down\" fore=\"l2ui_ch3.next1\" width=16 height=16 ></td>");
}
StringUtil.append(html, "</tr></table> </td> <td align=right><button value = \"&$421;\" action=\"bypass _bbstopics;crea;", String.valueOf(forum.getID()), "\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\" ></td></tr><tr><td><img src=\"l2ui.mini_logo\" width=5 height=10></td></tr><tr> <td></td><td align=center><table border=0><tr><td></td><td><edit var = \"Search\" width=130 height=11></td><td><button value=\"&$420;\" action=\"Write 5 -2 0 Search _ _\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\"> </td> </tr></table> </td></tr></table><br><br><br></center></body></html>");
CommunityBoardHandler.separateAndSend(html.toString(), activeChar);
}
public static TopicBBSManager getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final TopicBBSManager _instance = new TopicBBSManager();
}
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.model.holders.RangeAbilityPointsHolder;
/**
* @author UnAfraid
*/
public class AbilityPointsData implements DocumentParser
{
private final List<RangeAbilityPointsHolder> _points = new ArrayList<>();
protected AbilityPointsData()
{
load();
}
@Override
public synchronized void load()
{
_points.clear();
parseFile(new File("config/AbilityPoints.xml"));
LOGGER.log(Level.INFO, getClass().getSimpleName() + ": Loaded: " + _points.size() + " range fees.");
}
@Override
public void parseDocument(Document doc)
{
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("points".equalsIgnoreCase(d.getNodeName()))
{
final NamedNodeMap attrs = d.getAttributes();
final int from = parseInteger(attrs, "from");
final int to = parseInteger(attrs, "to");
final int costs = parseInteger(attrs, "costs");
_points.add(new RangeAbilityPointsHolder(from, to, costs));
}
}
}
}
}
public RangeAbilityPointsHolder getHolder(int points)
{
for (RangeAbilityPointsHolder holder : _points)
{
if ((holder.getMin() <= points) && (holder.getMax() >= points))
{
return holder;
}
}
return null;
}
public long getPrice(int points)
{
points++; // for next point
final RangeAbilityPointsHolder holder = getHolder(points);
if (holder == null)
{
final RangeAbilityPointsHolder prevHolder = getHolder(points - 1);
if (prevHolder != null)
{
return prevHolder.getSP();
}
// No data found
return points >= 13 ? 1_000_000_000 : points >= 9 ? 750_000_000 : points >= 5 ? 500_000_000 : 250_000_000;
}
return holder.getSP();
}
public static final AbilityPointsData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final AbilityPointsData _instance = new AbilityPointsData();
}
}

View File

@@ -0,0 +1,356 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import javolution.util.FastMap;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.model.L2AccessLevel;
import com.l2jserver.gameserver.model.L2AdminCommandAccessRight;
import com.l2jserver.gameserver.model.StatsSet;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.network.SystemMessageId;
import com.l2jserver.gameserver.network.serverpackets.L2GameServerPacket;
import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
/**
* @author UnAfraid
*/
public class AdminTable implements DocumentParser
{
private static final Map<Integer, L2AccessLevel> _accessLevels = new HashMap<>();
private static final Map<String, L2AdminCommandAccessRight> _adminCommandAccessRights = new HashMap<>();
private static final Map<L2PcInstance, Boolean> _gmList = new FastMap<L2PcInstance, Boolean>().shared();
private int _highestLevel = 0;
protected AdminTable()
{
load();
}
@Override
public synchronized void load()
{
_accessLevels.clear();
_adminCommandAccessRights.clear();
parseDatapackFile("config/accessLevels.xml");
LOGGER.log(Level.INFO, getClass().getSimpleName() + ": Loaded: " + _accessLevels.size() + " Access Levels");
parseDatapackFile("config/adminCommands.xml");
LOGGER.log(Level.INFO, getClass().getSimpleName() + ": Loaded: " + _adminCommandAccessRights.size() + " Access Commands");
}
@Override
public void parseDocument(Document doc)
{
NamedNodeMap attrs;
Node attr;
StatsSet set;
L2AccessLevel level;
L2AdminCommandAccessRight command;
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("access".equalsIgnoreCase(d.getNodeName()))
{
set = new StatsSet();
attrs = d.getAttributes();
for (int i = 0; i < attrs.getLength(); i++)
{
attr = attrs.item(i);
set.set(attr.getNodeName(), attr.getNodeValue());
}
level = new L2AccessLevel(set);
if (level.getLevel() > _highestLevel)
{
_highestLevel = level.getLevel();
}
_accessLevels.put(level.getLevel(), level);
}
else if ("admin".equalsIgnoreCase(d.getNodeName()))
{
set = new StatsSet();
attrs = d.getAttributes();
for (int i = 0; i < attrs.getLength(); i++)
{
attr = attrs.item(i);
set.set(attr.getNodeName(), attr.getNodeValue());
}
command = new L2AdminCommandAccessRight(set);
_adminCommandAccessRights.put(command.getAdminCommand(), command);
}
}
}
}
}
/**
* Returns the access level by characterAccessLevel.
* @param accessLevelNum as int
* @return the access level instance by char access level
*/
public L2AccessLevel getAccessLevel(int accessLevelNum)
{
if (accessLevelNum < 0)
{
return _accessLevels.get(-1);
}
else if (!_accessLevels.containsKey(accessLevelNum))
{
_accessLevels.put(accessLevelNum, new L2AccessLevel());
}
return _accessLevels.get(accessLevelNum);
}
/**
* Gets the master access level.
* @return the master access level
*/
public L2AccessLevel getMasterAccessLevel()
{
return _accessLevels.get(_highestLevel);
}
/**
* Checks for access level.
* @param id the id
* @return {@code true}, if successful, {@code false} otherwise
*/
public boolean hasAccessLevel(int id)
{
return _accessLevels.containsKey(id);
}
/**
* Checks for access.
* @param adminCommand the admin command
* @param accessLevel the access level
* @return {@code true}, if successful, {@code false} otherwise
*/
public boolean hasAccess(String adminCommand, L2AccessLevel accessLevel)
{
L2AdminCommandAccessRight acar = _adminCommandAccessRights.get(adminCommand);
if (acar == null)
{
// Trying to avoid the spam for next time when the gm would try to use the same command
if ((accessLevel.getLevel() > 0) && (accessLevel.getLevel() == _highestLevel))
{
acar = new L2AdminCommandAccessRight(adminCommand, true, accessLevel.getLevel());
_adminCommandAccessRights.put(adminCommand, acar);
LOGGER.info(getClass().getSimpleName() + ": No rights defined for admin command " + adminCommand + " auto setting accesslevel: " + accessLevel.getLevel() + " !");
}
else
{
LOGGER.info(getClass().getSimpleName() + ": No rights defined for admin command " + adminCommand + " !");
return false;
}
}
return acar.hasAccess(accessLevel);
}
/**
* Require confirm.
* @param command the command
* @return {@code true}, if the command require confirmation, {@code false} otherwise
*/
public boolean requireConfirm(String command)
{
final L2AdminCommandAccessRight acar = _adminCommandAccessRights.get(command);
if (acar == null)
{
LOGGER.info(getClass().getSimpleName() + ": No rights defined for admin command " + command + ".");
return false;
}
return acar.getRequireConfirm();
}
/**
* Gets the all GMs.
* @param includeHidden the include hidden
* @return the all GMs
*/
public List<L2PcInstance> getAllGms(boolean includeHidden)
{
final List<L2PcInstance> tmpGmList = new ArrayList<>();
for (Entry<L2PcInstance, Boolean> entry : _gmList.entrySet())
{
if (includeHidden || !entry.getValue())
{
tmpGmList.add(entry.getKey());
}
}
return tmpGmList;
}
/**
* Gets the all GM names.
* @param includeHidden the include hidden
* @return the all GM names
*/
public List<String> getAllGmNames(boolean includeHidden)
{
final List<String> tmpGmList = new ArrayList<>();
for (Entry<L2PcInstance, Boolean> entry : _gmList.entrySet())
{
if (!entry.getValue())
{
tmpGmList.add(entry.getKey().getName());
}
else if (includeHidden)
{
tmpGmList.add(entry.getKey().getName() + " (invis)");
}
}
return tmpGmList;
}
/**
* Add a L2PcInstance player to the Set _gmList.
* @param player the player
* @param hidden the hidden
*/
public void addGm(L2PcInstance player, boolean hidden)
{
_gmList.put(player, hidden);
}
/**
* Delete a GM.
* @param player the player
*/
public void deleteGm(L2PcInstance player)
{
_gmList.remove(player);
}
/**
* GM will be displayed on clients GM list.
* @param player the player
*/
public void showGm(L2PcInstance player)
{
if (_gmList.containsKey(player))
{
_gmList.put(player, false);
}
}
/**
* GM will no longer be displayed on clients GM list.
* @param player the player
*/
public void hideGm(L2PcInstance player)
{
if (_gmList.containsKey(player))
{
_gmList.put(player, true);
}
}
/**
* Checks if is GM online.
* @param includeHidden the include hidden
* @return true, if is GM online
*/
public boolean isGmOnline(boolean includeHidden)
{
for (Entry<L2PcInstance, Boolean> entry : _gmList.entrySet())
{
if (includeHidden || !entry.getValue())
{
return true;
}
}
return false;
}
/**
* Send list to player.
* @param player the player
*/
public void sendListToPlayer(L2PcInstance player)
{
if (isGmOnline(player.isGM()))
{
player.sendPacket(SystemMessageId.GM_LIST);
for (String name : getAllGmNames(player.isGM()))
{
SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.GM_C1);
sm.addString(name);
player.sendPacket(sm);
}
}
else
{
player.sendPacket(SystemMessageId.THERE_ARE_NO_GMS_CURRENTLY_VISIBLE_IN_THE_PUBLIC_LIST_AS_THEY_MAY_BE_PERFORMING_OTHER_FUNCTIONS_AT_THE_MOMENT);
}
}
/**
* Broadcast to GMs.
* @param packet the packet
*/
public void broadcastToGMs(L2GameServerPacket packet)
{
for (L2PcInstance gm : getAllGms(true))
{
gm.sendPacket(packet);
}
}
/**
* Broadcast message to GMs.
* @param message the message
*/
public void broadcastMessageToGMs(String message)
{
for (L2PcInstance gm : getAllGms(true))
{
gm.sendMessage(message);
}
}
/**
* Gets the single instance of AdminTable.
* @return AccessLevels: the one and only instance of this class<br>
*/
public static AdminTable getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final AdminTable _instance = new AdminTable();
}
}

View File

@@ -0,0 +1,174 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jserver.L2DatabaseFactory;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.announce.Announcement;
import com.l2jserver.gameserver.model.announce.AnnouncementType;
import com.l2jserver.gameserver.model.announce.AutoAnnouncement;
import com.l2jserver.gameserver.model.announce.IAnnouncement;
import com.l2jserver.gameserver.network.clientpackets.Say2;
import com.l2jserver.gameserver.network.serverpackets.CreatureSay;
/**
* @author UnAfraid
*/
public final class AnnouncementsTable
{
private static Logger LOGGER = Logger.getLogger(AnnouncementsTable.class.getName());
private final Map<Integer, IAnnouncement> _announcements = new ConcurrentSkipListMap<>();
protected AnnouncementsTable()
{
load();
}
/**
* Loads all announcements from database.
*/
private void load()
{
_announcements.clear();
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
Statement st = con.createStatement();
ResultSet rset = st.executeQuery("SELECT * FROM announcements"))
{
while (rset.next())
{
final AnnouncementType type = AnnouncementType.findById(rset.getInt("type"));
final Announcement announce;
switch (type)
{
case NORMAL:
case CRITICAL:
{
announce = new Announcement(rset);
break;
}
case AUTO_NORMAL:
case AUTO_CRITICAL:
{
announce = new AutoAnnouncement(rset);
break;
}
default:
{
continue;
}
}
_announcements.put(announce.getId(), announce);
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Failed loading announcements:", e);
}
}
/**
* Sending all announcements to the player
* @param player
*/
public void showAnnouncements(L2PcInstance player)
{
sendAnnouncements(player, AnnouncementType.NORMAL);
sendAnnouncements(player, AnnouncementType.CRITICAL);
sendAnnouncements(player, AnnouncementType.EVENT);
}
/**
* Sends all announcements to the player by the specified type
* @param player
* @param type
*/
public void sendAnnouncements(L2PcInstance player, AnnouncementType type)
{
for (IAnnouncement announce : _announcements.values())
{
if (announce.isValid() && (announce.getType() == type))
{
player.sendPacket(new CreatureSay(0, Say2.ANNOUNCEMENT, player.getName(), announce.getContent()));
}
}
}
/**
* Adds announcement
* @param announce
*/
public void addAnnouncement(IAnnouncement announce)
{
if (announce.storeMe())
{
_announcements.put(announce.getId(), announce);
}
}
/**
* Removes announcement by id
* @param id
* @return {@code true} if announcement exists and was deleted successfully, {@code false} otherwise.
*/
public boolean deleteAnnouncement(int id)
{
final IAnnouncement announce = _announcements.remove(id);
return (announce != null) && announce.deleteMe();
}
/**
* @param id
* @return {@link IAnnouncement} by id
*/
public IAnnouncement getAnnounce(int id)
{
return _announcements.get(id);
}
/**
* @return {@link Collection} containing all announcements
*/
public Collection<IAnnouncement> getAllAnnouncements()
{
return _announcements.values();
}
/**
* @return Single instance of {@link AnnouncementsTable}
*/
public static AnnouncementsTable getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final AnnouncementsTable _instance = new AnnouncementsTable();
}
}

View File

@@ -0,0 +1,206 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.HashMap;
import java.util.Map;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.model.L2ArmorSet;
import com.l2jserver.gameserver.model.holders.ArmorsetSkillHolder;
import com.l2jserver.gameserver.model.holders.SkillHolder;
/**
* @author godson, Luno, UnAfraid
*/
public final class ArmorSetsData implements DocumentParser
{
private static final Map<Integer, L2ArmorSet> _armorSets = new HashMap<>();
/**
* Instantiates a new armor sets data.
*/
protected ArmorSetsData()
{
load();
}
@Override
public void load()
{
_armorSets.clear();
parseDatapackDirectory("data/stats/armorsets", false);
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _armorSets.size() + " Armor sets.");
}
@Override
public void parseDocument(Document doc)
{
NamedNodeMap attrs;
L2ArmorSet set;
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("set".equalsIgnoreCase(d.getNodeName()))
{
set = new L2ArmorSet();
set.setMinimumPieces(parseInteger(d.getAttributes(), "minimumPieces"));
for (Node a = d.getFirstChild(); a != null; a = a.getNextSibling())
{
attrs = a.getAttributes();
switch (a.getNodeName())
{
case "chest":
{
set.addChest(parseInteger(attrs, "id"));
break;
}
case "feet":
{
set.addFeet(parseInteger(attrs, "id"));
break;
}
case "gloves":
{
set.addGloves(parseInteger(attrs, "id"));
break;
}
case "head":
{
set.addHead(parseInteger(attrs, "id"));
break;
}
case "legs":
{
set.addLegs(parseInteger(attrs, "id"));
break;
}
case "shield":
{
set.addShield(parseInteger(attrs, "id"));
break;
}
case "skill":
{
final int skillId = parseInteger(attrs, "id");
final int skillLevel = parseInteger(attrs, "level");
final int minimumPieces = parseInteger(attrs, "minimumPieces", set.getMinimumPieces());
set.addSkill(new ArmorsetSkillHolder(skillId, skillLevel, minimumPieces));
break;
}
case "shield_skill":
{
int skillId = parseInteger(attrs, "id");
int skillLevel = parseInteger(attrs, "level");
set.addShieldSkill(new SkillHolder(skillId, skillLevel));
break;
}
case "enchant6skill":
{
final int skillId = parseInteger(attrs, "id");
final int skillLevel = parseInteger(attrs, "level");
final int minimumEnchant = parseInteger(attrs, "minimumEnchant", 6);
set.addEnchantSkill(new ArmorsetSkillHolder(skillId, skillLevel, minimumEnchant));
break;
}
case "con":
{
set.addCon(parseInteger(attrs, "val"));
break;
}
case "dex":
{
set.addDex(parseInteger(attrs, "val"));
break;
}
case "str":
{
set.addStr(parseInteger(attrs, "val"));
break;
}
case "men":
{
set.addMen(parseInteger(attrs, "val"));
break;
}
case "wit":
{
set.addWit(parseInteger(attrs, "val"));
break;
}
case "int":
{
set.addInt(parseInteger(attrs, "val"));
break;
}
}
}
for (int chestId : set.getChests())
{
_armorSets.put(chestId, set);
}
}
}
}
}
}
/**
* Checks if is armor set.
* @param chestId the chest Id to verify.
* @return {@code true} if the chest Id belongs to a registered armor set, {@code false} otherwise.
*/
public boolean isArmorSet(int chestId)
{
return _armorSets.containsKey(chestId);
}
/**
* Gets the sets the.
* @param chestId the chest Id identifying the armor set.
* @return the armor set associated to the give chest Id.
*/
public L2ArmorSet getSet(int chestId)
{
return _armorSets.get(chestId);
}
/**
* Gets the single instance of ArmorSetsData.
* @return single instance of ArmorSetsData
*/
public static ArmorSetsData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final ArmorSetsData _instance = new ArmorSetsData();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,167 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.HashMap;
import java.util.Map;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.enums.Race;
import com.l2jserver.gameserver.enums.Sex;
import com.l2jserver.gameserver.model.StatsSet;
import com.l2jserver.gameserver.model.beautyshop.BeautyData;
import com.l2jserver.gameserver.model.beautyshop.BeautyItem;
/**
* @author Sdw
*/
public final class BeautyShopData implements DocumentParser
{
private final Map<Race, Map<Sex, BeautyData>> _beautyList = new HashMap<>();
private final Map<Sex, BeautyData> _beautyData = new HashMap<>();
protected BeautyShopData()
{
load();
}
@Override
public synchronized void load()
{
_beautyList.clear();
_beautyData.clear();
parseDatapackFile("data/BeautyShop.xml");
}
@Override
public void parseDocument(Document doc)
{
NamedNodeMap attrs;
StatsSet set;
Node att;
Race race = null;
Sex sex = null;
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("race".equalsIgnoreCase(d.getNodeName()))
{
att = d.getAttributes().getNamedItem("type");
if (att != null)
{
race = parseEnum(att, Race.class);
}
for (Node b = d.getFirstChild(); b != null; b = b.getNextSibling())
{
if ("sex".equalsIgnoreCase(b.getNodeName()))
{
att = b.getAttributes().getNamedItem("type");
if (att != null)
{
sex = parseEnum(att, Sex.class);
}
BeautyData beautyData = new BeautyData();
for (Node a = b.getFirstChild(); a != null; a = a.getNextSibling())
{
if ("hair".equalsIgnoreCase(a.getNodeName()))
{
attrs = a.getAttributes();
set = new StatsSet();
for (int i = 0; i < attrs.getLength(); i++)
{
att = attrs.item(i);
set.set(att.getNodeName(), att.getNodeValue());
}
BeautyItem hair = new BeautyItem(set);
for (Node g = a.getFirstChild(); g != null; g = g.getNextSibling())
{
if ("color".equalsIgnoreCase(g.getNodeName()))
{
attrs = g.getAttributes();
set = new StatsSet();
for (int i = 0; i < attrs.getLength(); i++)
{
att = attrs.item(i);
set.set(att.getNodeName(), att.getNodeValue());
}
hair.addColor(set);
}
}
beautyData.addHair(hair);
}
else if ("face".equalsIgnoreCase(a.getNodeName()))
{
attrs = a.getAttributes();
set = new StatsSet();
for (int i = 0; i < attrs.getLength(); i++)
{
att = attrs.item(i);
set.set(att.getNodeName(), att.getNodeValue());
}
BeautyItem face = new BeautyItem(set);
beautyData.addFace(face);
}
}
_beautyData.put(sex, beautyData);
}
}
_beautyList.put(race, _beautyData);
}
}
}
}
}
public boolean hasBeautyData(Race race, Sex sex)
{
return _beautyList.containsKey(race) && _beautyList.get(race).containsKey(sex);
}
public BeautyData getBeautyData(Race race, Sex sex)
{
if (_beautyList.containsKey(race))
{
return _beautyList.get(race).get(sex);
}
return null;
}
public static BeautyShopData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final BeautyShopData _instance = new BeautyShopData();
}
}

View File

@@ -0,0 +1,604 @@
package com.l2jserver.gameserver.datatables;
import java.io.File;
import java.io.FileNotFoundException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
import com.l2jserver.Config;
import com.l2jserver.L2DatabaseFactory;
import com.l2jserver.gameserver.ThreadPoolManager;
import com.l2jserver.gameserver.model.L2Clan;
import com.l2jserver.gameserver.model.L2Object;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.skills.Skill;
import com.l2jserver.gameserver.model.zone.ZoneId;
import com.l2jserver.gameserver.network.SystemMessageId;
import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
/**
* @author BiggBoss
*/
public final class BotReportTable
{
private static final Logger LOGGER = Logger.getLogger(BotReportTable.class.getName());
private static final int COLUMN_BOT_ID = 1;
private static final int COLUMN_REPORTER_ID = 2;
private static final int COLUMN_REPORT_TIME = 3;
public static final int ATTACK_ACTION_BLOCK_ID = -1;
public static final int TRADE_ACTION_BLOCK_ID = -2;
public static final int PARTY_ACTION_BLOCK_ID = -3;
public static final int ACTION_BLOCK_ID = -4;
public static final int CHAT_BLOCK_ID = -5;
private static final String SQL_LOAD_REPORTED_CHAR_DATA = "SELECT * FROM bot_reported_char_data";
private static final String SQL_INSERT_REPORTED_CHAR_DATA = "INSERT INTO bot_reported_char_data VALUES (?,?,?)";
private static final String SQL_CLEAR_REPORTED_CHAR_DATA = "DELETE FROM bot_reported_char_data";
private Map<Integer, Long> _ipRegistry;
private Map<Integer, ReporterCharData> _charRegistry;
private Map<Integer, ReportedCharData> _reports;
private Map<Integer, PunishHolder> _punishments;
BotReportTable()
{
if (Config.BOTREPORT_ENABLE)
{
_ipRegistry = new HashMap<>();
_charRegistry = new ConcurrentHashMap<>();
_reports = new ConcurrentHashMap<>();
_punishments = new ConcurrentHashMap<>();
try
{
File punishments = new File("./config/botreport_punishments.xml");
if (!punishments.exists())
{
throw new FileNotFoundException(punishments.getName());
}
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
parser.parse(punishments, new PunishmentsLoader());
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "BotReportTable: Could not load punishments from /config/botreport_punishments.xml", e);
}
loadReportedCharData();
scheduleResetPointTask();
}
}
/**
* Loads all reports of each reported bot into this cache class.<br>
* Warning: Heavy method, used only on server start up
*/
private void loadReportedCharData()
{
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
Statement st = con.createStatement();
ResultSet rset = st.executeQuery(SQL_LOAD_REPORTED_CHAR_DATA))
{
long lastResetTime = 0;
try
{
String[] hour = Config.BOTREPORT_RESETPOINT_HOUR;
Calendar c = Calendar.getInstance();
c.set(Calendar.HOUR_OF_DAY, Integer.parseInt(hour[0]));
c.set(Calendar.MINUTE, Integer.parseInt(hour[1]));
if (System.currentTimeMillis() < c.getTimeInMillis())
{
c.set(Calendar.DAY_OF_YEAR, c.get(Calendar.DAY_OF_YEAR) - 1);
}
lastResetTime = c.getTimeInMillis();
}
catch (Exception e)
{
}
while (rset.next())
{
int botId = rset.getInt(COLUMN_BOT_ID);
int reporter = rset.getInt(COLUMN_REPORTER_ID);
long date = rset.getLong(COLUMN_REPORT_TIME);
if (_reports.containsKey(botId))
{
_reports.get(botId).addReporter(reporter, date);
}
else
{
ReportedCharData rcd = new ReportedCharData();
rcd.addReporter(reporter, date);
_reports.put(rset.getInt(COLUMN_BOT_ID), rcd);
}
if (date > lastResetTime)
{
ReporterCharData rcd = _charRegistry.get(reporter);
if (rcd != null)
{
rcd.setPoints(rcd.getPointsLeft() - 1);
}
else
{
rcd = new ReporterCharData();
rcd.setPoints(6);
_charRegistry.put(reporter, rcd);
}
}
}
LOGGER.info("BotReportTable: Loaded " + _reports.size() + " bot reports");
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "BotReportTable: Could not load reported char data!", e);
}
}
/**
* Save all reports for each reported bot down to database.<br>
* Warning: Heavy method, used only at server shutdown
*/
public void saveReportedCharData()
{
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
Statement st = con.createStatement();
PreparedStatement ps = con.prepareStatement(SQL_INSERT_REPORTED_CHAR_DATA))
{
st.execute(SQL_CLEAR_REPORTED_CHAR_DATA);
for (Map.Entry<Integer, ReportedCharData> entrySet : _reports.entrySet())
{
Map<Integer, Long> reportTable = entrySet.getValue()._reporters;
for (int reporterId : reportTable.keySet())
{
ps.setInt(1, entrySet.getKey());
ps.setInt(2, reporterId);
ps.setLong(3, reportTable.get(reporterId));
ps.execute();
}
}
}
catch (Exception e)
{
LOGGER.log(Level.SEVERE, "BotReportTable: Could not update reported char data in database!", e);
}
}
/**
* Attempts to perform a bot report. R/W to ip and char id registry is synchronized. Triggers bot punish management<br>
* @param reporter (L2PcInstance who issued the report)
* @return True, if the report was registered, False otherwise
*/
public boolean reportBot(L2PcInstance reporter)
{
L2Object target = reporter.getTarget();
if (target == null)
{
return false;
}
L2PcInstance bot = target.getActingPlayer();
if ((bot == null) || (target.getObjectId() == reporter.getObjectId()))
{
return false;
}
if (bot.isInsideZone(ZoneId.PEACE) || bot.isInsideZone(ZoneId.PVP))
{
reporter.sendPacket(SystemMessageId.YOU_CANNOT_REPORT_A_CHARACTER_WHO_IS_IN_A_PEACE_ZONE_OR_A_BATTLEGROUND);
return false;
}
if (bot.isInOlympiadMode())
{
reporter.sendPacket(SystemMessageId.THIS_CHARACTER_CANNOT_MAKE_A_REPORT_YOU_CANNOT_MAKE_A_REPORT_WHILE_LOCATED_INSIDE_A_PEACE_ZONE_OR_A_BATTLEGROUND_WHILE_YOU_ARE_AN_OPPOSING_CLAN_MEMBER_DURING_A_CLAN_WAR_OR_WHILE_PARTICIPATING_IN_THE_OLYMPIAD);
return false;
}
if ((bot.getClan() != null) && bot.getClan().isAtWarWith(reporter.getClan()))
{
reporter.sendPacket(SystemMessageId.YOU_CANNOT_REPORT_WHEN_A_CLAN_WAR_HAS_BEEN_DECLARED);
return false;
}
if (bot.getExp() == bot.getStat().getStartingExp())
{
reporter.sendPacket(SystemMessageId.YOU_CANNOT_REPORT_A_CHARACTER_WHO_HAS_NOT_ACQUIRED_ANY_XP_AFTER_CONNECTING);
return false;
}
ReportedCharData rcd = _reports.get(bot.getObjectId());
ReporterCharData rcdRep = _charRegistry.get(reporter.getObjectId());
final int reporterId = reporter.getObjectId();
synchronized (this)
{
if (_reports.containsKey(reporterId))
{
reporter.sendPacket(SystemMessageId.YOU_HAVE_BEEN_REPORTED_AS_AN_ILLEGAL_PROGRAM_USER_AND_CANNOT_REPORT_OTHER_USERS);
return false;
}
final int ip = hashIp(reporter);
if (!timeHasPassed(_ipRegistry, ip))
{
reporter.sendPacket(SystemMessageId.THIS_CHARACTER_CANNOT_MAKE_A_REPORT_THE_TARGET_HAS_ALREADY_BEEN_REPORTED_BY_EITHER_YOUR_CLAN_OR_ALLIANCE_OR_HAS_ALREADY_BEEN_REPORTED_FROM_YOUR_CURRENT_IP);
return false;
}
if (rcd != null)
{
if (rcd.alredyReportedBy(reporterId))
{
reporter.sendPacket(SystemMessageId.YOU_CANNOT_REPORT_THIS_PERSON_AGAIN_AT_THIS_TIME);
return false;
}
if (!Config.BOTREPORT_ALLOW_REPORTS_FROM_SAME_CLAN_MEMBERS && rcd.reportedBySameClan(reporter.getClan()))
{
reporter.sendPacket(SystemMessageId.THIS_CHARACTER_CANNOT_MAKE_A_REPORT_THE_TARGET_HAS_ALREADY_BEEN_REPORTED_BY_EITHER_YOUR_CLAN_OR_ALLIANCE_OR_HAS_ALREADY_BEEN_REPORTED_FROM_YOUR_CURRENT_IP);
return false;
}
}
if (rcdRep != null)
{
if (rcdRep.getPointsLeft() == 0)
{
reporter.sendPacket(SystemMessageId.YOU_HAVE_USED_ALL_AVAILABLE_POINTS_POINTS_ARE_RESET_EVERYDAY_AT_NOON);
return false;
}
long reuse = (System.currentTimeMillis() - rcdRep.getLastReporTime());
if (reuse < Config.BOTREPORT_REPORT_DELAY)
{
SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_CAN_MAKE_ANOTHER_REPORT_IN_S1_MINUTE_S_YOU_HAVE_S2_POINT_S_REMAINING_ON_THIS_ACCOUNT);
sm.addInt((int) (reuse / 60000));
sm.addInt(rcdRep.getPointsLeft());
reporter.sendPacket(sm);
return false;
}
}
final long curTime = System.currentTimeMillis();
if (rcd == null)
{
rcd = new ReportedCharData();
_reports.put(bot.getObjectId(), rcd);
}
rcd.addReporter(reporterId, curTime);
if (rcdRep == null)
{
rcdRep = new ReporterCharData();
}
rcdRep.registerReport(curTime);
_ipRegistry.put(ip, curTime);
_charRegistry.put(reporterId, rcdRep);
}
SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.C1_WAS_REPORTED_AS_A_BOT);
sm.addCharName(bot);
reporter.sendPacket(sm);
sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_USED_A_REPORT_POINT_ON_C1_YOU_HAVE_S2_POINTS_REMAINING_ON_THIS_ACCOUNT);
sm.addCharName(bot);
sm.addInt(rcdRep.getPointsLeft());
reporter.sendPacket(sm);
handleReport(bot, rcd);
return true;
}
/**
* Find the punishs to apply to the given bot and triggers the punish method.
* @param bot (L2PcInstance to be punished)
* @param rcd (RepotedCharData linked to this bot)
*/
private void handleReport(L2PcInstance bot, final ReportedCharData rcd)
{
// Report count punishment
punishBot(bot, _punishments.get(rcd.getReportCount()));
// Range punishments
for (int key : _punishments.keySet())
{
if ((key < 0) && (Math.abs(key) <= rcd.getReportCount()))
{
punishBot(bot, _punishments.get(key));
}
}
}
/**
* Applies the given punish to the bot if the action is secure
* @param bot (L2PcInstance to punish)
* @param ph (PunishHolder containing the debuff and a possible system message to send)
*/
private void punishBot(L2PcInstance bot, PunishHolder ph)
{
if (ph != null)
{
ph._punish.applyEffects(bot, bot);
if (ph._systemMessageId > -1)
{
SystemMessageId id = SystemMessageId.getSystemMessageId(ph._systemMessageId);
if (id != null)
{
bot.sendPacket(id);
}
}
}
}
/**
* Adds a debuff punishment into the punishments record. If skill does not exist, will log it and return
* @param neededReports (report count to trigger this debuff)
* @param skillId
* @param skillLevel
* @param sysMsg (id of a system message to send when applying the punish)
*/
void addPunishment(int neededReports, int skillId, int skillLevel, int sysMsg)
{
Skill sk = SkillData.getInstance().getSkill(skillId, skillLevel);
if (sk != null)
{
_punishments.put(neededReports, new PunishHolder(sk, sysMsg));
}
else
{
LOGGER.warning("BotReportTable: Could not add punishment for " + neededReports + " report(s): Skill " + skillId + "-" + skillLevel + " does not exist!");
}
}
void resetPointsAndSchedule()
{
synchronized (_charRegistry)
{
for (ReporterCharData rcd : _charRegistry.values())
{
rcd.setPoints(7);
}
}
scheduleResetPointTask();
}
private void scheduleResetPointTask()
{
try
{
String[] hour = Config.BOTREPORT_RESETPOINT_HOUR;
Calendar c = Calendar.getInstance();
c.set(Calendar.HOUR_OF_DAY, Integer.parseInt(hour[0]));
c.set(Calendar.MINUTE, Integer.parseInt(hour[1]));
if (System.currentTimeMillis() > c.getTimeInMillis())
{
c.set(Calendar.DAY_OF_YEAR, c.get(Calendar.DAY_OF_YEAR) + 1);
}
ThreadPoolManager.getInstance().scheduleGeneral(new ResetPointTask(), c.getTimeInMillis() - System.currentTimeMillis());
}
catch (Exception e)
{
ThreadPoolManager.getInstance().scheduleGeneral(new ResetPointTask(), 24 * 3600 * 1000);
LOGGER.log(Level.WARNING, "BotReportTable: Could not properly schedule bot report points reset task. Scheduled in 24 hours.", e);
}
}
public static BotReportTable getInstance()
{
return SingletonHolder.INSTANCE;
}
/**
* Returns a integer representative number from a connection
* @param player (The L2PcInstance owner of the connection)
* @return int (hashed ip)
*/
private static int hashIp(L2PcInstance player)
{
String con = player.getClient().getConnection().getInetAddress().getHostAddress();
String[] rawByte = con.split("\\.");
int[] rawIp = new int[4];
for (int i = 0; i < 4; i++)
{
rawIp[i] = Integer.parseInt(rawByte[i]);
}
return rawIp[0] | (rawIp[1] << 8) | (rawIp[2] << 16) | (rawIp[3] << 24);
}
/**
* Checks and return if the abstrat barrier specified by an integer (map key) has accomplished the waiting time
* @param map (a Map to study (Int = barrier, Long = fully qualified unix time)
* @param objectId (an existent map key)
* @return true if the time has passed.
*/
private static boolean timeHasPassed(Map<Integer, Long> map, int objectId)
{
if (map.containsKey(objectId))
{
return (System.currentTimeMillis() - map.get(objectId)) > Config.BOTREPORT_REPORT_DELAY;
}
return true;
}
/**
* Represents the info about a reporter
*/
private final class ReporterCharData
{
private long _lastReport;
private byte _reportPoints;
ReporterCharData()
{
_reportPoints = 7;
_lastReport = 0;
}
void registerReport(long time)
{
_reportPoints -= 1;
_lastReport = time;
}
long getLastReporTime()
{
return _lastReport;
}
byte getPointsLeft()
{
return _reportPoints;
}
void setPoints(int points)
{
_reportPoints = (byte) points;
}
}
/**
* Represents the info about a reported character
*/
private final class ReportedCharData
{
Map<Integer, Long> _reporters;
ReportedCharData()
{
_reporters = new HashMap<>();
}
int getReportCount()
{
return _reporters.size();
}
boolean alredyReportedBy(int objectId)
{
return _reporters.containsKey(objectId);
}
void addReporter(int objectId, long reportTime)
{
_reporters.put(objectId, reportTime);
}
boolean reportedBySameClan(L2Clan clan)
{
if (clan == null)
{
return false;
}
for (int reporterId : _reporters.keySet())
{
if (clan.isMember(reporterId))
{
return true;
}
}
return false;
}
}
/**
* SAX loader to parse /config/botreport_punishments.xml file
*/
private final class PunishmentsLoader extends DefaultHandler
{
PunishmentsLoader()
{
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attr)
{
if (qName.equals("punishment"))
{
int reportCount = -1, skillId = -1, skillLevel = 1, sysMessage = -1;
try
{
reportCount = Integer.parseInt(attr.getValue("neededReportCount"));
skillId = Integer.parseInt(attr.getValue("skillId"));
String level = attr.getValue("skillLevel");
String systemMessageId = attr.getValue("sysMessageId");
if (level != null)
{
skillLevel = Integer.parseInt(level);
}
if (systemMessageId != null)
{
sysMessage = Integer.parseInt(systemMessageId);
}
}
catch (Exception e)
{
e.printStackTrace();
}
addPunishment(reportCount, skillId, skillLevel, sysMessage);
}
}
}
class PunishHolder
{
final Skill _punish;
final int _systemMessageId;
PunishHolder(final Skill sk, final int sysMsg)
{
_punish = sk;
_systemMessageId = sysMsg;
}
}
class ResetPointTask implements Runnable
{
@Override
public void run()
{
resetPointsAndSchedule();
}
}
private static final class SingletonHolder
{
static final BotReportTable INSTANCE = new BotReportTable();
}
}

View File

@@ -0,0 +1,192 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.io.File;
import java.io.FileFilter;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.Config;
import com.l2jserver.L2DatabaseFactory;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.model.buylist.L2BuyList;
import com.l2jserver.gameserver.model.buylist.Product;
import com.l2jserver.gameserver.model.items.L2Item;
import com.l2jserver.util.file.filter.NumericNameFilter;
/**
* @author NosBit
*/
public final class BuyListData implements DocumentParser
{
private final Map<Integer, L2BuyList> _buyLists = new HashMap<>();
private static final FileFilter NUMERIC_FILTER = new NumericNameFilter();
protected BuyListData()
{
load();
}
@Override
public synchronized void load()
{
_buyLists.clear();
parseDatapackDirectory("data/buylists", false);
if (Config.CUSTOM_BUYLIST_LOAD)
{
parseDatapackDirectory("data/buylists/custom", false);
}
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _buyLists.size() + " BuyLists.");
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
Statement statement = con.createStatement();
ResultSet rs = statement.executeQuery("SELECT * FROM `buylists`"))
{
while (rs.next())
{
int buyListId = rs.getInt("buylist_id");
int itemId = rs.getInt("item_id");
long count = rs.getLong("count");
long nextRestockTime = rs.getLong("next_restock_time");
final L2BuyList buyList = getBuyList(buyListId);
if (buyList == null)
{
LOGGER.warning("BuyList found in database but not loaded from xml! BuyListId: " + buyListId);
continue;
}
final Product product = buyList.getProductByItemId(itemId);
if (product == null)
{
LOGGER.warning("ItemId found in database but not loaded from xml! BuyListId: " + buyListId + " ItemId: " + itemId);
continue;
}
if (count < product.getMaxCount())
{
product.setCount(count);
product.restartRestockTask(nextRestockTime);
}
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Failed to load buyList data from database.", e);
}
}
@Override
public void parseDocument(Document doc, File f)
{
try
{
final int buyListId = Integer.parseInt(f.getName().replaceAll(".xml", ""));
for (Node node = doc.getFirstChild(); node != null; node = node.getNextSibling())
{
if ("list".equalsIgnoreCase(node.getNodeName()))
{
final L2BuyList buyList = new L2BuyList(buyListId);
for (Node list_node = node.getFirstChild(); list_node != null; list_node = list_node.getNextSibling())
{
if ("item".equalsIgnoreCase(list_node.getNodeName()))
{
int itemId = -1;
long price = -1;
long restockDelay = -1;
long count = -1;
NamedNodeMap attrs = list_node.getAttributes();
Node attr = attrs.getNamedItem("id");
itemId = Integer.parseInt(attr.getNodeValue());
attr = attrs.getNamedItem("price");
if (attr != null)
{
price = Long.parseLong(attr.getNodeValue());
}
attr = attrs.getNamedItem("restock_delay");
if (attr != null)
{
restockDelay = Long.parseLong(attr.getNodeValue());
}
attr = attrs.getNamedItem("count");
if (attr != null)
{
count = Long.parseLong(attr.getNodeValue());
}
final L2Item item = ItemTable.getInstance().getTemplate(itemId);
if (item != null)
{
buyList.addProduct(new Product(buyList.getListId(), item, price, restockDelay, count));
}
else
{
LOGGER.warning("Item not found. BuyList:" + buyList.getListId() + " ItemID:" + itemId + " File:" + f.getName());
}
}
else if ("npcs".equalsIgnoreCase(list_node.getNodeName()))
{
for (Node npcs_node = list_node.getFirstChild(); npcs_node != null; npcs_node = npcs_node.getNextSibling())
{
if ("npc".equalsIgnoreCase(npcs_node.getNodeName()))
{
int npcId = Integer.parseInt(npcs_node.getTextContent());
buyList.addAllowedNpc(npcId);
}
}
}
}
_buyLists.put(buyList.getListId(), buyList);
}
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Failed to load buyList data from xml File:" + f.getName(), e);
}
}
@Override
public FileFilter getCurrentFileFilter()
{
return NUMERIC_FILTER;
}
public L2BuyList getBuyList(int listId)
{
return _buyLists.get(listId);
}
public static BuyListData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final BuyListData _instance = new BuyListData();
}
}

View File

@@ -0,0 +1,127 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.enums.CategoryType;
/**
* This class holds different categories containing class ids or npc ids.
* @author NosBit, xban1x
*/
public final class CategoryData implements DocumentParser
{
private static final Logger LOGGER = Logger.getLogger(CategoryData.class.getName());
private final Map<CategoryType, Set<Integer>> _categories = new HashMap<>();
protected CategoryData()
{
load();
}
@Override
public void load()
{
_categories.clear();
parseDatapackFile("data/categoryData.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _categories.size() + " Categories.");
}
@Override
public void parseDocument(Document doc)
{
for (Node node = doc.getFirstChild(); node != null; node = node.getNextSibling())
{
if ("list".equalsIgnoreCase(node.getNodeName()))
{
for (Node list_node = node.getFirstChild(); list_node != null; list_node = list_node.getNextSibling())
{
if ("category".equalsIgnoreCase(list_node.getNodeName()))
{
final NamedNodeMap attrs = list_node.getAttributes();
final CategoryType categoryType = CategoryType.findByName(attrs.getNamedItem("name").getNodeValue());
if (categoryType == null)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Can't find category by name :" + attrs.getNamedItem("name").getNodeValue());
continue;
}
final Set<Integer> ids = new HashSet<>();
for (Node category_node = list_node.getFirstChild(); category_node != null; category_node = category_node.getNextSibling())
{
if ("id".equalsIgnoreCase(category_node.getNodeName()))
{
ids.add(Integer.parseInt(category_node.getTextContent()));
}
}
_categories.put(categoryType, ids);
}
}
}
}
}
/**
* Checks if id is in category.
* @param type The category type.
* @param id The id to be checked.
* @return {@code true} if id is in category, {@code false} if id is not in category or category was not found.
*/
public boolean isInCategory(CategoryType type, int id)
{
final Set<Integer> category = getCategoryByType(type);
if (category == null)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Can't find category type :" + type);
return false;
}
return category.contains(id);
}
/**
* @param type The category type
* @return A {@code Set} containing all the ids in category if category is found, {@code null} if category was not found.
*/
public Set<Integer> getCategoryByType(CategoryType type)
{
return _categories.get(type);
}
public static CategoryData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final CategoryData _instance = new CategoryData();
}
}

View File

@@ -0,0 +1,294 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jserver.Config;
import com.l2jserver.L2DatabaseFactory;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
/**
* This class ...
* @version $Revision: 1.3.2.2.2.1 $ $Date: 2005/03/27 15:29:18 $
*/
public class CharNameTable
{
private static Logger _log = Logger.getLogger(CharNameTable.class.getName());
private final Map<Integer, String> _chars = new ConcurrentHashMap<>();
private final Map<Integer, Integer> _accessLevels = new ConcurrentHashMap<>();
protected CharNameTable()
{
if (Config.CACHE_CHAR_NAMES)
{
loadAll();
}
}
public final void addName(L2PcInstance player)
{
if (player != null)
{
addName(player.getObjectId(), player.getName());
_accessLevels.put(player.getObjectId(), player.getAccessLevel().getLevel());
}
}
private final void addName(int objectId, String name)
{
if (name != null)
{
if (!name.equals(_chars.get(objectId)))
{
_chars.put(objectId, name);
}
}
}
public final void removeName(int objId)
{
_chars.remove(objId);
_accessLevels.remove(objId);
}
public final int getIdByName(String name)
{
if ((name == null) || name.isEmpty())
{
return -1;
}
for (Entry<Integer, String> entry : _chars.entrySet())
{
if (entry.getValue().equalsIgnoreCase(name))
{
return entry.getKey();
}
}
if (Config.CACHE_CHAR_NAMES)
{
return -1;
}
int id = -1;
int accessLevel = 0;
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement ps = con.prepareStatement("SELECT charId,accesslevel FROM characters WHERE char_name=?"))
{
ps.setString(1, name);
try (ResultSet rs = ps.executeQuery())
{
while (rs.next())
{
id = rs.getInt(1);
accessLevel = rs.getInt(2);
}
}
}
catch (SQLException e)
{
_log.log(Level.WARNING, getClass().getSimpleName() + ": Could not check existing char name: " + e.getMessage(), e);
}
if (id > 0)
{
_chars.put(id, name);
_accessLevels.put(id, accessLevel);
return id;
}
return -1; // not found
}
public final String getNameById(int id)
{
if (id <= 0)
{
return null;
}
String name = _chars.get(id);
if (name != null)
{
return name;
}
if (Config.CACHE_CHAR_NAMES)
{
return null;
}
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement ps = con.prepareStatement("SELECT char_name,accesslevel FROM characters WHERE charId=?"))
{
ps.setInt(1, id);
try (ResultSet rset = ps.executeQuery())
{
if (rset.next())
{
name = rset.getString(1);
_chars.put(id, name);
_accessLevels.put(id, rset.getInt(2));
return name;
}
}
}
catch (SQLException e)
{
_log.log(Level.WARNING, getClass().getSimpleName() + ": Could not check existing char id: " + e.getMessage(), e);
}
return null; // not found
}
public final int getAccessLevelById(int objectId)
{
if (getNameById(objectId) != null)
{
return _accessLevels.get(objectId);
}
return 0;
}
public synchronized boolean doesCharNameExist(String name)
{
boolean result = true;
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement ps = con.prepareStatement("SELECT account_name FROM characters WHERE char_name=?"))
{
ps.setString(1, name);
try (ResultSet rs = ps.executeQuery())
{
result = rs.next();
}
}
catch (SQLException e)
{
_log.log(Level.WARNING, getClass().getSimpleName() + ": Could not check existing charname: " + e.getMessage(), e);
}
return result;
}
public int getAccountCharacterCount(String account)
{
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement ps = con.prepareStatement("SELECT COUNT(char_name) FROM characters WHERE account_name=?"))
{
ps.setString(1, account);
try (ResultSet rset = ps.executeQuery())
{
while (rset.next())
{
return rset.getInt(1);
}
}
}
catch (SQLException e)
{
_log.log(Level.WARNING, getClass().getSimpleName() + ": Could not check existing char count: " + e.getMessage(), e);
}
return 0;
}
public int getLevelById(int objectId)
{
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement ps = con.prepareStatement("SELECT level FROM characters WHERE charId = ?"))
{
ps.setInt(1, objectId);
try (ResultSet rset = ps.executeQuery())
{
while (rset.next())
{
return rset.getInt(1);
}
}
}
catch (SQLException e)
{
_log.log(Level.WARNING, getClass().getSimpleName() + ": Could not check existing char count: " + e.getMessage(), e);
}
return 0;
}
public int getClassIdById(int objectId)
{
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement ps = con.prepareStatement("SELECT classid FROM characters WHERE charId = ?"))
{
ps.setInt(1, objectId);
try (ResultSet rset = ps.executeQuery())
{
while (rset.next())
{
return rset.getInt(1);
}
}
}
catch (SQLException e)
{
_log.log(Level.WARNING, getClass().getSimpleName() + ": Could not check existing char count: " + e.getMessage(), e);
}
return 0;
}
private void loadAll()
{
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
Statement s = con.createStatement();
ResultSet rs = s.executeQuery("SELECT charId, char_name, accesslevel FROM characters"))
{
while (rs.next())
{
final int id = rs.getInt(1);
_chars.put(id, rs.getString(2));
_accessLevels.put(id, rs.getInt(3));
}
}
catch (SQLException e)
{
_log.log(Level.WARNING, getClass().getSimpleName() + ": Could not load char name: " + e.getMessage(), e);
}
_log.info(getClass().getSimpleName() + ": Loaded " + _chars.size() + " char names.");
}
public static CharNameTable getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final CharNameTable _instance = new CharNameTable();
}
}

View File

@@ -0,0 +1,251 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
import com.l2jserver.Config;
import com.l2jserver.L2DatabaseFactory;
import com.l2jserver.gameserver.model.L2PetData;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.actor.instance.L2PetInstance;
import com.l2jserver.gameserver.model.actor.instance.L2ServitorInstance;
import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate;
import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
import com.l2jserver.gameserver.model.skills.Skill;
import com.l2jserver.gameserver.network.serverpackets.PetItemList;
/**
* @author Nyaran
*/
public class CharSummonTable
{
private static final Logger LOGGER = Logger.getLogger(CharSummonTable.class.getName());
private static final Map<Integer, Integer> _pets = new ConcurrentHashMap<>();
private static final Map<Integer, Integer> _servitors = new ConcurrentHashMap<>();
// SQL
private static final String INIT_PET = "SELECT ownerId, item_obj_id FROM pets WHERE restore = 'true'";
private static final String INIT_SUMMONS = "SELECT ownerId, summonSkillId FROM character_summons";
private static final String LOAD_SUMMON = "SELECT curHp, curMp, time FROM character_summons WHERE ownerId = ? AND summonSkillId = ?";
private static final String REMOVE_SUMMON = "DELETE FROM character_summons WHERE ownerId = ?";
private static final String SAVE_SUMMON = "REPLACE INTO character_summons (ownerId,summonSkillId,curHp,curMp,time) VALUES (?,?,?,?,?)";
public Map<Integer, Integer> getPets()
{
return _pets;
}
public Map<Integer, Integer> getServitors()
{
return _servitors;
}
public void init()
{
if (Config.RESTORE_SERVITOR_ON_RECONNECT)
{
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
Statement s = con.createStatement();
ResultSet rs = s.executeQuery(INIT_SUMMONS))
{
while (rs.next())
{
_servitors.put(rs.getInt("ownerId"), rs.getInt("summonSkillId"));
}
}
catch (Exception e)
{
LOGGER.warning(getClass().getSimpleName() + ": Error while loading saved servitor: " + e);
}
}
if (Config.RESTORE_PET_ON_RECONNECT)
{
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
Statement s = con.createStatement();
ResultSet rs = s.executeQuery(INIT_PET))
{
while (rs.next())
{
_pets.put(rs.getInt("ownerId"), rs.getInt("item_obj_id"));
}
}
catch (Exception e)
{
LOGGER.warning(getClass().getSimpleName() + ": Error while loading saved pet: " + e);
}
}
}
public void removeServitor(L2PcInstance activeChar)
{
_servitors.remove(activeChar.getObjectId());
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement ps = con.prepareStatement(REMOVE_SUMMON))
{
ps.setInt(1, activeChar.getObjectId());
ps.execute();
}
catch (SQLException e)
{
LOGGER.warning(getClass().getSimpleName() + ": Summon cannot be removed: " + e);
}
}
public void restorePet(L2PcInstance activeChar)
{
final L2ItemInstance item = activeChar.getInventory().getItemByObjectId(_pets.get(activeChar.getObjectId()));
if (item == null)
{
LOGGER.warning(getClass().getSimpleName() + ": Null pet summoning item for: " + activeChar);
return;
}
final L2PetData petData = PetDataTable.getInstance().getPetDataByItemId(item.getId());
if (petData == null)
{
LOGGER.warning(getClass().getSimpleName() + ": Null pet data for: " + activeChar + " and summoning item: " + item);
return;
}
final L2NpcTemplate npcTemplate = NpcData.getInstance().getTemplate(petData.getNpcId());
if (npcTemplate == null)
{
LOGGER.warning(getClass().getSimpleName() + ": Null pet NPC template for: " + activeChar + " and pet Id:" + petData.getNpcId());
return;
}
final L2PetInstance pet = L2PetInstance.spawnPet(npcTemplate, activeChar, item);
if (pet == null)
{
LOGGER.warning(getClass().getSimpleName() + ": Null pet instance for: " + activeChar + " and pet NPC template:" + npcTemplate);
return;
}
pet.setShowSummonAnimation(true);
pet.setTitle(activeChar.getName());
if (!pet.isRespawned())
{
pet.setCurrentHp(pet.getMaxHp());
pet.setCurrentMp(pet.getMaxMp());
pet.getStat().setExp(pet.getExpForThisLevel());
pet.setCurrentFed(pet.getMaxFed());
}
pet.setRunning();
if (!pet.isRespawned())
{
pet.storeMe();
}
item.setEnchantLevel(pet.getLevel());
activeChar.setPet(pet);
pet.spawnMe(activeChar.getX() + 50, activeChar.getY() + 100, activeChar.getZ());
pet.startFeed();
pet.setFollowStatus(true);
pet.getOwner().sendPacket(new PetItemList(pet.getInventory().getItems()));
pet.broadcastStatusUpdate();
}
public void restoreServitor(L2PcInstance activeChar)
{
int skillId = _servitors.get(activeChar.getObjectId());
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement ps = con.prepareStatement(LOAD_SUMMON))
{
ps.setInt(1, activeChar.getObjectId());
ps.setInt(2, skillId);
try (ResultSet rs = ps.executeQuery())
{
Skill skill;
while (rs.next())
{
int curHp = rs.getInt("curHp");
int curMp = rs.getInt("curMp");
int time = rs.getInt("time");
skill = SkillData.getInstance().getSkill(skillId, activeChar.getSkillLevel(skillId));
if (skill == null)
{
removeServitor(activeChar);
return;
}
skill.applyEffects(activeChar, activeChar);
if (activeChar.hasServitor())
{
final L2ServitorInstance summon = (L2ServitorInstance) activeChar.getSummon();
summon.setCurrentHp(curHp);
summon.setCurrentMp(curMp);
summon.setLifeTimeRemaining(time);
}
}
}
}
catch (SQLException e)
{
LOGGER.warning(getClass().getSimpleName() + ": Servitor cannot be restored: " + e);
}
}
public void saveSummon(L2ServitorInstance summon)
{
if ((summon == null) || (summon.getLifeTimeRemaining() <= 0))
{
return;
}
_servitors.put(summon.getOwner().getObjectId(), summon.getReferenceSkill());
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement ps = con.prepareStatement(SAVE_SUMMON))
{
ps.setInt(1, summon.getOwner().getObjectId());
ps.setInt(2, summon.getReferenceSkill());
ps.setInt(3, (int) Math.round(summon.getCurrentHp()));
ps.setInt(4, (int) Math.round(summon.getCurrentMp()));
ps.setInt(5, summon.getLifeTimeRemaining());
ps.execute();
}
catch (Exception e)
{
LOGGER.warning(getClass().getSimpleName() + ": Failed to store summon: " + summon + " from " + summon.getOwner() + ", error: " + e);
}
}
public static CharSummonTable getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final CharSummonTable _instance = new CharSummonTable();
}
}

View File

@@ -0,0 +1,193 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.model.Location;
import com.l2jserver.gameserver.model.StatsSet;
import com.l2jserver.gameserver.model.actor.templates.L2PcTemplate;
import com.l2jserver.gameserver.model.base.ClassId;
/**
* This will be reworked Soon(tm).
* @author Forsaiken, Zoey76, GKR
*/
public final class CharTemplateTable implements DocumentParser
{
private static final Logger LOGGER = Logger.getLogger(CharTemplateTable.class.getName());
private static final Map<ClassId, L2PcTemplate> _charTemplates = new HashMap<>();
private int _dataCount = 0;
protected CharTemplateTable()
{
load();
}
@Override
public void load()
{
_charTemplates.clear();
parseDatapackDirectory("data/stats/chars/baseStats", false);
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _charTemplates.size() + " character templates.");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _dataCount + " level up gain records.");
}
@Override
public void parseDocument(Document doc)
{
NamedNodeMap attrs;
int classId = 0;
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("classId".equalsIgnoreCase(d.getNodeName()))
{
classId = Integer.parseInt(d.getTextContent());
}
else if ("staticData".equalsIgnoreCase(d.getNodeName()))
{
StatsSet set = new StatsSet();
set.set("classId", classId);
List<Location> creationPoints = new ArrayList<>();
for (Node nd = d.getFirstChild(); nd != null; nd = nd.getNextSibling())
{
// Skip odd nodes
if (nd.getNodeName().equals("#text"))
{
continue;
}
if (nd.getChildNodes().getLength() > 1)
{
for (Node cnd = nd.getFirstChild(); cnd != null; cnd = cnd.getNextSibling())
{
// use L2CharTemplate(superclass) fields for male collision height and collision radius
if (nd.getNodeName().equalsIgnoreCase("collisionMale"))
{
if (cnd.getNodeName().equalsIgnoreCase("radius"))
{
set.set("collision_radius", cnd.getTextContent());
}
else if (cnd.getNodeName().equalsIgnoreCase("height"))
{
set.set("collision_height", cnd.getTextContent());
}
}
if ("node".equalsIgnoreCase(cnd.getNodeName()))
{
attrs = cnd.getAttributes();
creationPoints.add(new Location(parseInteger(attrs, "x"), parseInteger(attrs, "y"), parseInteger(attrs, "z")));
}
else if ("walk".equalsIgnoreCase(cnd.getNodeName()))
{
set.set("baseWalkSpd", cnd.getTextContent());
}
else if ("run".equalsIgnoreCase(cnd.getNodeName()))
{
set.set("baseRunSpd", cnd.getTextContent());
}
else if ("slowSwim".equals(cnd.getNodeName()))
{
set.set("baseSwimWalkSpd", cnd.getTextContent());
}
else if ("fastSwim".equals(cnd.getNodeName()))
{
set.set("baseSwimRunSpd", cnd.getTextContent());
}
else if (!cnd.getNodeName().equals("#text"))
{
set.set((nd.getNodeName() + cnd.getNodeName()), cnd.getTextContent());
}
}
}
else
{
set.set(nd.getNodeName(), nd.getTextContent());
}
}
// calculate total pdef and mdef from parts
set.set("basePDef", (set.getInt("basePDefchest", 0) + set.getInt("basePDeflegs", 0) + set.getInt("basePDefhead", 0) + set.getInt("basePDeffeet", 0) + set.getInt("basePDefgloves", 0) + set.getInt("basePDefunderwear", 0) + set.getInt("basePDefcloak", 0)));
set.set("baseMDef", (set.getInt("baseMDefrear", 0) + set.getInt("baseMDeflear", 0) + set.getInt("baseMDefrfinger", 0) + set.getInt("baseMDefrfinger", 0) + set.getInt("baseMDefneck", 0)));
_charTemplates.put(ClassId.getClassId(classId), new L2PcTemplate(set, creationPoints));
}
else if ("lvlUpgainData".equalsIgnoreCase(d.getNodeName()))
{
for (Node lvlNode = d.getFirstChild(); lvlNode != null; lvlNode = lvlNode.getNextSibling())
{
if ("level".equalsIgnoreCase(lvlNode.getNodeName()))
{
attrs = lvlNode.getAttributes();
int level = parseInteger(attrs, "val");
for (Node valNode = lvlNode.getFirstChild(); valNode != null; valNode = valNode.getNextSibling())
{
String nodeName = valNode.getNodeName();
if ((nodeName.startsWith("hp") || nodeName.startsWith("mp") || nodeName.startsWith("cp")) && _charTemplates.containsKey(ClassId.getClassId(classId)))
{
_charTemplates.get(ClassId.getClassId(classId)).setUpgainValue(nodeName, level, Double.parseDouble(valNode.getTextContent()));
_dataCount++;
}
}
}
}
}
}
}
}
}
public L2PcTemplate getTemplate(final ClassId classId)
{
return _charTemplates.get(classId);
}
public L2PcTemplate getTemplate(final int classId)
{
return _charTemplates.get(ClassId.getClassId(classId));
}
public static final CharTemplateTable getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final CharTemplateTable _instance = new CharTemplateTable();
}
}

View File

@@ -0,0 +1,552 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jserver.Config;
import com.l2jserver.L2DatabaseFactory;
import com.l2jserver.gameserver.ThreadPoolManager;
import com.l2jserver.gameserver.communitybbs.Manager.ForumsBBSManager;
import com.l2jserver.gameserver.enums.UserInfoType;
import com.l2jserver.gameserver.idfactory.IdFactory;
import com.l2jserver.gameserver.instancemanager.AuctionManager;
import com.l2jserver.gameserver.instancemanager.CHSiegeManager;
import com.l2jserver.gameserver.instancemanager.FortManager;
import com.l2jserver.gameserver.instancemanager.FortSiegeManager;
import com.l2jserver.gameserver.instancemanager.SiegeManager;
import com.l2jserver.gameserver.model.ClanPrivilege;
import com.l2jserver.gameserver.model.L2Clan;
import com.l2jserver.gameserver.model.L2ClanMember;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.entity.Auction;
import com.l2jserver.gameserver.model.entity.Fort;
import com.l2jserver.gameserver.model.entity.FortSiege;
import com.l2jserver.gameserver.model.entity.Siege;
import com.l2jserver.gameserver.model.entity.clanhall.SiegableHall;
import com.l2jserver.gameserver.model.events.EventDispatcher;
import com.l2jserver.gameserver.model.events.impl.character.player.clan.OnPlayerClanCreate;
import com.l2jserver.gameserver.model.events.impl.character.player.clan.OnPlayerClanDestroy;
import com.l2jserver.gameserver.model.events.impl.clan.OnClanWarFinish;
import com.l2jserver.gameserver.model.events.impl.clan.OnClanWarStart;
import com.l2jserver.gameserver.network.SystemMessageId;
import com.l2jserver.gameserver.network.serverpackets.PledgeShowInfoUpdate;
import com.l2jserver.gameserver.network.serverpackets.PledgeShowMemberListAll;
import com.l2jserver.gameserver.network.serverpackets.PledgeShowMemberListUpdate;
import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
import com.l2jserver.gameserver.util.Util;
import com.l2jserver.util.EnumIntBitmask;
/**
* This class loads the clan related data.
*/
public class ClanTable
{
private static final Logger _log = Logger.getLogger(ClanTable.class.getName());
private final Map<Integer, L2Clan> _clans = new HashMap<>();
public L2Clan[] getClans()
{
return _clans.values().toArray(new L2Clan[_clans.size()]);
}
protected ClanTable()
{
// forums has to be loaded before clan data, because of last forum id used should have also memo included
if (Config.ENABLE_COMMUNITY_BOARD)
{
ForumsBBSManager.getInstance().initRoot();
}
L2Clan clan;
// Count the clans
int clanCount = 0;
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
Statement s = con.createStatement();
ResultSet rs = s.executeQuery("SELECT clan_id FROM clan_data"))
{
while (rs.next())
{
int clanId = rs.getInt("clan_id");
_clans.put(clanId, new L2Clan(clanId));
clan = getClan(clanId);
if (clan.getDissolvingExpiryTime() != 0)
{
scheduleRemoveClan(clan.getId());
}
clanCount++;
}
}
catch (Exception e)
{
_log.log(Level.SEVERE, "Error restoring ClanTable.", e);
}
_log.info(getClass().getSimpleName() + ": Restored " + clanCount + " clans from the database.");
allianceCheck();
restorewars();
}
/**
* @param clanId
* @return
*/
public L2Clan getClan(int clanId)
{
return _clans.get(clanId);
}
public L2Clan getClanByName(String clanName)
{
for (L2Clan clan : getClans())
{
if (clan.getName().equalsIgnoreCase(clanName))
{
return clan;
}
}
return null;
}
/**
* Creates a new clan and store clan info to database
* @param player
* @param clanName
* @return NULL if clan with same name already exists
*/
public L2Clan createClan(L2PcInstance player, String clanName)
{
if (null == player)
{
return null;
}
if (Config.DEBUG)
{
_log.info(getClass().getSimpleName() + ": " + player.getObjectId() + "(" + player.getName() + ") requested a clan creation.");
}
if (10 > player.getLevel())
{
player.sendPacket(SystemMessageId.YOU_DO_NOT_MEET_THE_CRITERIA_IN_ORDER_TO_CREATE_A_CLAN);
return null;
}
if (0 != player.getClanId())
{
player.sendPacket(SystemMessageId.YOU_HAVE_FAILED_TO_CREATE_A_CLAN);
return null;
}
if (System.currentTimeMillis() < player.getClanCreateExpiryTime())
{
player.sendPacket(SystemMessageId.YOU_MUST_WAIT_10_DAYS_BEFORE_CREATING_A_NEW_CLAN);
return null;
}
if (!Util.isAlphaNumeric(clanName) || (2 > clanName.length()))
{
player.sendPacket(SystemMessageId.CLAN_NAME_IS_INVALID);
return null;
}
if (16 < clanName.length())
{
player.sendPacket(SystemMessageId.CLAN_NAME_S_LENGTH_IS_INCORRECT);
return null;
}
if (null != getClanByName(clanName))
{
// clan name is already taken
SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_ALREADY_EXISTS);
sm.addString(clanName);
player.sendPacket(sm);
return null;
}
L2Clan clan = new L2Clan(IdFactory.getInstance().getNextId(), clanName);
L2ClanMember leader = new L2ClanMember(clan, player);
clan.setLeader(leader);
leader.setPlayerInstance(player);
clan.store();
player.setClan(clan);
player.setPledgeClass(L2ClanMember.calculatePledgeClass(player));
player.setClanPrivileges(new EnumIntBitmask<>(ClanPrivilege.class, true));
_clans.put(Integer.valueOf(clan.getId()), clan);
// should be update packet only
player.sendPacket(new PledgeShowInfoUpdate(clan));
player.sendPacket(new PledgeShowMemberListAll(clan));
player.sendPacket(new PledgeShowMemberListUpdate(player));
player.sendPacket(SystemMessageId.YOUR_CLAN_HAS_BEEN_CREATED);
player.broadcastUserInfo(UserInfoType.RELATION, UserInfoType.CLAN);
// Notify to scripts
EventDispatcher.getInstance().notifyEventAsync(new OnPlayerClanCreate(player, clan));
return clan;
}
public synchronized void destroyClan(int clanId)
{
L2Clan clan = getClan(clanId);
if (clan == null)
{
return;
}
clan.broadcastToOnlineMembers(SystemMessage.getSystemMessage(SystemMessageId.CLAN_HAS_DISPERSED));
int castleId = clan.getCastleId();
if (castleId == 0)
{
for (Siege siege : SiegeManager.getInstance().getSieges())
{
siege.removeSiegeClan(clan);
}
}
int fortId = clan.getFortId();
if (fortId == 0)
{
for (FortSiege siege : FortSiegeManager.getInstance().getSieges())
{
siege.removeAttacker(clan);
}
}
int hallId = clan.getHideoutId();
if (hallId == 0)
{
for (SiegableHall hall : CHSiegeManager.getInstance().getConquerableHalls().values())
{
hall.removeAttacker(clan);
}
}
Auction auction = AuctionManager.getInstance().getAuction(clan.getAuctionBiddedAt());
if (auction != null)
{
auction.cancelBid(clan.getId());
}
L2ClanMember leaderMember = clan.getLeader();
if (leaderMember == null)
{
clan.getWarehouse().destroyAllItems("ClanRemove", null, null);
}
else
{
clan.getWarehouse().destroyAllItems("ClanRemove", clan.getLeader().getPlayerInstance(), null);
}
for (L2ClanMember member : clan.getMembers())
{
clan.removeClanMember(member.getObjectId(), 0);
}
_clans.remove(clanId);
IdFactory.getInstance().releaseId(clanId);
try (Connection con = L2DatabaseFactory.getInstance().getConnection())
{
try (PreparedStatement ps = con.prepareStatement("DELETE FROM clan_data WHERE clan_id=?"))
{
ps.setInt(1, clanId);
ps.execute();
}
try (PreparedStatement ps = con.prepareStatement("DELETE FROM clan_privs WHERE clan_id=?"))
{
ps.setInt(1, clanId);
ps.execute();
}
try (PreparedStatement ps = con.prepareStatement("DELETE FROM clan_skills WHERE clan_id=?"))
{
ps.setInt(1, clanId);
ps.execute();
}
try (PreparedStatement ps = con.prepareStatement("DELETE FROM clan_subpledges WHERE clan_id=?"))
{
ps.setInt(1, clanId);
ps.execute();
}
try (PreparedStatement ps = con.prepareStatement("DELETE FROM clan_wars WHERE clan1=? OR clan2=?"))
{
ps.setInt(1, clanId);
ps.setInt(2, clanId);
ps.execute();
}
try (PreparedStatement ps = con.prepareStatement("DELETE FROM clan_notices WHERE clan_id=?"))
{
ps.setInt(1, clanId);
ps.execute();
}
if (castleId != 0)
{
try (PreparedStatement ps = con.prepareStatement("UPDATE castle SET taxPercent = 0 WHERE id = ?"))
{
ps.setInt(1, castleId);
ps.execute();
}
}
if (fortId != 0)
{
Fort fort = FortManager.getInstance().getFortById(fortId);
if (fort != null)
{
L2Clan owner = fort.getOwnerClan();
if (clan == owner)
{
fort.removeOwner(true);
}
}
}
if (hallId != 0)
{
SiegableHall hall = CHSiegeManager.getInstance().getSiegableHall(hallId);
if ((hall != null) && (hall.getOwnerId() == clanId))
{
hall.free();
}
}
}
catch (Exception e)
{
_log.log(Level.SEVERE, getClass().getSimpleName() + ": Error removing clan from DB.", e);
}
// Notify to scripts
EventDispatcher.getInstance().notifyEventAsync(new OnPlayerClanDestroy(leaderMember, clan));
}
public void scheduleRemoveClan(final int clanId)
{
ThreadPoolManager.getInstance().scheduleGeneral(() ->
{
if (getClan(clanId) == null)
{
return;
}
if (getClan(clanId).getDissolvingExpiryTime() != 0)
{
destroyClan(clanId);
}
}, Math.max(getClan(clanId).getDissolvingExpiryTime() - System.currentTimeMillis(), 300000));
}
public boolean isAllyExists(String allyName)
{
for (L2Clan clan : getClans())
{
if ((clan.getAllyName() != null) && clan.getAllyName().equalsIgnoreCase(allyName))
{
return true;
}
}
return false;
}
public void storeclanswars(int clanId1, int clanId2)
{
final L2Clan clan1 = ClanTable.getInstance().getClan(clanId1);
final L2Clan clan2 = ClanTable.getInstance().getClan(clanId2);
EventDispatcher.getInstance().notifyEventAsync(new OnClanWarStart(clan1, clan2));
clan1.setEnemyClan(clan2);
clan2.setAttackerClan(clan1);
clan1.broadcastClanStatus();
clan2.broadcastClanStatus();
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement ps = con.prepareStatement("REPLACE INTO clan_wars (clan1, clan2, wantspeace1, wantspeace2) VALUES(?,?,?,?)"))
{
ps.setInt(1, clanId1);
ps.setInt(2, clanId2);
ps.setInt(3, 0);
ps.setInt(4, 0);
ps.execute();
}
catch (Exception e)
{
_log.log(Level.SEVERE, getClass().getSimpleName() + ": Error storing clan wars data.", e);
}
// SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.A_CLAN_WAR_WITH_CLAN_S1_HAS_STARTED_THE_CLAN_THAT_CANCELS_THE_WAR_FIRST_WILL_LOSE_5_000_CLAN_REPUTATION_ANY_CLAN_THAT_CANCELS_THE_WAR_WILL_BE_UNABLE_TO_DECLARE_A_WAR_FOR_1_WEEK_IF_YOUR_CLAN_MEMBER_GETS_KILLED_BY_THE_OTHER_CLAN_XP_DECREASES_BY_1_4_OF_THE_AMOUNT_THAT_DECREASES_IN_THE_HUNTING_GROUND);
//
SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_DECLARED_A_CLAN_WAR_WITH_S1);
msg.addString(clan2.getName());
clan1.broadcastToOnlineMembers(msg);
// msg = SystemMessage.getSystemMessage(SystemMessageId.A_CLAN_WAR_WITH_CLAN_S1_HAS_STARTED_THE_CLAN_THAT_CANCELS_THE_WAR_FIRST_WILL_LOSE_5_000_CLAN_REPUTATION_ANY_CLAN_THAT_CANCELS_THE_WAR_WILL_BE_UNABLE_TO_DECLARE_A_WAR_FOR_1_WEEK_IF_YOUR_CLAN_MEMBER_GETS_KILLED_BY_THE_OTHER_CLAN_XP_DECREASES_BY_1_4_OF_THE_AMOUNT_THAT_DECREASES_IN_THE_HUNTING_GROUND);
// msg.addString(clan1.getName());
// clan2.broadcastToOnlineMembers(msg);
// clan1 declared clan war.
msg = SystemMessage.getSystemMessage(SystemMessageId.S1_HAS_DECLARED_A_CLAN_WAR_THE_WAR_WILL_AUTOMATICALLY_START_IF_YOU_KILL_S1_CLAN_MEMBERS_5_TIMES_WITHIN_A_WEEK);
msg.addString(clan1.getName());
clan2.broadcastToOnlineMembers(msg);
}
public void deleteclanswars(int clanId1, int clanId2)
{
L2Clan clan1 = ClanTable.getInstance().getClan(clanId1);
L2Clan clan2 = ClanTable.getInstance().getClan(clanId2);
EventDispatcher.getInstance().notifyEventAsync(new OnClanWarFinish(clan1, clan2));
clan1.deleteEnemyClan(clan2);
clan2.deleteAttackerClan(clan1);
clan1.broadcastClanStatus();
clan2.broadcastClanStatus();
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement ps = con.prepareStatement("DELETE FROM clan_wars WHERE clan1=? AND clan2=?"))
{
ps.setInt(1, clanId1);
ps.setInt(2, clanId2);
ps.execute();
}
catch (Exception e)
{
_log.log(Level.SEVERE, getClass().getSimpleName() + ": Error removing clan wars data.", e);
}
// SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.WAR_WITH_THE_S1_CLAN_HAS_ENDED);
SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.THE_WAR_AGAINST_S1_CLAN_HAS_BEEN_STOPPED);
msg.addString(clan2.getName());
clan1.broadcastToOnlineMembers(msg);
msg = SystemMessage.getSystemMessage(SystemMessageId.THE_CLAN_S1_HAS_DECIDED_TO_STOP_THE_WAR);
msg.addString(clan1.getName());
clan2.broadcastToOnlineMembers(msg);
}
public void checkSurrender(L2Clan clan1, L2Clan clan2)
{
int count = 0;
for (L2ClanMember player : clan1.getMembers())
{
if ((player != null) && (player.getPlayerInstance().getWantsPeace() == 1))
{
count++;
}
}
if (count == (clan1.getMembers().length - 1))
{
clan1.deleteEnemyClan(clan2);
clan2.deleteEnemyClan(clan1);
deleteclanswars(clan1.getId(), clan2.getId());
}
}
private void restorewars()
{
L2Clan clan1, clan2;
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
Statement statement = con.createStatement();
ResultSet rset = statement.executeQuery("SELECT clan1, clan2 FROM clan_wars"))
{
while (rset.next())
{
clan1 = getClan(rset.getInt("clan1"));
clan2 = getClan(rset.getInt("clan2"));
if ((clan1 != null) && (clan2 != null))
{
clan1.setEnemyClan(rset.getInt("clan2"));
clan2.setAttackerClan(rset.getInt("clan1"));
}
else
{
_log.log(Level.WARNING, getClass().getSimpleName() + ": restorewars one of clans is null clan1:" + clan1 + " clan2:" + clan2);
}
}
}
catch (Exception e)
{
_log.log(Level.SEVERE, getClass().getSimpleName() + ": Error restoring clan wars data.", e);
}
}
/**
* Check for nonexistent alliances
*/
private void allianceCheck()
{
for (L2Clan clan : _clans.values())
{
int allyId = clan.getAllyId();
if ((allyId != 0) && (clan.getId() != allyId))
{
if (!_clans.containsKey(allyId))
{
clan.setAllyId(0);
clan.setAllyName(null);
clan.changeAllyCrest(0, true);
clan.updateClanInDB();
_log.info(getClass().getSimpleName() + ": Removed alliance from clan: " + clan);
}
}
}
}
public List<L2Clan> getClanAllies(int allianceId)
{
final List<L2Clan> clanAllies = new ArrayList<>();
if (allianceId != 0)
{
for (L2Clan clan : _clans.values())
{
if ((clan != null) && (clan.getAllyId() == allianceId))
{
clanAllies.add(clan);
}
}
}
return clanAllies;
}
public void storeClanScore()
{
for (L2Clan clan : _clans.values())
{
clan.updateClanScoreInDB();
}
}
public static ClanTable getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final ClanTable _instance = new ClanTable();
}
}

View File

@@ -0,0 +1,130 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.HashMap;
import java.util.Map;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.model.base.ClassId;
import com.l2jserver.gameserver.model.base.ClassInfo;
/**
* This class holds the list of classes and it's info.<br>
* It's in <i>beta</i> state, so it's expected to change over time.
* @author Zoey76
*/
public final class ClassListData implements DocumentParser
{
private static final Map<ClassId, ClassInfo> _classData = new HashMap<>();
/**
* Instantiates a new class list data.
*/
protected ClassListData()
{
load();
}
@Override
public void load()
{
_classData.clear();
parseDatapackFile("data/stats/chars/classList.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _classData.size() + " Class data.");
}
@Override
public void parseDocument(Document doc)
{
NamedNodeMap attrs;
Node attr;
ClassId classId;
String className;
ClassId parentClassId;
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equals(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
attrs = d.getAttributes();
if ("class".equals(d.getNodeName()))
{
attr = attrs.getNamedItem("classId");
classId = ClassId.getClassId(parseInteger(attr));
attr = attrs.getNamedItem("name");
className = attr.getNodeValue();
attr = attrs.getNamedItem("parentClassId");
parentClassId = (attr != null) ? ClassId.getClassId(parseInteger(attr)) : null;
_classData.put(classId, new ClassInfo(classId, className, parentClassId));
}
}
}
}
}
/**
* Gets the class list.
* @return the complete class list.
*/
public Map<ClassId, ClassInfo> getClassList()
{
return _classData;
}
/**
* Gets the class info.
* @param classId the class Id.
* @return the class info related to the given {@code classId}.
*/
public ClassInfo getClass(final ClassId classId)
{
return _classData.get(classId);
}
/**
* Gets the class info.
* @param classId the class Id as integer.
* @return the class info related to the given {@code classId}.
*/
public ClassInfo getClass(final int classId)
{
final ClassId id = ClassId.getClassId(classId);
return (id != null) ? _classData.get(id) : null;
}
/**
* Gets the single instance of ClassListData.
* @return single instance of ClassListData
*/
public static ClassListData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final ClassListData _instance = new ClassListData();
}
}

View File

@@ -0,0 +1,321 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.io.File;
import java.nio.file.Files;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jserver.Config;
import com.l2jserver.L2DatabaseFactory;
import com.l2jserver.gameserver.model.L2Clan;
import com.l2jserver.gameserver.model.L2Crest;
import com.l2jserver.gameserver.model.L2Crest.CrestType;
import com.l2jserver.util.file.filter.BMPFilter;
/**
* @author NosBit
*/
public final class CrestTable
{
private static final Logger LOGGER = Logger.getLogger(CrestTable.class.getName());
private final Map<Integer, L2Crest> _crests = new ConcurrentHashMap<>();
private final AtomicInteger _nextId = new AtomicInteger(1);
protected CrestTable()
{
load();
}
public synchronized void load()
{
_crests.clear();
Set<Integer> crestsInUse = new HashSet<>();
for (L2Clan clan : ClanTable.getInstance().getClans())
{
if (clan.getCrestId() != 0)
{
crestsInUse.add(clan.getCrestId());
}
if (clan.getCrestLargeId() != 0)
{
crestsInUse.add(clan.getCrestLargeId());
}
if (clan.getAllyCrestId() != 0)
{
crestsInUse.add(clan.getAllyCrestId());
}
}
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
Statement statement = con.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);
ResultSet rs = statement.executeQuery("SELECT `crest_id`, `data`, `type` FROM `crests` ORDER BY `crest_id` DESC"))
{
while (rs.next())
{
int id = rs.getInt("crest_id");
if (_nextId.get() <= id)
{
_nextId.set(id + 1);
}
// delete all unused crests except the last one we dont want to reuse
// a crest id because client will display wrong crest if its reused
if (!crestsInUse.contains(id) && (id != (_nextId.get() - 1)))
{
rs.deleteRow();
continue;
}
byte[] data = rs.getBytes("data");
CrestType crestType = CrestType.getById(rs.getInt("type"));
if (crestType != null)
{
_crests.put(id, new L2Crest(id, data, crestType));
}
else
{
LOGGER.warning("Unknown crest type found in database. Type:" + rs.getInt("type"));
}
}
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "There was an error while loading crests from database:", e);
}
moveOldCrestsToDb(crestsInUse);
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crests.size() + " Crests.");
for (L2Clan clan : ClanTable.getInstance().getClans())
{
if (clan.getCrestId() != 0)
{
if (getCrest(clan.getCrestId()) == null)
{
LOGGER.info("Removing non-existent crest for clan " + clan.getName() + " [" + clan.getId() + "], crestId:" + clan.getCrestId());
clan.setCrestId(0);
clan.changeClanCrest(0);
}
}
if (clan.getCrestLargeId() != 0)
{
if (getCrest(clan.getCrestLargeId()) == null)
{
LOGGER.info("Removing non-existent large crest for clan " + clan.getName() + " [" + clan.getId() + "], crestLargeId:" + clan.getCrestLargeId());
clan.setCrestLargeId(0);
clan.changeLargeCrest(0);
}
}
if (clan.getAllyCrestId() != 0)
{
if (getCrest(clan.getAllyCrestId()) == null)
{
LOGGER.info("Removing non-existent ally crest for clan " + clan.getName() + " [" + clan.getId() + "], allyCrestId:" + clan.getAllyCrestId());
clan.setAllyCrestId(0);
clan.changeAllyCrest(0, true);
}
}
}
}
/**
* Moves old crests from data/crests folder to database and deletes crest folder<br>
* <b>TODO:</b> remove it after some time
* @param crestsInUse the set of crests in use
*/
private void moveOldCrestsToDb(Set<Integer> crestsInUse)
{
final File crestDir = new File(Config.DATAPACK_ROOT, "data/crests/");
if (crestDir.exists())
{
for (File file : crestDir.listFiles(new BMPFilter()))
{
try
{
final byte[] data = Files.readAllBytes(file.toPath());
if (file.getName().startsWith("Crest_Large_"))
{
final int crestId = Integer.parseInt(file.getName().substring(12, file.getName().length() - 4));
if (crestsInUse.contains(crestId))
{
final L2Crest crest = createCrest(data, CrestType.PLEDGE_LARGE);
if (crest != null)
{
for (L2Clan clan : ClanTable.getInstance().getClans())
{
if (clan.getCrestLargeId() == crestId)
{
clan.setCrestLargeId(0);
clan.changeLargeCrest(crest.getId());
}
}
}
}
}
else if (file.getName().startsWith("Crest_"))
{
final int crestId = Integer.parseInt(file.getName().substring(6, file.getName().length() - 4));
if (crestsInUse.contains(crestId))
{
final L2Crest crest = createCrest(data, CrestType.PLEDGE);
if (crest != null)
{
for (L2Clan clan : ClanTable.getInstance().getClans())
{
if (clan.getCrestId() == crestId)
{
clan.setCrestId(0);
clan.changeClanCrest(crest.getId());
}
}
}
}
}
else if (file.getName().startsWith("AllyCrest_"))
{
final int crestId = Integer.parseInt(file.getName().substring(10, file.getName().length() - 4));
if (crestsInUse.contains(crestId))
{
final L2Crest crest = createCrest(data, CrestType.ALLY);
if (crest != null)
{
for (L2Clan clan : ClanTable.getInstance().getClans())
{
if (clan.getAllyCrestId() == crestId)
{
clan.setAllyCrestId(0);
clan.changeAllyCrest(crest.getId(), false);
}
}
}
}
}
file.delete();
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "There was an error while moving crest file " + file.getName() + " to database:", e);
}
}
crestDir.delete();
}
}
/**
* @param crestId The crest id
* @return {@code L2Crest} if crest is found, {@code null} if crest was not found.
*/
public L2Crest getCrest(int crestId)
{
return _crests.get(crestId);
}
/**
* Creates a {@code L2Crest} object and inserts it in database and cache.
* @param data
* @param crestType
* @return {@code L2Crest} on success, {@code null} on failure.
*/
public L2Crest createCrest(byte[] data, CrestType crestType)
{
try (Connection con = L2DatabaseFactory.getInstance().getConnection())
{
try (PreparedStatement statement = con.prepareStatement("INSERT INTO `crests`(`crest_id`, `data`, `type`) VALUES(?, ?, ?)"))
{
final L2Crest crest = new L2Crest(getNextId(), data, crestType);
statement.setInt(1, crest.getId());
statement.setBytes(2, crest.getData());
statement.setInt(3, crest.getType().getId());
statement.executeUpdate();
_crests.put(crest.getId(), crest);
return crest;
}
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "There was an error while saving crest in database:", e);
}
return null;
}
/**
* Removes crest from database and cache.
* @param crestId the id of crest to be removed.
*/
public void removeCrest(int crestId)
{
_crests.remove(crestId);
// avoid removing last crest id we dont want to lose index...
// because client will display wrong crest if its reused
if (crestId == (_nextId.get() - 1))
{
return;
}
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement statement = con.prepareStatement("DELETE FROM `crests` WHERE `crest_id` = ?"))
{
statement.setInt(1, crestId);
statement.executeUpdate();
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "There was an error while deleting crest from database:", e);
}
}
/**
* @return The next crest id.
*/
public int getNextId()
{
return _nextId.getAndIncrement();
}
public static CrestTable getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final CrestTable _instance = new CrestTable();
}
}

View File

@@ -0,0 +1,263 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.idfactory.IdFactory;
import com.l2jserver.gameserver.instancemanager.InstanceManager;
import com.l2jserver.gameserver.instancemanager.MapRegionManager;
import com.l2jserver.gameserver.model.StatsSet;
import com.l2jserver.gameserver.model.actor.instance.L2DoorInstance;
import com.l2jserver.gameserver.model.actor.templates.L2DoorTemplate;
import com.l2jserver.gameserver.pathfinding.AbstractNodeLoc;
/**
* @author JIV, GodKratos, UnAfraid
*/
public class DoorTable implements DocumentParser
{
private static final Map<String, Set<Integer>> _groups = new HashMap<>();
private final Map<Integer, L2DoorInstance> _doors = new HashMap<>();
private final Map<Integer, StatsSet> _templates = new HashMap<>();
private final Map<Integer, List<L2DoorInstance>> _regions = new HashMap<>();
protected DoorTable()
{
load();
}
@Override
public void load()
{
_doors.clear();
_groups.clear();
_regions.clear();
parseDatapackFile("data/doors.xml");
}
@Override
public void parseDocument(Document doc)
{
NamedNodeMap attrs;
Node att;
StatsSet set;
for (Node a = doc.getFirstChild(); a != null; a = a.getNextSibling())
{
if ("list".equalsIgnoreCase(a.getNodeName()))
{
for (Node b = a.getFirstChild(); b != null; b = b.getNextSibling())
{
if ("door".equalsIgnoreCase(b.getNodeName()))
{
attrs = b.getAttributes();
set = new StatsSet();
set.set("baseHpMax", 1); // Avoid doors without HP value created dead due to default value 0 in L2CharTemplate
for (int i = 0; i < attrs.getLength(); i++)
{
att = attrs.item(i);
set.set(att.getNodeName(), att.getNodeValue());
}
makeDoor(set);
_templates.put(set.getInt("id"), set);
}
}
}
}
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _doors.size() + " Door Templates for " + _regions.size() + " regions.");
}
public void insertCollisionData(StatsSet set)
{
int posX, posY, nodeX, nodeY, height;
height = set.getInt("height");
String[] pos = set.getString("node1").split(",");
nodeX = Integer.parseInt(pos[0]);
nodeY = Integer.parseInt(pos[1]);
pos = set.getString("node2").split(",");
posX = Integer.parseInt(pos[0]);
posY = Integer.parseInt(pos[1]);
int collisionRadius; // (max) radius for movement checks
collisionRadius = Math.min(Math.abs(nodeX - posX), Math.abs(nodeY - posY));
if (collisionRadius < 20)
{
collisionRadius = 20;
}
set.set("collision_radius", collisionRadius);
set.set("collision_height", height);
}
/**
* @param set
*/
private void makeDoor(StatsSet set)
{
insertCollisionData(set);
L2DoorTemplate template = new L2DoorTemplate(set);
L2DoorInstance door = new L2DoorInstance(IdFactory.getInstance().getNextId(), template);
door.setCurrentHp(door.getMaxHp());
door.spawnMe(template.getX(), template.getY(), template.getZ());
putDoor(door, MapRegionManager.getInstance().getMapRegionLocId(door));
}
public StatsSet getDoorTemplate(int doorId)
{
return _templates.get(doorId);
}
public L2DoorInstance getDoor(int doorId)
{
return _doors.get(doorId);
}
public void putDoor(L2DoorInstance door, int region)
{
_doors.put(door.getId(), door);
if (!_regions.containsKey(region))
{
_regions.put(region, new ArrayList<L2DoorInstance>());
}
_regions.get(region).add(door);
}
public static void addDoorGroup(String groupName, int doorId)
{
Set<Integer> set = _groups.get(groupName);
if (set == null)
{
set = new HashSet<>();
_groups.put(groupName, set);
}
set.add(doorId);
}
public static Set<Integer> getDoorsByGroup(String groupName)
{
return _groups.get(groupName);
}
public Collection<L2DoorInstance> getDoors()
{
return _doors.values();
}
public boolean checkIfDoorsBetween(AbstractNodeLoc start, AbstractNodeLoc end, int instanceId)
{
return checkIfDoorsBetween(start.getX(), start.getY(), start.getZ(), end.getX(), end.getY(), end.getZ(), instanceId);
}
public boolean checkIfDoorsBetween(int x, int y, int z, int tx, int ty, int tz, int instanceId)
{
return checkIfDoorsBetween(x, y, z, tx, ty, tz, instanceId, false);
}
/**
* GodKratos: TODO: remove GeoData checks from door table and convert door nodes to Geo zones
* @param x
* @param y
* @param z
* @param tx
* @param ty
* @param tz
* @param instanceId
* @param doubleFaceCheck
* @return {@code boolean}
*/
public boolean checkIfDoorsBetween(int x, int y, int z, int tx, int ty, int tz, int instanceId, boolean doubleFaceCheck)
{
Collection<L2DoorInstance> allDoors;
if ((instanceId > 0) && (InstanceManager.getInstance().getInstance(instanceId) != null))
{
allDoors = InstanceManager.getInstance().getInstance(instanceId).getDoors();
}
else
{
allDoors = _regions.get(MapRegionManager.getInstance().getMapRegionLocId(x, y));
}
if (allDoors == null)
{
return false;
}
for (L2DoorInstance doorInst : allDoors)
{
// check dead and open
if (doorInst.isDead() || doorInst.getOpen() || !doorInst.checkCollision() || (doorInst.getX(0) == 0))
{
continue;
}
boolean intersectFace = false;
for (int i = 0; i < 4; i++)
{
int j = (i + 1) < 4 ? i + 1 : 0;
// lower part of the multiplier fraction, if it is 0 we avoid an error and also know that the lines are parallel
int denominator = ((ty - y) * (doorInst.getX(i) - doorInst.getX(j))) - ((tx - x) * (doorInst.getY(i) - doorInst.getY(j)));
if (denominator == 0)
{
continue;
}
// multipliers to the equations of the lines. If they are lower than 0 or bigger than 1, we know that segments don't intersect
float multiplier1 = (float) (((doorInst.getX(j) - doorInst.getX(i)) * (y - doorInst.getY(i))) - ((doorInst.getY(j) - doorInst.getY(i)) * (x - doorInst.getX(i)))) / denominator;
float multiplier2 = (float) (((tx - x) * (y - doorInst.getY(i))) - ((ty - y) * (x - doorInst.getX(i)))) / denominator;
if ((multiplier1 >= 0) && (multiplier1 <= 1) && (multiplier2 >= 0) && (multiplier2 <= 1))
{
int intersectZ = Math.round(z + (multiplier1 * (tz - z)));
// now checking if the resulting point is between door's min and max z
if ((intersectZ > doorInst.getZMin()) && (intersectZ < doorInst.getZMax()))
{
if (!doubleFaceCheck || intersectFace)
{
return true;
}
intersectFace = true;
}
}
}
}
return false;
}
public static DoorTable getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final DoorTable _instance = new DoorTable();
}
}

View File

@@ -0,0 +1,167 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.model.StatsSet;
import com.l2jserver.gameserver.model.items.enchant.EnchantScroll;
import com.l2jserver.gameserver.model.items.enchant.EnchantSupportItem;
import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
/**
* This class holds the Enchant Item information.
* @author UnAfraid
*/
public class EnchantItemData implements DocumentParser
{
public static final Map<Integer, EnchantScroll> _scrolls = new HashMap<>();
public static final Map<Integer, EnchantSupportItem> _supports = new HashMap<>();
/**
* Instantiates a new enchant item data.
*/
public EnchantItemData()
{
load();
}
@Override
public synchronized void load()
{
_scrolls.clear();
_supports.clear();
parseDatapackFile("data/enchantItemData.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _scrolls.size() + " Enchant Scrolls.");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _supports.size() + " Support Items.");
}
@Override
public void parseDocument(Document doc)
{
StatsSet set;
Node att;
NamedNodeMap attrs;
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("enchant".equalsIgnoreCase(d.getNodeName()))
{
attrs = d.getAttributes();
set = new StatsSet();
for (int i = 0; i < attrs.getLength(); i++)
{
att = attrs.item(i);
set.set(att.getNodeName(), att.getNodeValue());
}
try
{
final EnchantScroll item = new EnchantScroll(set);
for (Node cd = d.getFirstChild(); cd != null; cd = cd.getNextSibling())
{
if ("item".equalsIgnoreCase(cd.getNodeName()))
{
item.addItem(parseInteger(cd.getAttributes(), "id"));
}
}
_scrolls.put(item.getId(), item);
}
catch (NullPointerException e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Unexistent enchant scroll: " + set.getString("id") + " defined in enchant data!");
}
catch (IllegalAccessError e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Wrong enchant scroll item type: " + set.getString("id") + " defined in enchant data!");
}
}
else if ("support".equalsIgnoreCase(d.getNodeName()))
{
attrs = d.getAttributes();
set = new StatsSet();
for (int i = 0; i < attrs.getLength(); i++)
{
att = attrs.item(i);
set.set(att.getNodeName(), att.getNodeValue());
}
try
{
final EnchantSupportItem item = new EnchantSupportItem(set);
_supports.put(item.getId(), item);
}
catch (NullPointerException e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Unexistent enchant support item: " + set.getString("id") + " defined in enchant data!");
}
catch (IllegalAccessError e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Wrong enchant support item type: " + set.getString("id") + " defined in enchant data!");
}
}
}
}
}
}
/**
* Gets the enchant scroll.
* @param scroll the scroll
* @return enchant template for scroll
*/
public final EnchantScroll getEnchantScroll(L2ItemInstance scroll)
{
return _scrolls.get(scroll.getId());
}
/**
* Gets the support item.
* @param item the item
* @return enchant template for support item
*/
public final EnchantSupportItem getSupportItem(L2ItemInstance item)
{
return _supports.get(item.getId());
}
/**
* Gets the single instance of EnchantItemData.
* @return single instance of EnchantItemData
*/
public static final EnchantItemData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final EnchantItemData _instance = new EnchantItemData();
}
}

View File

@@ -0,0 +1,167 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.model.holders.RangeChanceHolder;
import com.l2jserver.gameserver.model.items.L2Item;
import com.l2jserver.gameserver.model.items.enchant.EnchantItemGroup;
import com.l2jserver.gameserver.model.items.enchant.EnchantRateItem;
import com.l2jserver.gameserver.model.items.enchant.EnchantScrollGroup;
import com.l2jserver.gameserver.util.Util;
/**
* @author UnAfraid
*/
public final class EnchantItemGroupsData implements DocumentParser
{
private final Map<String, EnchantItemGroup> _itemGroups = new HashMap<>();
private final Map<Integer, EnchantScrollGroup> _scrollGroups = new HashMap<>();
protected EnchantItemGroupsData()
{
load();
}
@Override
public synchronized void load()
{
_itemGroups.clear();
_scrollGroups.clear();
parseDatapackFile("data/enchantItemGroups.xml");
LOGGER.log(Level.INFO, getClass().getSimpleName() + ": Loaded: " + _itemGroups.size() + " item group templates.");
LOGGER.log(Level.INFO, getClass().getSimpleName() + ": Loaded: " + _scrollGroups.size() + " scroll group templates.");
}
@Override
public void parseDocument(Document doc)
{
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("enchantRateGroup".equalsIgnoreCase(d.getNodeName()))
{
String name = parseString(d.getAttributes(), "name");
final EnchantItemGroup group = new EnchantItemGroup(name);
for (Node cd = d.getFirstChild(); cd != null; cd = cd.getNextSibling())
{
if ("current".equalsIgnoreCase(cd.getNodeName()))
{
String range = parseString(cd.getAttributes(), "enchant");
double chance = parseDouble(cd.getAttributes(), "chance");
int min = -1;
int max = 0;
if (range.contains("-"))
{
String[] split = range.split("-");
if ((split.length == 2) && Util.isDigit(split[0]) && Util.isDigit(split[1]))
{
min = Integer.parseInt(split[0]);
max = Integer.parseInt(split[1]);
}
}
else if (Util.isDigit(range))
{
min = Integer.parseInt(range);
max = min;
}
if ((min > -1) && (max > 0))
{
group.addChance(new RangeChanceHolder(min, max, chance));
}
}
}
_itemGroups.put(name, group);
}
else if ("enchantScrollGroup".equals(d.getNodeName()))
{
int id = parseInteger(d.getAttributes(), "id");
final EnchantScrollGroup group = new EnchantScrollGroup(id);
for (Node cd = d.getFirstChild(); cd != null; cd = cd.getNextSibling())
{
if ("enchantRate".equalsIgnoreCase(cd.getNodeName()))
{
final EnchantRateItem rateGroup = new EnchantRateItem(parseString(cd.getAttributes(), "group"));
for (Node z = cd.getFirstChild(); z != null; z = z.getNextSibling())
{
if ("item".equals(z.getNodeName()))
{
final NamedNodeMap attrs = z.getAttributes();
if (attrs.getNamedItem("slot") != null)
{
rateGroup.addSlot(ItemTable._slots.get(parseString(attrs, "slot")));
}
if (attrs.getNamedItem("magicWeapon") != null)
{
rateGroup.setMagicWeapon(parseBoolean(attrs, "magicWeapon"));
}
if (attrs.getNamedItem("id") != null)
{
rateGroup.setItemId(parseInteger(attrs, "id"));
}
}
}
group.addRateGroup(rateGroup);
}
}
_scrollGroups.put(id, group);
}
}
}
}
}
public EnchantItemGroup getItemGroup(L2Item item, int scrollGroup)
{
final EnchantScrollGroup group = _scrollGroups.get(scrollGroup);
final EnchantRateItem rateGroup = group.getRateGroup(item);
return rateGroup != null ? _itemGroups.get(rateGroup.getName()) : null;
}
public EnchantItemGroup getItemGroup(String name)
{
return _itemGroups.get(name);
}
public EnchantScrollGroup getScrollGroup(int id)
{
return _scrollGroups.get(id);
}
public static EnchantItemGroupsData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final EnchantItemGroupsData _instance = new EnchantItemGroupsData();
}
}

View File

@@ -0,0 +1,156 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.enums.StatFunction;
import com.l2jserver.gameserver.model.items.L2Item;
import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
import com.l2jserver.gameserver.model.items.type.CrystalType;
import com.l2jserver.gameserver.model.stats.Stats;
import com.l2jserver.gameserver.model.stats.functions.FuncTemplate;
/**
* This class holds the Enchant HP Bonus Data.
* @author MrPoke, Zoey76
*/
public class EnchantItemHPBonusData implements DocumentParser
{
private final Map<CrystalType, List<Integer>> _armorHPBonuses = new EnumMap<>(CrystalType.class);
private static final float FULL_ARMOR_MODIFIER = 1.5f; // TODO: Move it to config!
/**
* Instantiates a new enchant hp bonus data.
*/
protected EnchantItemHPBonusData()
{
load();
}
@Override
public void parseDocument(Document doc)
{
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("enchantHP".equalsIgnoreCase(d.getNodeName()))
{
List<Integer> bonuses = new ArrayList<>();
for (Node e = d.getFirstChild(); e != null; e = e.getNextSibling())
{
if ("bonus".equalsIgnoreCase(e.getNodeName()))
{
bonuses.add(Integer.valueOf(e.getTextContent()));
}
}
_armorHPBonuses.put(parseEnum(d.getAttributes(), CrystalType.class, "grade"), bonuses);
}
}
}
}
if (!_armorHPBonuses.isEmpty())
{
final ItemTable it = ItemTable.getInstance();
L2Item item;
// Armors
final Collection<Integer> armorIds = it.getAllArmorsId();
for (Integer itemId : armorIds)
{
item = it.getTemplate(itemId);
if ((item != null) && (item.getCrystalType() != CrystalType.NONE))
{
switch (item.getBodyPart())
{
case L2Item.SLOT_CHEST:
case L2Item.SLOT_FEET:
case L2Item.SLOT_GLOVES:
case L2Item.SLOT_HEAD:
case L2Item.SLOT_LEGS:
case L2Item.SLOT_BACK:
case L2Item.SLOT_FULL_ARMOR:
case L2Item.SLOT_UNDERWEAR:
case L2Item.SLOT_L_HAND:
case L2Item.SLOT_BELT:
item.attach(new FuncTemplate(null, null, StatFunction.ENCHANTHP.getName(), -1, Stats.MAX_HP, 0));
break;
default:
break;
}
}
}
}
}
@Override
public void load()
{
_armorHPBonuses.clear();
parseDatapackFile("data/stats/enchantHPBonus.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _armorHPBonuses.size() + " Enchant HP Bonuses.");
}
/**
* Gets the HP bonus.
* @param item the item
* @return the HP bonus
*/
public final int getHPBonus(L2ItemInstance item)
{
final List<Integer> values = _armorHPBonuses.get(item.getItem().getItemGradeSPlus());
if ((values == null) || values.isEmpty() || (item.getOlyEnchantLevel() <= 0))
{
return 0;
}
final int bonus = values.get(Math.min(item.getOlyEnchantLevel(), values.size()) - 1);
if (item.getItem().getBodyPart() == L2Item.SLOT_FULL_ARMOR)
{
return (int) (bonus * FULL_ARMOR_MODIFIER);
}
return bonus;
}
/**
* Gets the single instance of EnchantHPBonusData.
* @return single instance of EnchantHPBonusData
*/
public static final EnchantItemHPBonusData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final EnchantItemHPBonusData _instance = new EnchantItemHPBonusData();
}
}

View File

@@ -0,0 +1,132 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
import com.l2jserver.gameserver.model.options.EnchantOptions;
import com.l2jserver.gameserver.util.Util;
/**
* @author UnAfraid
*/
public class EnchantItemOptionsData implements DocumentParser
{
private final Map<Integer, Map<Integer, EnchantOptions>> _data = new HashMap<>();
protected EnchantItemOptionsData()
{
load();
}
@Override
public synchronized void load()
{
_data.clear();
parseDatapackFile("data/enchantItemOptions.xml");
}
@Override
public void parseDocument(Document doc)
{
Node att = null;
int counter = 0;
EnchantOptions op = null;
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("item".equalsIgnoreCase(d.getNodeName()))
{
int itemId = parseInteger(d.getAttributes(), "id");
if (!_data.containsKey(itemId))
{
_data.put(itemId, new HashMap<Integer, EnchantOptions>());
}
for (Node cd = d.getFirstChild(); cd != null; cd = cd.getNextSibling())
{
if ("options".equalsIgnoreCase(cd.getNodeName()))
{
op = new EnchantOptions(parseInteger(cd.getAttributes(), "level"));
_data.get(itemId).put(op.getLevel(), op);
for (byte i = 0; i < 3; i++)
{
att = cd.getAttributes().getNamedItem("option" + (i + 1));
if ((att != null) && Util.isDigit(att.getNodeValue()))
{
op.setOption(i, parseInteger(att));
}
}
counter++;
}
}
}
}
}
}
LOGGER.log(Level.INFO, getClass().getSimpleName() + ": Loaded: " + _data.size() + " Items and " + counter + " Options.");
}
/**
* @param itemId
* @param enchantLevel
* @return enchant effects information.
*/
public EnchantOptions getOptions(int itemId, int enchantLevel)
{
if (!_data.containsKey(itemId) || !_data.get(itemId).containsKey(enchantLevel))
{
return null;
}
return _data.get(itemId).get(enchantLevel);
}
/**
* @param item
* @return enchant effects information.
*/
public EnchantOptions getOptions(L2ItemInstance item)
{
return item != null ? getOptions(item.getId(), item.getEnchantLevel()) : null;
}
/**
* Gets the single instance of EnchantOptionsData.
* @return single instance of EnchantOptionsData
*/
public static final EnchantItemOptionsData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final EnchantItemOptionsData _instance = new EnchantItemOptionsData();
}
}

View File

@@ -0,0 +1,257 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.Config;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.model.L2EnchantSkillGroup;
import com.l2jserver.gameserver.model.L2EnchantSkillGroup.EnchantSkillHolder;
import com.l2jserver.gameserver.model.L2EnchantSkillLearn;
import com.l2jserver.gameserver.model.StatsSet;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.skills.Skill;
/**
* This class holds the Enchant Groups information.
* @author Micr0
*/
public class EnchantSkillGroupsData implements DocumentParser
{
public static final int NORMAL_ENCHANT_COST_MULTIPLIER = Config.NORMAL_ENCHANT_COST_MULTIPLIER;
public static final int SAFE_ENCHANT_COST_MULTIPLIER = Config.SAFE_ENCHANT_COST_MULTIPLIER;
public static final int NORMAL_ENCHANT_BOOK = 6622;
public static final int SAFE_ENCHANT_BOOK = 9627;
public static final int CHANGE_ENCHANT_BOOK = 9626;
public static final int UNTRAIN_ENCHANT_BOOK = 9625;
private final Map<Integer, L2EnchantSkillGroup> _enchantSkillGroups = new HashMap<>();
private final Map<Integer, L2EnchantSkillLearn> _enchantSkillTrees = new HashMap<>();
/**
* Instantiates a new enchant groups table.
*/
protected EnchantSkillGroupsData()
{
load();
}
@Override
public void load()
{
_enchantSkillGroups.clear();
_enchantSkillTrees.clear();
parseDatapackFile("data/enchantSkillGroups.xml");
int routes = 0;
for (L2EnchantSkillGroup group : _enchantSkillGroups.values())
{
routes += group.getEnchantGroupDetails().size();
}
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _enchantSkillGroups.size() + " groups and " + routes + " routes.");
}
@Override
public void parseDocument(Document doc)
{
NamedNodeMap attrs;
StatsSet set;
Node att;
int id = 0;
L2EnchantSkillGroup group;
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("group".equalsIgnoreCase(d.getNodeName()))
{
attrs = d.getAttributes();
id = parseInteger(attrs, "id");
group = _enchantSkillGroups.get(id);
if (group == null)
{
group = new L2EnchantSkillGroup(id);
_enchantSkillGroups.put(id, group);
}
for (Node b = d.getFirstChild(); b != null; b = b.getNextSibling())
{
if ("enchant".equalsIgnoreCase(b.getNodeName()))
{
attrs = b.getAttributes();
set = new StatsSet();
for (int i = 0; i < attrs.getLength(); i++)
{
att = attrs.item(i);
set.set(att.getNodeName(), att.getNodeValue());
}
group.addEnchantDetail(new EnchantSkillHolder(set));
}
}
}
}
}
}
}
/**
* Adds the new route for skill.
* @param skillId the skill id
* @param maxLvL the max lvl
* @param route the route
* @param group the group
* @return the int
*/
public int addNewRouteForSkill(int skillId, int maxLvL, int route, int group)
{
L2EnchantSkillLearn enchantableSkill = _enchantSkillTrees.get(skillId);
if (enchantableSkill == null)
{
enchantableSkill = new L2EnchantSkillLearn(skillId, maxLvL);
_enchantSkillTrees.put(skillId, enchantableSkill);
}
if (_enchantSkillGroups.containsKey(group))
{
enchantableSkill.addNewEnchantRoute(route, group);
return _enchantSkillGroups.get(group).getEnchantGroupDetails().size();
}
LOGGER.log(Level.SEVERE, getClass().getSimpleName() + ": Error while loading generating enchant skill id: " + skillId + "; route: " + route + "; missing group: " + group);
return 0;
}
/**
* Gets the skill enchantment for skill.
* @param skill the skill
* @return the skill enchantment for skill
*/
public L2EnchantSkillLearn getSkillEnchantmentForSkill(Skill skill)
{
// there is enchantment for this skill and we have the required level of it
final L2EnchantSkillLearn esl = getSkillEnchantmentBySkillId(skill.getId());
if ((esl != null) && (skill.getLevel() >= esl.getBaseLevel()))
{
return esl;
}
return null;
}
/**
* Gets the skill enchantment by skill id.
* @param skillId the skill id
* @return the skill enchantment by skill id
*/
public L2EnchantSkillLearn getSkillEnchantmentBySkillId(int skillId)
{
return _enchantSkillTrees.get(skillId);
}
/**
* Gets the enchant skill group by id.
* @param id the id
* @return the enchant skill group by id
*/
public L2EnchantSkillGroup getEnchantSkillGroupById(int id)
{
return _enchantSkillGroups.get(id);
}
/**
* Gets the enchant skill sp cost.
* @param skill the skill
* @return the enchant skill sp cost
*/
public int getEnchantSkillSpCost(Skill skill)
{
final L2EnchantSkillLearn enchantSkillLearn = _enchantSkillTrees.get(skill.getId());
if (enchantSkillLearn != null)
{
final EnchantSkillHolder esh = enchantSkillLearn.getEnchantSkillHolder(skill.getLevel());
if (esh != null)
{
return esh.getSpCost();
}
}
return Integer.MAX_VALUE;
}
/**
* Gets the enchant skill Adena cost.
* @param skill the skill
* @return the enchant skill Adena cost
*/
public int getEnchantSkillAdenaCost(Skill skill)
{
final L2EnchantSkillLearn enchantSkillLearn = _enchantSkillTrees.get(skill.getId());
if (enchantSkillLearn != null)
{
final EnchantSkillHolder esh = enchantSkillLearn.getEnchantSkillHolder(skill.getLevel());
if (esh != null)
{
return esh.getAdenaCost();
}
}
return Integer.MAX_VALUE;
}
/**
* Gets the enchant skill rate.
* @param player the player
* @param skill the skill
* @return the enchant skill rate
*/
public byte getEnchantSkillRate(L2PcInstance player, Skill skill)
{
final L2EnchantSkillLearn enchantSkillLearn = _enchantSkillTrees.get(skill.getId());
if (enchantSkillLearn != null)
{
final EnchantSkillHolder esh = enchantSkillLearn.getEnchantSkillHolder(skill.getLevel());
if (esh != null)
{
return esh.getRate(player);
}
}
return 0;
}
/**
* Gets the single instance of EnchantGroupsData.
* @return single instance of EnchantGroupsData
*/
public static EnchantSkillGroupsData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final EnchantSkillGroupsData _instance = new EnchantSkillGroupsData();
}
}

View File

@@ -0,0 +1,130 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.Date;
import java.util.List;
import javolution.util.FastList;
import com.l2jserver.gameserver.script.DateRange;
import com.l2jserver.gameserver.script.EventDrop;
/**
* This class manage drop of Special Events created by GM for a defined period.<br>
* During a Special Event all L2Attackable can drop extra Items.<br>
* Those extra Items are defined in the table <b>allNpcDateDrops</b>.<br>
* Each Special Event has a start and end date to stop to drop extra Items automatically.
*/
public class EventDroplist
{
/**
* The table containing all DataDrop object
*/
private static final List<DateDrop> _allNpcDateDrops = new FastList<>();
public static class DateDrop
{
protected final DateRange _dateRange;
private final EventDrop _eventDrop;
public DateDrop(DateRange dateRange, EventDrop eventDrop)
{
_dateRange = dateRange;
_eventDrop = eventDrop;
}
/**
* @return the _eventDrop
*/
public EventDrop getEventDrop()
{
return _eventDrop;
}
/**
* @return the _dateRange
*/
public DateRange getDateRange()
{
return _dateRange;
}
}
/**
* Create and Init a new DateDrop then add it to the allNpcDateDrops of EventDroplist .
* @param itemIdList The table containing all item identifier of this DateDrop
* @param count The table containing min and max value of this DateDrop
* @param chance The chance to obtain this drop
* @param dateRange The DateRange object to add to this DateDrop
*/
public void addGlobalDrop(int[] itemIdList, int[] count, int chance, DateRange dateRange)
{
_allNpcDateDrops.add(new DateDrop(dateRange, new EventDrop(itemIdList, count[0], count[1], chance)));
}
/**
* @param itemId the item Id for the drop
* @param min the minimum drop count
* @param max the maximum drop count
* @param chance the drop chance
* @param dateRange the event drop rate range
*/
public void addGlobalDrop(int itemId, long min, long max, int chance, DateRange dateRange)
{
_allNpcDateDrops.add(new DateDrop(dateRange, new EventDrop(itemId, min, max, chance)));
}
/**
* Adds an event drop for a given date range.
* @param dateRange the date range.
* @param eventDrop the event drop.
*/
public void addGlobalDrop(DateRange dateRange, EventDrop eventDrop)
{
_allNpcDateDrops.add(new DateDrop(dateRange, eventDrop));
}
/**
* @return all DateDrop of EventDroplist allNpcDateDrops within the date range.
*/
public List<DateDrop> getAllDrops()
{
final List<DateDrop> list = new FastList<>();
final Date currentDate = new Date();
for (DateDrop drop : _allNpcDateDrops)
{
if (drop._dateRange.isWithinRange(currentDate))
{
list.add(drop);
}
}
return list;
}
public static EventDroplist getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final EventDroplist _instance = new EventDroplist();
}
}

View File

@@ -0,0 +1,120 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.HashMap;
import java.util.Map;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
/**
* This class holds the Experience points for each level for players and pets.
* @author mrTJO
*/
public final class ExperienceTable implements DocumentParser
{
private final Map<Integer, Long> _expTable = new HashMap<>();
private byte MAX_LEVEL;
private byte MAX_PET_LEVEL;
/**
* Instantiates a new experience table.
*/
protected ExperienceTable()
{
load();
}
@Override
public void load()
{
_expTable.clear();
parseDatapackFile("data/stats/experience.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _expTable.size() + " levels.");
LOGGER.info(getClass().getSimpleName() + ": Max Player Level is: " + (MAX_LEVEL - 1));
LOGGER.info(getClass().getSimpleName() + ": Max Pet Level is: " + (MAX_PET_LEVEL - 1));
}
@Override
public void parseDocument(Document doc)
{
final Node table = doc.getFirstChild();
final NamedNodeMap tableAttr = table.getAttributes();
MAX_LEVEL = (byte) (Byte.parseByte(tableAttr.getNamedItem("maxLevel").getNodeValue()) + 1);
MAX_PET_LEVEL = (byte) (Byte.parseByte(tableAttr.getNamedItem("maxPetLevel").getNodeValue()) + 1);
NamedNodeMap attrs;
for (Node n = table.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("experience".equals(n.getNodeName()))
{
attrs = n.getAttributes();
_expTable.put(parseInteger(attrs, "level"), parseLong(attrs, "tolevel"));
}
}
}
/**
* Gets the exp for level.
* @param level the level required.
* @return the experience points required to reach the given level.
*/
public long getExpForLevel(int level)
{
return _expTable.get(level);
}
/**
* Gets the max level.
* @return the maximum level acquirable by a player.
*/
public byte getMaxLevel()
{
return MAX_LEVEL;
}
/**
* Gets the max pet level.
* @return the maximum level acquirable by a pet.
*/
public byte getMaxPetLevel()
{
return MAX_PET_LEVEL;
}
/**
* Gets the single instance of ExperienceTable.
* @return single instance of ExperienceTable
*/
public static ExperienceTable getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final ExperienceTable _instance = new ExperienceTable();
}
}

View File

@@ -0,0 +1,174 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.model.StatsSet;
import com.l2jserver.gameserver.model.fishing.L2Fish;
/**
* This class holds the Fish information.
* @author nonom
*/
public final class FishData implements DocumentParser
{
private static final Map<Integer, L2Fish> _fishNormal = new HashMap<>();
private static final Map<Integer, L2Fish> _fishEasy = new HashMap<>();
private static final Map<Integer, L2Fish> _fishHard = new HashMap<>();
/**
* Instantiates a new fish data.
*/
protected FishData()
{
load();
}
@Override
public void load()
{
_fishEasy.clear();
_fishNormal.clear();
_fishHard.clear();
parseDatapackFile("data/stats/fishing/fishes.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + (_fishEasy.size() + _fishNormal.size() + _fishHard.size()) + " Fishes.");
}
@Override
public void parseDocument(Document doc)
{
NamedNodeMap attrs;
Node att;
L2Fish fish;
StatsSet set;
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("fish".equalsIgnoreCase(d.getNodeName()))
{
attrs = d.getAttributes();
set = new StatsSet();
for (int i = 0; i < attrs.getLength(); i++)
{
att = attrs.item(i);
set.set(att.getNodeName(), att.getNodeValue());
}
fish = new L2Fish(set);
switch (fish.getFishGrade())
{
case 0:
{
_fishEasy.put(fish.getFishId(), fish);
break;
}
case 1:
{
_fishNormal.put(fish.getFishId(), fish);
break;
}
case 2:
{
_fishHard.put(fish.getFishId(), fish);
break;
}
}
}
}
}
}
}
/**
* Gets the fish.
* @param level the fish Level
* @param group the fish Group
* @param grade the fish Grade
* @return List of Fish that can be fished
*/
public List<L2Fish> getFish(int level, int group, int grade)
{
final ArrayList<L2Fish> result = new ArrayList<>();
Map<Integer, L2Fish> fish = null;
switch (grade)
{
case 0:
{
fish = _fishEasy;
break;
}
case 1:
{
fish = _fishNormal;
break;
}
case 2:
{
fish = _fishHard;
break;
}
default:
{
LOGGER.warning(getClass().getSimpleName() + ": Unmanaged fish grade!");
return result;
}
}
for (L2Fish f : fish.values())
{
if ((f.getFishLevel() != level) || (f.getFishGroup() != group))
{
continue;
}
result.add(f);
}
if (result.isEmpty())
{
LOGGER.warning(getClass().getSimpleName() + ": Cannot find any fish for level: " + level + " group: " + group + " and grade: " + grade + "!");
}
return result;
}
/**
* Gets the single instance of FishData.
* @return single instance of FishData
*/
public static FishData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final FishData _instance = new FishData();
}
}

View File

@@ -0,0 +1,132 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.HashMap;
import java.util.Map;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.model.StatsSet;
import com.l2jserver.gameserver.model.fishing.L2FishingMonster;
/**
* This class holds the Fishing Monsters information.
* @author nonom
*/
public final class FishingMonstersData implements DocumentParser
{
private static final Map<Integer, L2FishingMonster> _fishingMonstersData = new HashMap<>();
/**
* Instantiates a new fishing monsters data.
*/
protected FishingMonstersData()
{
load();
}
@Override
public void load()
{
_fishingMonstersData.clear();
parseDatapackFile("data/stats/fishing/fishingMonsters.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _fishingMonstersData.size() + " Fishing Monsters.");
}
@Override
public void parseDocument(Document doc)
{
NamedNodeMap attrs;
Node att;
L2FishingMonster fishingMonster;
StatsSet set;
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("fishingMonster".equalsIgnoreCase(d.getNodeName()))
{
attrs = d.getAttributes();
set = new StatsSet();
for (int i = 0; i < attrs.getLength(); i++)
{
att = attrs.item(i);
set.set(att.getNodeName(), att.getNodeValue());
}
fishingMonster = new L2FishingMonster(set);
_fishingMonstersData.put(fishingMonster.getFishingMonsterId(), fishingMonster);
}
}
}
}
}
/**
* Gets the fishing monster.
* @param lvl the fisherman level
* @return a fishing monster given the fisherman level
*/
public L2FishingMonster getFishingMonster(int lvl)
{
for (L2FishingMonster fishingMonster : _fishingMonstersData.values())
{
if ((lvl >= fishingMonster.getUserMinLevel()) && (lvl <= fishingMonster.getUserMaxLevel()))
{
return fishingMonster;
}
}
return null;
}
/**
* Gets the fishing monster by Id.
* @param id the fishing monster Id
* @return the fishing monster by Id
*/
public L2FishingMonster getFishingMonsterById(int id)
{
if (_fishingMonstersData.containsKey(id))
{
return _fishingMonstersData.get(id);
}
return null;
}
/**
* Gets the single instance of FishingMonsterData.
* @return single instance of FishingMonsterData
*/
public static FishingMonstersData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final FishingMonstersData _instance = new FishingMonstersData();
}
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.HashMap;
import java.util.Map;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.model.StatsSet;
import com.l2jserver.gameserver.model.fishing.L2FishingRod;
/**
* This class holds the Fishing Rods information.
* @author nonom
*/
public final class FishingRodsData implements DocumentParser
{
private static final Map<Integer, L2FishingRod> _fishingRods = new HashMap<>();
/**
* Instantiates a new fishing rods data.
*/
protected FishingRodsData()
{
load();
}
@Override
public void load()
{
_fishingRods.clear();
parseDatapackFile("data/stats/fishing/fishingRods.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _fishingRods.size() + " Fishing Rods.");
}
@Override
public void parseDocument(Document doc)
{
NamedNodeMap attrs;
Node att;
L2FishingRod fishingRod;
StatsSet set;
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("fishingRod".equalsIgnoreCase(d.getNodeName()))
{
attrs = d.getAttributes();
set = new StatsSet();
for (int i = 0; i < attrs.getLength(); i++)
{
att = attrs.item(i);
set.set(att.getNodeName(), att.getNodeValue());
}
fishingRod = new L2FishingRod(set);
_fishingRods.put(fishingRod.getFishingRodItemId(), fishingRod);
}
}
}
}
}
/**
* Gets the fishing rod.
* @param itemId the item id
* @return A fishing Rod by Item Id
*/
public L2FishingRod getFishingRod(int itemId)
{
return _fishingRods.get(itemId);
}
/**
* Gets the single instance of FishingRodsData.
* @return single instance of FishingRodsData
*/
public static FishingRodsData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final FishingRodsData _instance = new FishingRodsData();
}
}

View File

@@ -0,0 +1,181 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.model.StatsSet;
import com.l2jserver.gameserver.model.base.ClassId;
import com.l2jserver.gameserver.model.items.L2Henna;
/**
* This class holds the henna related information.<br>
* Cost and required amount to add the henna to the player.<br>
* Cost and retrieved amount for removing the henna from the player.<br>
* Allowed classes to wear each henna.
* @author Zoey76
*/
public final class HennaData implements DocumentParser
{
private static final Map<Integer, L2Henna> _hennaList = new HashMap<>();
/**
* Instantiates a new henna data.
*/
protected HennaData()
{
load();
}
@Override
public void load()
{
_hennaList.clear();
parseDatapackFile("data/stats/hennaList.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _hennaList.size() + " Henna data.");
}
@Override
public void parseDocument(Document doc)
{
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equals(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("henna".equals(d.getNodeName()))
{
parseHenna(d);
}
}
}
}
}
/**
* Parses the henna.
* @param d the d
*/
private void parseHenna(Node d)
{
final StatsSet set = new StatsSet();
final List<ClassId> wearClassIds = new ArrayList<>();
NamedNodeMap attrs = d.getAttributes();
Node attr;
String name;
for (int i = 0; i < attrs.getLength(); i++)
{
attr = attrs.item(i);
set.set(attr.getNodeName(), attr.getNodeValue());
}
for (Node c = d.getFirstChild(); c != null; c = c.getNextSibling())
{
name = c.getNodeName();
attrs = c.getAttributes();
switch (name)
{
case "stats":
{
for (int i = 0; i < attrs.getLength(); i++)
{
attr = attrs.item(i);
set.set(attr.getNodeName(), attr.getNodeValue());
}
break;
}
case "wear":
{
attr = attrs.getNamedItem("count");
set.set("wear_count", attr.getNodeValue());
attr = attrs.getNamedItem("fee");
set.set("wear_fee", attr.getNodeValue());
break;
}
case "cancel":
{
attr = attrs.getNamedItem("count");
set.set("cancel_count", attr.getNodeValue());
attr = attrs.getNamedItem("fee");
set.set("cancel_fee", attr.getNodeValue());
break;
}
case "classId":
{
wearClassIds.add(ClassId.getClassId(Integer.parseInt(c.getTextContent())));
break;
}
}
}
final L2Henna henna = new L2Henna(set);
henna.setWearClassIds(wearClassIds);
_hennaList.put(henna.getDyeId(), henna);
}
/**
* Gets the henna.
* @param id of the dye.
* @return the dye with that id.
*/
public L2Henna getHenna(int id)
{
return _hennaList.get(id);
}
/**
* Gets the henna list.
* @param classId the player's class Id.
* @return the list with all the allowed dyes.
*/
public List<L2Henna> getHennaList(ClassId classId)
{
final List<L2Henna> list = new ArrayList<>();
for (L2Henna henna : _hennaList.values())
{
if (henna.isAllowedClass(classId))
{
list.add(henna);
}
}
return list;
}
/**
* Gets the single instance of HennaData.
* @return single instance of HennaData
*/
public static HennaData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final HennaData _instance = new HennaData();
}
}

View File

@@ -0,0 +1,162 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.Config;
import com.l2jserver.gameserver.GameTimeController;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.model.actor.L2Character;
/**
* This class load, holds and calculates the hit condition bonuses.
* @author Nik
*/
public final class HitConditionBonus implements DocumentParser
{
private int frontBonus = 0;
private int sideBonus = 0;
private int backBonus = 0;
private int highBonus = 0;
private int lowBonus = 0;
private int darkBonus = 0;
private int rainBonus = 0;
/**
* Instantiates a new hit condition bonus.
*/
protected HitConditionBonus()
{
load();
}
@Override
public void load()
{
parseDatapackFile("data/stats/hitConditionBonus.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded Hit Condition bonuses.");
if (Config.DEBUG)
{
LOGGER.info(getClass().getSimpleName() + ": Front bonus: " + frontBonus);
LOGGER.info(getClass().getSimpleName() + ": Side bonus: " + sideBonus);
LOGGER.info(getClass().getSimpleName() + ": Back bonus: " + backBonus);
LOGGER.info(getClass().getSimpleName() + ": High bonus: " + highBonus);
LOGGER.info(getClass().getSimpleName() + ": Low bonus: " + lowBonus);
LOGGER.info(getClass().getSimpleName() + ": Dark bonus: " + darkBonus);
LOGGER.info(getClass().getSimpleName() + ": Rain bonus: " + rainBonus);
}
}
@Override
public void parseDocument(Document doc)
{
final Node n = doc.getFirstChild();
NamedNodeMap attrs;
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
attrs = d.getAttributes();
switch (d.getNodeName())
{
case "front":
frontBonus = parseInteger(attrs, "val");
break;
case "side":
sideBonus = parseInteger(attrs, "val");
break;
case "back":
backBonus = parseInteger(attrs, "val");
break;
case "high":
highBonus = parseInteger(attrs, "val");
break;
case "low":
lowBonus = parseInteger(attrs, "val");
break;
case "dark":
darkBonus = parseInteger(attrs, "val");
break;
case "rain":
rainBonus = parseInteger(attrs, "val");
break;
}
}
}
/**
* Gets the condition bonus.
* @param attacker the attacking character.
* @param target the attacked character.
* @return the bonus of the attacker against the target.
*/
public double getConditionBonus(L2Character attacker, L2Character target)
{
double mod = 100;
// Get high or low bonus
if ((attacker.getZ() - target.getZ()) > 50)
{
mod += highBonus;
}
else if ((attacker.getZ() - target.getZ()) < -50)
{
mod += lowBonus;
}
// Get weather bonus
if (GameTimeController.getInstance().isNight())
{
mod += darkBonus;
// else if () No rain support yet.
// chance += hitConditionBonus.rainBonus;
}
// Get side bonus
if (attacker.isBehindTarget())
{
mod += backBonus;
}
else if (attacker.isInFrontOfTarget())
{
mod += frontBonus;
}
else
{
mod += sideBonus;
}
// If (mod / 100) is less than 0, return 0, because we can't lower more than 100%.
return Math.max(mod / 100, 0);
}
/**
* Gets the single instance of HitConditionBonus.
* @return single instance of HitConditionBonus
*/
public static HitConditionBonus getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final HitConditionBonus _instance = new HitConditionBonus();
}
}

View File

@@ -0,0 +1,140 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.Config;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.model.StatsSet;
import com.l2jserver.gameserver.model.base.ClassId;
import com.l2jserver.gameserver.model.items.PcItemTemplate;
/**
* This class holds the Initial Equipment information.<br>
* What items get each newly created character and if this item is equipped upon creation (<b>Requires the item to be equippable</b>).
* @author Zoey76
*/
public final class InitialEquipmentData implements DocumentParser
{
private static final String NORMAL = "data/stats/initialEquipment.xml";
private static final String EVENT = "data/stats/initialEquipmentEvent.xml";
private final Map<ClassId, List<PcItemTemplate>> _initialEquipmentList = new HashMap<>();
/**
* Instantiates a new initial equipment data.
*/
protected InitialEquipmentData()
{
load();
}
@Override
public void load()
{
_initialEquipmentList.clear();
parseDatapackFile(Config.INITIAL_EQUIPMENT_EVENT ? EVENT : NORMAL);
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _initialEquipmentList.size() + " Initial Equipment data.");
}
@Override
public void parseDocument(Document doc)
{
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("equipment".equalsIgnoreCase(d.getNodeName()))
{
parseEquipment(d);
}
}
}
}
}
/**
* Parses the equipment.
* @param d parse an initial equipment and add it to {@link #_initialEquipmentList}
*/
private void parseEquipment(Node d)
{
NamedNodeMap attrs = d.getAttributes();
final ClassId classId = ClassId.getClassId(Integer.parseInt(attrs.getNamedItem("classId").getNodeValue()));
final List<PcItemTemplate> equipList = new ArrayList<>();
for (Node c = d.getFirstChild(); c != null; c = c.getNextSibling())
{
if ("item".equalsIgnoreCase(c.getNodeName()))
{
final StatsSet set = new StatsSet();
attrs = c.getAttributes();
for (int i = 0; i < attrs.getLength(); i++)
{
Node attr = attrs.item(i);
set.set(attr.getNodeName(), attr.getNodeValue());
}
equipList.add(new PcItemTemplate(set));
}
}
_initialEquipmentList.put(classId, equipList);
}
/**
* Gets the equipment list.
* @param cId the class Id for the required initial equipment.
* @return the initial equipment for the given class Id.
*/
public List<PcItemTemplate> getEquipmentList(ClassId cId)
{
return _initialEquipmentList.get(cId);
}
/**
* Gets the equipment list.
* @param cId the class Id for the required initial equipment.
* @return the initial equipment for the given class Id.
*/
public List<PcItemTemplate> getEquipmentList(int cId)
{
return _initialEquipmentList.get(ClassId.getClassId(cId));
}
/**
* Gets the single instance of InitialEquipmentData.
* @return single instance of InitialEquipmentData
*/
public static InitialEquipmentData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final InitialEquipmentData _instance = new InitialEquipmentData();
}
}

View File

@@ -0,0 +1,369 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.enums.MacroType;
import com.l2jserver.gameserver.enums.ShortcutType;
import com.l2jserver.gameserver.model.Macro;
import com.l2jserver.gameserver.model.MacroCmd;
import com.l2jserver.gameserver.model.Shortcut;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.base.ClassId;
import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
import com.l2jserver.gameserver.network.serverpackets.ShortCutRegister;
/**
* This class holds the Initial Shortcuts information.<br>
* What shortcuts get each newly created character.
* @author Zoey76
*/
public final class InitialShortcutData implements DocumentParser
{
private final Map<ClassId, List<Shortcut>> _initialShortcutData = new HashMap<>();
private final List<Shortcut> _initialGlobalShortcutList = new ArrayList<>();
private final Map<Integer, Macro> _macroPresets = new HashMap<>();
/**
* Instantiates a new initial shortcuts data.
*/
protected InitialShortcutData()
{
load();
}
@Override
public void load()
{
_initialShortcutData.clear();
_initialGlobalShortcutList.clear();
parseDatapackFile("data/stats/initialShortcuts.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _initialGlobalShortcutList.size() + " Initial Global Shortcuts data.");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _initialShortcutData.size() + " Initial Shortcuts data.");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _macroPresets.size() + " Macros presets.");
}
@Override
public void parseDocument(Document doc)
{
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equals(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
switch (d.getNodeName())
{
case "shortcuts":
{
parseShortcuts(d);
break;
}
case "macros":
{
parseMacros(d);
break;
}
}
}
}
}
}
/**
* Parses a shortcut.
* @param d the node
*/
private void parseShortcuts(Node d)
{
NamedNodeMap attrs = d.getAttributes();
final Node classIdNode = attrs.getNamedItem("classId");
final List<Shortcut> list = new ArrayList<>();
for (Node c = d.getFirstChild(); c != null; c = c.getNextSibling())
{
if ("page".equals(c.getNodeName()))
{
attrs = c.getAttributes();
final int pageId = parseInteger(attrs, "pageId");
for (Node b = c.getFirstChild(); b != null; b = b.getNextSibling())
{
if ("slot".equals(b.getNodeName()))
{
list.add(createShortcut(pageId, b));
}
}
}
}
if (classIdNode != null)
{
_initialShortcutData.put(ClassId.getClassId(Integer.parseInt(classIdNode.getNodeValue())), list);
}
else
{
_initialGlobalShortcutList.addAll(list);
}
}
/**
* Parses a macro.
* @param d the node
*/
private void parseMacros(Node d)
{
for (Node c = d.getFirstChild(); c != null; c = c.getNextSibling())
{
if ("macro".equals(c.getNodeName()))
{
NamedNodeMap attrs = c.getAttributes();
if (!parseBoolean(attrs, "enabled", true))
{
continue;
}
final int macroId = parseInteger(attrs, "macroId");
final int icon = parseInteger(attrs, "icon");
final String name = parseString(attrs, "name");
final String description = parseString(attrs, "description");
final String acronym = parseString(attrs, "acronym");
final List<MacroCmd> commands = new ArrayList<>(1);
int entry = 0;
for (Node b = c.getFirstChild(); b != null; b = b.getNextSibling())
{
if ("command".equals(b.getNodeName()))
{
attrs = b.getAttributes();
final MacroType type = parseEnum(attrs, MacroType.class, "type");
int d1 = 0;
int d2 = 0;
final String cmd = b.getTextContent();
switch (type)
{
case SKILL:
{
d1 = parseInteger(attrs, "skillId"); // Skill ID
d2 = parseInteger(attrs, "skillLvl", 0); // Skill level
break;
}
case ACTION:
{
// Not handled by client.
d1 = parseInteger(attrs, "actionId");
break;
}
case TEXT:
{
// Doesn't have numeric parameters.
break;
}
case SHORTCUT:
{
d1 = parseInteger(attrs, "page"); // Page
d2 = parseInteger(attrs, "slot", 0); // Slot
break;
}
case ITEM:
{
// Not handled by client.
d1 = parseInteger(attrs, "itemId");
break;
}
case DELAY:
{
d1 = parseInteger(attrs, "delay"); // Delay in seconds
break;
}
}
commands.add(new MacroCmd(entry++, type, d1, d2, cmd));
}
}
_macroPresets.put(macroId, new Macro(macroId, icon, name, description, acronym, commands));
}
}
}
/**
* Parses a node an create a shortcut from it.
* @param pageId the page ID
* @param b the node to parse
* @return the new shortcut
*/
private Shortcut createShortcut(int pageId, Node b)
{
final NamedNodeMap attrs = b.getAttributes();
final int slotId = parseInteger(attrs, "slotId");
final ShortcutType shortcutType = parseEnum(attrs, ShortcutType.class, "shortcutType");
final int shortcutId = parseInteger(attrs, "shortcutId");
final int shortcutLevel = parseInteger(attrs, "shortcutLevel", 0);
final int characterType = parseInteger(attrs, "characterType", 0);
return new Shortcut(slotId, pageId, shortcutType, shortcutId, shortcutLevel, characterType);
}
/**
* Gets the shortcut list.
* @param cId the class ID for the shortcut list
* @return the shortcut list for the give class ID
*/
public List<Shortcut> getShortcutList(ClassId cId)
{
return _initialShortcutData.get(cId);
}
/**
* Gets the shortcut list.
* @param cId the class ID for the shortcut list
* @return the shortcut list for the give class ID
*/
public List<Shortcut> getShortcutList(int cId)
{
return _initialShortcutData.get(ClassId.getClassId(cId));
}
/**
* Gets the global shortcut list.
* @return the global shortcut list
*/
public List<Shortcut> getGlobalMacroList()
{
return _initialGlobalShortcutList;
}
/**
* Register all the available shortcuts for the given player.
* @param player the player
*/
public void registerAllShortcuts(L2PcInstance player)
{
if (player == null)
{
return;
}
// Register global shortcuts.
for (Shortcut shortcut : _initialGlobalShortcutList)
{
int shortcutId = shortcut.getId();
switch (shortcut.getType())
{
case ITEM:
{
final L2ItemInstance item = player.getInventory().getItemByItemId(shortcutId);
if (item == null)
{
continue;
}
shortcutId = item.getObjectId();
break;
}
case SKILL:
{
if (!player.getSkills().containsKey(shortcutId))
{
continue;
}
break;
}
case MACRO:
{
final Macro macro = _macroPresets.get(shortcutId);
if (macro == null)
{
continue;
}
player.registerMacro(macro);
break;
}
}
// Register shortcut
final Shortcut newShortcut = new Shortcut(shortcut.getSlot(), shortcut.getPage(), shortcut.getType(), shortcutId, shortcut.getLevel(), shortcut.getCharacterType());
player.sendPacket(new ShortCutRegister(newShortcut));
player.registerShortCut(newShortcut);
}
// Register class specific shortcuts.
if (_initialShortcutData.containsKey(player.getClassId()))
{
for (Shortcut shortcut : _initialShortcutData.get(player.getClassId()))
{
int shortcutId = shortcut.getId();
switch (shortcut.getType())
{
case ITEM:
{
final L2ItemInstance item = player.getInventory().getItemByItemId(shortcutId);
if (item == null)
{
continue;
}
shortcutId = item.getObjectId();
break;
}
case SKILL:
{
if (!player.getSkills().containsKey(shortcut.getId()))
{
continue;
}
break;
}
case MACRO:
{
final Macro macro = _macroPresets.get(shortcutId);
if (macro == null)
{
continue;
}
player.registerMacro(macro);
break;
}
}
// Register shortcut
final Shortcut newShortcut = new Shortcut(shortcut.getSlot(), shortcut.getPage(), shortcut.getType(), shortcutId, shortcut.getLevel(), shortcut.getCharacterType());
player.sendPacket(new ShortCutRegister(newShortcut));
player.registerShortCut(newShortcut);
}
}
}
/**
* Gets the single instance of InitialEquipmentData.
* @return single instance of InitialEquipmentData
*/
public static InitialShortcutData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final InitialShortcutData _instance = new InitialShortcutData();
}
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.model.CrystalizationData;
import com.l2jserver.gameserver.model.holders.ItemChanceHolder;
/**
* @author UnAfraid
*/
public class ItemCrystalizationData implements DocumentParser
{
private final Map<Integer, CrystalizationData> _items = new HashMap<>();
protected ItemCrystalizationData()
{
load();
}
@Override
public void load()
{
parseDatapackFile("data/CrystalizableItems.xml");
LOGGER.log(Level.INFO, getClass().getSimpleName() + ": Loaded: " + _items.size() + " Items");
}
@Override
public void parseDocument(Document doc)
{
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("crystalizable_item".equalsIgnoreCase(d.getNodeName()))
{
final int id = parseInteger(d.getAttributes(), "id");
final CrystalizationData data = new CrystalizationData(id);
for (Node c = d.getFirstChild(); c != null; c = c.getNextSibling())
{
if ("item".equalsIgnoreCase(c.getNodeName()))
{
NamedNodeMap attrs = c.getAttributes();
final int itemId = parseInteger(attrs, "id");
final long itemCount = parseLong(attrs, "count");
final double itemChance = parseDouble(attrs, "chance");
data.addItem(new ItemChanceHolder(itemId, itemChance, itemCount));
}
}
_items.put(id, data);
}
}
}
}
}
public CrystalizationData getCrystalization(int itemId)
{
return _items.get(itemId);
}
/**
* Gets the single instance of ItemCrystalizationData.
* @return single instance of ItemCrystalizationData
*/
public static final ItemCrystalizationData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final ItemCrystalizationData _instance = new ItemCrystalizationData();
}
}

View File

@@ -0,0 +1,441 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import static com.l2jserver.gameserver.model.itemcontainer.Inventory.ADENA_ID;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import javolution.util.FastMap;
import com.l2jserver.Config;
import com.l2jserver.L2DatabaseFactory;
import com.l2jserver.gameserver.ThreadPoolManager;
import com.l2jserver.gameserver.engines.DocumentEngine;
import com.l2jserver.gameserver.enums.ItemLocation;
import com.l2jserver.gameserver.idfactory.IdFactory;
import com.l2jserver.gameserver.model.L2Object;
import com.l2jserver.gameserver.model.L2World;
import com.l2jserver.gameserver.model.actor.L2Attackable;
import com.l2jserver.gameserver.model.actor.instance.L2EventMonsterInstance;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.events.EventDispatcher;
import com.l2jserver.gameserver.model.events.impl.item.OnItemCreate;
import com.l2jserver.gameserver.model.items.L2Armor;
import com.l2jserver.gameserver.model.items.L2EtcItem;
import com.l2jserver.gameserver.model.items.L2Item;
import com.l2jserver.gameserver.model.items.L2Weapon;
import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
import com.l2jserver.gameserver.util.GMAudit;
/**
* This class serves as a container for all item templates in the game.
*/
public class ItemTable
{
private static Logger LOGGER = Logger.getLogger(ItemTable.class.getName());
private static Logger LOGGER_ITEMS = Logger.getLogger("item");
public static final Map<String, Integer> _slots = new FastMap<>();
private L2Item[] _allTemplates;
private final Map<Integer, L2EtcItem> _etcItems;
private final Map<Integer, L2Armor> _armors;
private final Map<Integer, L2Weapon> _weapons;
static
{
_slots.put("shirt", L2Item.SLOT_UNDERWEAR);
_slots.put("lbracelet", L2Item.SLOT_L_BRACELET);
_slots.put("rbracelet", L2Item.SLOT_R_BRACELET);
_slots.put("talisman", L2Item.SLOT_DECO);
_slots.put("chest", L2Item.SLOT_CHEST);
_slots.put("fullarmor", L2Item.SLOT_FULL_ARMOR);
_slots.put("head", L2Item.SLOT_HEAD);
_slots.put("hair", L2Item.SLOT_HAIR);
_slots.put("hairall", L2Item.SLOT_HAIRALL);
_slots.put("underwear", L2Item.SLOT_UNDERWEAR);
_slots.put("back", L2Item.SLOT_BACK);
_slots.put("neck", L2Item.SLOT_NECK);
_slots.put("legs", L2Item.SLOT_LEGS);
_slots.put("feet", L2Item.SLOT_FEET);
_slots.put("gloves", L2Item.SLOT_GLOVES);
_slots.put("chest,legs", L2Item.SLOT_CHEST | L2Item.SLOT_LEGS);
_slots.put("belt", L2Item.SLOT_BELT);
_slots.put("rhand", L2Item.SLOT_R_HAND);
_slots.put("lhand", L2Item.SLOT_L_HAND);
_slots.put("lrhand", L2Item.SLOT_LR_HAND);
_slots.put("rear;lear", L2Item.SLOT_R_EAR | L2Item.SLOT_L_EAR);
_slots.put("rfinger;lfinger", L2Item.SLOT_R_FINGER | L2Item.SLOT_L_FINGER);
_slots.put("wolf", L2Item.SLOT_WOLF);
_slots.put("greatwolf", L2Item.SLOT_GREATWOLF);
_slots.put("hatchling", L2Item.SLOT_HATCHLING);
_slots.put("strider", L2Item.SLOT_STRIDER);
_slots.put("babypet", L2Item.SLOT_BABYPET);
_slots.put("none", L2Item.SLOT_NONE);
// retail compatibility
_slots.put("onepiece", L2Item.SLOT_FULL_ARMOR);
_slots.put("hair2", L2Item.SLOT_HAIR2);
_slots.put("dhair", L2Item.SLOT_HAIRALL);
_slots.put("alldress", L2Item.SLOT_ALLDRESS);
_slots.put("deco1", L2Item.SLOT_DECO);
_slots.put("waist", L2Item.SLOT_BELT);
}
/**
* @return a reference to this ItemTable object
*/
public static ItemTable getInstance()
{
return SingletonHolder._instance;
}
protected ItemTable()
{
_etcItems = new FastMap<>();
_armors = new FastMap<>();
_weapons = new FastMap<>();
load();
}
private void load()
{
int highest = 0;
_armors.clear();
_etcItems.clear();
_weapons.clear();
for (L2Item item : DocumentEngine.getInstance().loadItems())
{
if (highest < item.getId())
{
highest = item.getId();
}
if (item instanceof L2EtcItem)
{
_etcItems.put(item.getId(), (L2EtcItem) item);
}
else if (item instanceof L2Armor)
{
_armors.put(item.getId(), (L2Armor) item);
}
else
{
_weapons.put(item.getId(), (L2Weapon) item);
}
}
buildFastLookupTable(highest);
LOGGER.log(Level.INFO, getClass().getSimpleName() + ": Loaded: " + _etcItems.size() + " Etc Items");
LOGGER.log(Level.INFO, getClass().getSimpleName() + ": Loaded: " + _armors.size() + " Armor Items");
LOGGER.log(Level.INFO, getClass().getSimpleName() + ": Loaded: " + _weapons.size() + " Weapon Items");
LOGGER.log(Level.INFO, getClass().getSimpleName() + ": Loaded: " + (_etcItems.size() + _armors.size() + _weapons.size()) + " Items in total.");
}
/**
* Builds a variable in which all items are putting in in function of their ID.
* @param size
*/
private void buildFastLookupTable(int size)
{
// Create a FastLookUp Table called _allTemplates of size : value of the highest item ID
LOGGER.info(getClass().getSimpleName() + ": Highest item id used:" + size);
_allTemplates = new L2Item[size + 1];
// Insert armor item in Fast Look Up Table
for (L2Armor item : _armors.values())
{
_allTemplates[item.getId()] = item;
}
// Insert weapon item in Fast Look Up Table
for (L2Weapon item : _weapons.values())
{
_allTemplates[item.getId()] = item;
}
// Insert etcItem item in Fast Look Up Table
for (L2EtcItem item : _etcItems.values())
{
_allTemplates[item.getId()] = item;
}
}
/**
* Returns the item corresponding to the item ID
* @param id : int designating the item
* @return L2Item
*/
public L2Item getTemplate(int id)
{
if ((id >= _allTemplates.length) || (id < 0))
{
return null;
}
return _allTemplates[id];
}
/**
* Create the L2ItemInstance corresponding to the Item Identifier and quantitiy add logs the activity. <B><U> Actions</U> :</B> <li>Create and Init the L2ItemInstance corresponding to the Item Identifier and quantity</li> <li>Add the L2ItemInstance object to _allObjects of L2world</li> <li>Logs
* Item creation according to log settings</li>
* @param process : String Identifier of process triggering this action
* @param itemId : int Item Identifier of the item to be created
* @param count : int Quantity of items to be created for stackable items
* @param actor : L2PcInstance Player requesting the item creation
* @param reference : Object Object referencing current action like NPC selling item or previous item in transformation
* @return L2ItemInstance corresponding to the new item
*/
public L2ItemInstance createItem(String process, int itemId, long count, L2PcInstance actor, Object reference)
{
// Create and Init the L2ItemInstance corresponding to the Item Identifier
L2ItemInstance item = new L2ItemInstance(IdFactory.getInstance().getNextId(), itemId);
if (process.equalsIgnoreCase("loot"))
{
ScheduledFuture<?> itemLootShedule;
if ((reference instanceof L2Attackable) && ((L2Attackable) reference).isRaid()) // loot privilege for raids
{
L2Attackable raid = (L2Attackable) reference;
// if in CommandChannel and was killing a World/RaidBoss
if ((raid.getFirstCommandChannelAttacked() != null) && !Config.AUTO_LOOT_RAIDS)
{
item.setOwnerId(raid.getFirstCommandChannelAttacked().getLeaderObjectId());
itemLootShedule = ThreadPoolManager.getInstance().scheduleGeneral(new ResetOwner(item), Config.LOOT_RAIDS_PRIVILEGE_INTERVAL);
item.setItemLootShedule(itemLootShedule);
}
}
else if (!Config.AUTO_LOOT || ((reference instanceof L2EventMonsterInstance) && ((L2EventMonsterInstance) reference).eventDropOnGround()))
{
item.setOwnerId(actor.getObjectId());
itemLootShedule = ThreadPoolManager.getInstance().scheduleGeneral(new ResetOwner(item), 15000);
item.setItemLootShedule(itemLootShedule);
}
}
if (Config.DEBUG)
{
LOGGER.fine(getClass().getSimpleName() + ": Item created oid:" + item.getObjectId() + " itemid:" + itemId);
}
// Add the L2ItemInstance object to _allObjects of L2world
L2World.getInstance().storeObject(item);
// Set Item parameters
if (item.isStackable() && (count > 1))
{
item.setCount(count);
}
if (Config.LOG_ITEMS && !process.equals("Reset"))
{
if (!Config.LOG_ITEMS_SMALL_LOG || (Config.LOG_ITEMS_SMALL_LOG && (item.isEquipable() || (item.getId() == ADENA_ID))))
{
LogRecord record = new LogRecord(Level.INFO, "CREATE:" + process);
record.setLoggerName("item");
record.setParameters(new Object[]
{
item,
actor,
reference
});
LOGGER_ITEMS.log(record);
}
}
if (actor != null)
{
if (actor.isGM())
{
String referenceName = "no-reference";
if (reference instanceof L2Object)
{
referenceName = (((L2Object) reference).getName() != null ? ((L2Object) reference).getName() : "no-name");
}
else if (reference instanceof String)
{
referenceName = (String) reference;
}
String targetName = (actor.getTarget() != null ? actor.getTarget().getName() : "no-target");
if (Config.GMAUDIT)
{
GMAudit.auditGMAction(actor.getName() + " [" + actor.getObjectId() + "]", process + "(id: " + itemId + " count: " + count + " name: " + item.getItemName() + " objId: " + item.getObjectId() + ")", targetName, "L2Object referencing this action is: " + referenceName);
}
}
}
// Notify to scripts
EventDispatcher.getInstance().notifyEventAsync(new OnItemCreate(process, item, actor, reference), item.getItem());
return item;
}
public L2ItemInstance createItem(String process, int itemId, int count, L2PcInstance actor)
{
return createItem(process, itemId, count, actor, null);
}
/**
* Returns a dummy item.<br>
* <U><I>Concept :</I></U><BR>
* Dummy item is created by setting the ID of the object in the world at null value
* @param itemId : int designating the item
* @return L2ItemInstance designating the dummy item created
*/
public L2ItemInstance createDummyItem(int itemId)
{
L2Item item = getTemplate(itemId);
if (item == null)
{
return null;
}
L2ItemInstance temp = new L2ItemInstance(0, item);
return temp;
}
/**
* Destroys the L2ItemInstance.<br>
* <B><U> Actions</U> :</B>
* <ul>
* <li>Sets L2ItemInstance parameters to be unusable</li>
* <li>Removes the L2ItemInstance object to _allObjects of L2world</li>
* <li>Logs Item deletion according to log settings</li>
* </ul>
* @param process a string identifier of process triggering this action.
* @param item the item instance to be destroyed.
* @param actor the player requesting the item destroy.
* @param reference the object referencing current action like NPC selling item or previous item in transformation.
*/
public void destroyItem(String process, L2ItemInstance item, L2PcInstance actor, Object reference)
{
synchronized (item)
{
long old = item.getCount();
item.setCount(0);
item.setOwnerId(0);
item.setItemLocation(ItemLocation.VOID);
item.setLastChange(L2ItemInstance.REMOVED);
L2World.getInstance().removeObject(item);
IdFactory.getInstance().releaseId(item.getObjectId());
if (Config.LOG_ITEMS)
{
if (!Config.LOG_ITEMS_SMALL_LOG || (Config.LOG_ITEMS_SMALL_LOG && (item.isEquipable() || (item.getId() == ADENA_ID))))
{
LogRecord record = new LogRecord(Level.INFO, "DELETE:" + process);
record.setLoggerName("item");
record.setParameters(new Object[]
{
item,
"PrevCount(" + old + ")",
actor,
reference
});
LOGGER_ITEMS.log(record);
}
}
if (actor != null)
{
if (actor.isGM())
{
String referenceName = "no-reference";
if (reference instanceof L2Object)
{
referenceName = (((L2Object) reference).getName() != null ? ((L2Object) reference).getName() : "no-name");
}
else if (reference instanceof String)
{
referenceName = (String) reference;
}
String targetName = (actor.getTarget() != null ? actor.getTarget().getName() : "no-target");
if (Config.GMAUDIT)
{
GMAudit.auditGMAction(actor.getName() + " [" + actor.getObjectId() + "]", process + "(id: " + item.getId() + " count: " + item.getCount() + " itemObjId: " + item.getObjectId() + ")", targetName, "L2Object referencing this action is: " + referenceName);
}
}
}
// if it's a pet control item, delete the pet as well
if (item.getItem().isPetItem())
{
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement statement = con.prepareStatement("DELETE FROM pets WHERE item_obj_id=?"))
{
// Delete the pet in db
statement.setInt(1, item.getObjectId());
statement.execute();
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "could not delete pet objectid:", e);
}
}
}
}
public void reload()
{
load();
EnchantItemHPBonusData.getInstance().load();
}
protected static class ResetOwner implements Runnable
{
L2ItemInstance _item;
public ResetOwner(L2ItemInstance item)
{
_item = item;
}
@Override
public void run()
{
_item.setOwnerId(0);
_item.setItemLootShedule(null);
}
}
public Set<Integer> getAllArmorsId()
{
return _armors.keySet();
}
public Set<Integer> getAllWeaponsId()
{
return _weapons.keySet();
}
public int getArraySize()
{
return _allTemplates.length;
}
private static class SingletonHolder
{
protected static final ItemTable _instance = new ItemTable();
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
/**
* @author UnAfraid
*/
public class KarmaData implements DocumentParser
{
private final Map<Integer, Double> _karmaTable = new HashMap<>();
public KarmaData()
{
load();
}
@Override
public synchronized void load()
{
_karmaTable.clear();
parseDatapackFile("data/stats/chars/pcKarmaIncrease.xml");
LOGGER.log(Level.INFO, getClass().getSimpleName() + ": Loaded " + _karmaTable.size() + " karma modifiers.");
}
@Override
public void parseDocument(Document doc)
{
NamedNodeMap attrs;
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("pcKarmaIncrease".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("increase".equalsIgnoreCase(d.getNodeName()))
{
attrs = d.getAttributes();
_karmaTable.put(parseInteger(attrs, "lvl"), parseDouble(attrs, "val"));
}
}
}
}
}
/**
* @param level
* @return {@code double} modifier used to calculate karma lost upon death.
*/
public double getMultiplier(int level)
{
return _karmaTable.get(level);
}
/**
* Gets the single instance of KarmaData.
* @return single instance of KarmaData
*/
public static KarmaData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final KarmaData _instance = new KarmaData();
}
}

View File

@@ -0,0 +1,292 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javolution.util.FastMap;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import com.l2jserver.Config;
import com.l2jserver.gameserver.InstanceListManager;
import com.l2jserver.gameserver.instancemanager.CastleManager;
import com.l2jserver.gameserver.model.actor.instance.L2MerchantInstance;
import com.l2jserver.gameserver.model.entity.Castle;
/**
* @author KenM
*/
public class MerchantPriceConfigTable implements InstanceListManager
{
private static Logger LOGGER = Logger.getLogger(MerchantPriceConfigTable.class.getName());
public static MerchantPriceConfigTable getInstance()
{
return SingletonHolder._instance;
}
private static final String MPCS_FILE = "MerchantPriceConfig.xml";
private final Map<Integer, MerchantPriceConfig> _mpcs = new FastMap<>();
private MerchantPriceConfig _defaultMpc;
public MerchantPriceConfig getMerchantPriceConfig(L2MerchantInstance npc)
{
for (MerchantPriceConfig mpc : _mpcs.values())
{
if ((npc.getWorldRegion() != null) && npc.getWorldRegion().containsZone(mpc.getZoneId()))
{
return mpc;
}
}
return _defaultMpc;
}
public MerchantPriceConfig getMerchantPriceConfig(int id)
{
return _mpcs.get(id);
}
public void loadXML() throws SAXException, IOException, ParserConfigurationException
{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
factory.setIgnoringComments(true);
File file = new File(Config.DATAPACK_ROOT + "/data/" + MPCS_FILE);
if (file.exists())
{
int defaultPriceConfigId;
Document doc = factory.newDocumentBuilder().parse(file);
Node n = doc.getDocumentElement();
Node dpcNode = n.getAttributes().getNamedItem("defaultPriceConfig");
if (dpcNode == null)
{
throw new IllegalStateException("merchantPriceConfig must define an 'defaultPriceConfig'");
}
defaultPriceConfigId = Integer.parseInt(dpcNode.getNodeValue());
MerchantPriceConfig mpc;
for (n = n.getFirstChild(); n != null; n = n.getNextSibling())
{
mpc = parseMerchantPriceConfig(n);
if (mpc != null)
{
_mpcs.put(mpc.getId(), mpc);
}
}
MerchantPriceConfig defaultMpc = this.getMerchantPriceConfig(defaultPriceConfigId);
if (defaultMpc == null)
{
throw new IllegalStateException("'defaultPriceConfig' points to an non-loaded priceConfig");
}
_defaultMpc = defaultMpc;
}
}
private MerchantPriceConfig parseMerchantPriceConfig(Node n)
{
if (n.getNodeName().equals("priceConfig"))
{
final int id;
final int baseTax;
int castleId = -1;
int zoneId = -1;
final String name;
Node node = n.getAttributes().getNamedItem("id");
if (node == null)
{
throw new IllegalStateException("Must define the priceConfig 'id'");
}
id = Integer.parseInt(node.getNodeValue());
node = n.getAttributes().getNamedItem("name");
if (node == null)
{
throw new IllegalStateException("Must define the priceConfig 'name'");
}
name = node.getNodeValue();
node = n.getAttributes().getNamedItem("baseTax");
if (node == null)
{
throw new IllegalStateException("Must define the priceConfig 'baseTax'");
}
baseTax = Integer.parseInt(node.getNodeValue());
node = n.getAttributes().getNamedItem("castleId");
if (node != null)
{
castleId = Integer.parseInt(node.getNodeValue());
}
node = n.getAttributes().getNamedItem("zoneId");
if (node != null)
{
zoneId = Integer.parseInt(node.getNodeValue());
}
return new MerchantPriceConfig(id, name, baseTax, castleId, zoneId);
}
return null;
}
@Override
public void loadInstances()
{
try
{
loadXML();
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _mpcs.size() + " merchant price configs.");
}
catch (Exception e)
{
LOGGER.log(Level.SEVERE, getClass().getSimpleName() + ": Failed loading MerchantPriceConfigTable. Reason: " + e.getMessage(), e);
}
}
@Override
public void updateReferences()
{
for (final MerchantPriceConfig mpc : _mpcs.values())
{
mpc.updateReferences();
}
}
@Override
public void activateInstances()
{
}
/**
* @author KenM
*/
public static final class MerchantPriceConfig
{
private final int _id;
private final String _name;
private final int _baseTax;
private final int _castleId;
private Castle _castle;
private final int _zoneId;
public MerchantPriceConfig(final int id, final String name, final int baseTax, final int castleId, final int zoneId)
{
_id = id;
_name = name;
_baseTax = baseTax;
_castleId = castleId;
_zoneId = zoneId;
}
/**
* @return Returns the id.
*/
public int getId()
{
return _id;
}
/**
* @return Returns the name.
*/
public String getName()
{
return _name;
}
/**
* @return Returns the baseTax.
*/
public int getBaseTax()
{
return _baseTax;
}
/**
* @return Returns the baseTax / 100.0.
*/
public double getBaseTaxRate()
{
return _baseTax / 100.0;
}
/**
* @return Returns the castle.
*/
public Castle getCastle()
{
return _castle;
}
/**
* @return Returns the zoneId.
*/
public int getZoneId()
{
return _zoneId;
}
public boolean hasCastle()
{
return getCastle() != null;
}
public double getCastleTaxRate()
{
return hasCastle() ? getCastle().getTaxRate() : 0.0;
}
public int getTotalTax()
{
return hasCastle() ? (getCastle().getTaxPercent() + getBaseTax()) : getBaseTax();
}
public double getTotalTaxRate()
{
return getTotalTax() / 100.0;
}
public void updateReferences()
{
if (_castleId > 0)
{
_castle = CastleManager.getInstance().getCastleById(_castleId);
}
}
}
private static class SingletonHolder
{
protected static final MerchantPriceConfigTable _instance = new MerchantPriceConfigTable();
}
}

View File

@@ -0,0 +1,395 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.io.File;
import java.io.FileFilter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.logging.Level;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.Config;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.model.StatsSet;
import com.l2jserver.gameserver.model.actor.L2Npc;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.multisell.Entry;
import com.l2jserver.gameserver.model.multisell.Ingredient;
import com.l2jserver.gameserver.model.multisell.ListContainer;
import com.l2jserver.gameserver.model.multisell.PreparedListContainer;
import com.l2jserver.gameserver.network.SystemMessageId;
import com.l2jserver.gameserver.network.serverpackets.MultiSellList;
import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
import com.l2jserver.gameserver.network.serverpackets.UserInfo;
import com.l2jserver.gameserver.util.Util;
import com.l2jserver.util.file.filter.NumericNameFilter;
public final class MultisellData implements DocumentParser
{
public static final int PAGE_SIZE = 40;
public static final int PC_BANG_POINTS = -100;
public static final int CLAN_REPUTATION = -200;
public static final int FAME = -300;
private static final FileFilter NUMERIC_FILTER = new NumericNameFilter();
private final Map<Integer, ListContainer> _entries = new HashMap<>();
protected MultisellData()
{
load();
}
@Override
public void load()
{
_entries.clear();
parseDatapackDirectory("data/multisell", false);
if (Config.CUSTOM_MULTISELL_LOAD)
{
parseDatapackDirectory("data/multisell/custom", false);
}
verify();
LOGGER.log(Level.INFO, getClass().getSimpleName() + ": Loaded " + _entries.size() + " multisell lists.");
}
@Override
public void parseDocument(Document doc, File f)
{
try
{
int id = Integer.parseInt(f.getName().replaceAll(".xml", ""));
int entryId = 1;
Node att;
final ListContainer list = new ListContainer(id);
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
list.setApplyTaxes(parseBoolean(n.getAttributes(), "applyTaxes", false));
list.setNewMultisell(parseBoolean(n.getAttributes(), "isNewMultisell", false));
list.setMaintainEnchantment(parseBoolean(n.getAttributes(), "maintainEnchantment", false));
att = n.getAttributes().getNamedItem("useRate");
if (att != null)
{
try
{
list.setUseRate(Double.valueOf(att.getNodeValue()));
if (list.getUseRate() <= 1e-6)
{
throw new NumberFormatException("The value cannot be 0"); // threat 0 as invalid value
}
}
catch (NumberFormatException e)
{
try
{
list.setUseRate(Config.class.getField(att.getNodeValue()).getDouble(Config.class));
}
catch (Exception e1)
{
LOGGER.warning(e1.getMessage() + doc.getLocalName());
list.setUseRate(1.0);
}
}
catch (DOMException e)
{
LOGGER.warning(e.getMessage() + doc.getLocalName());
}
}
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("item".equalsIgnoreCase(d.getNodeName()))
{
Entry e = parseEntry(d, entryId++, list);
list.getEntries().add(e);
}
else if ("npcs".equalsIgnoreCase(d.getNodeName()))
{
for (Node b = d.getFirstChild(); b != null; b = b.getNextSibling())
{
if ("npc".equalsIgnoreCase(b.getNodeName()))
{
if (Util.isDigit(b.getTextContent()))
{
list.allowNpc(Integer.parseInt(b.getTextContent()));
}
}
}
}
}
}
}
_entries.put(id, list);
}
catch (Exception e)
{
LOGGER.log(Level.SEVERE, getClass().getSimpleName() + ": Error in file " + f, e);
}
}
@Override
public FileFilter getCurrentFileFilter()
{
return NUMERIC_FILTER;
}
private final Entry parseEntry(Node n, int entryId, ListContainer list)
{
Node first = n.getFirstChild();
final Entry entry = new Entry(entryId);
NamedNodeMap attrs;
Node att;
StatsSet set;
for (n = first; n != null; n = n.getNextSibling())
{
if ("ingredient".equalsIgnoreCase(n.getNodeName()))
{
attrs = n.getAttributes();
set = new StatsSet();
for (int i = 0; i < attrs.getLength(); i++)
{
att = attrs.item(i);
set.set(att.getNodeName(), att.getNodeValue());
}
entry.addIngredient(new Ingredient(set));
}
else if ("production".equalsIgnoreCase(n.getNodeName()))
{
attrs = n.getAttributes();
set = new StatsSet();
for (int i = 0; i < attrs.getLength(); i++)
{
att = attrs.item(i);
set.set(att.getNodeName(), att.getNodeValue());
}
entry.addProduct(new Ingredient(set));
}
}
return entry;
}
/**
* This will generate the multisell list for the items.<br>
* There exist various parameters in multisells that affect the way they will appear:
* <ol>
* <li>Inventory only:
* <ul>
* <li>If true, only show items of the multisell for which the "primary" ingredients are already in the player's inventory. By "primary" ingredients we mean weapon and armor.</li>
* <li>If false, show the entire list.</li>
* </ul>
* </li>
* <li>Maintain enchantment: presumably, only lists with "inventory only" set to true should sometimes have this as true. This makes no sense otherwise...
* <ul>
* <li>If true, then the product will match the enchantment level of the ingredient.<br>
* If the player has multiple items that match the ingredient list but the enchantment levels differ, then the entries need to be duplicated to show the products and ingredients for each enchantment level.<br>
* For example: If the player has a crystal staff +1 and a crystal staff +3 and goes to exchange it at the mammon, the list should have all exchange possibilities for the +1 staff, followed by all possibilities for the +3 staff.</li>
* <li>If false, then any level ingredient will be considered equal and product will always be at +0</li>
* </ul>
* </li>
* <li>Apply taxes: Uses the "taxIngredient" entry in order to add a certain amount of adena to the ingredients.
* <li>
* <li>Additional product and ingredient multipliers.</li>
* </ol>
* @param listId
* @param player
* @param npc
* @param inventoryOnly
* @param productMultiplier
* @param ingredientMultiplier
*/
public final void separateAndSend(int listId, L2PcInstance player, L2Npc npc, boolean inventoryOnly, double productMultiplier, double ingredientMultiplier)
{
ListContainer template = _entries.get(listId);
if (template == null)
{
LOGGER.warning(getClass().getSimpleName() + ": can't find list id: " + listId + " requested by player: " + player.getName() + ", npcId:" + (npc != null ? npc.getId() : 0));
return;
}
if (((npc != null) && !template.isNpcAllowed(npc.getId())) || ((npc == null) && template.isNpcOnly()))
{
LOGGER.warning(getClass().getSimpleName() + ": player " + player + " attempted to open multisell " + listId + " from npc " + npc + " which is not allowed!");
return;
}
final PreparedListContainer list = new PreparedListContainer(template, inventoryOnly, player, npc);
// Pass through this only when multipliers are different from 1
if ((productMultiplier != 1) || (ingredientMultiplier != 1))
{
list.getEntries().forEach(entry ->
{
// Math.max used here to avoid dropping count to 0
entry.getProducts().forEach(product -> product.setItemCount((long) Math.max(product.getItemCount() * productMultiplier, 1)));
// Math.max used here to avoid dropping count to 0
entry.getIngredients().forEach(ingredient -> ingredient.setItemCount((long) Math.max(ingredient.getItemCount() * ingredientMultiplier, 1)));
});
}
int index = 0;
do
{
// send list at least once even if size = 0
player.sendPacket(new MultiSellList(list, index));
index += PAGE_SIZE;
}
while (index < list.getEntries().size());
player.setMultiSell(list);
}
public final void separateAndSend(int listId, L2PcInstance player, L2Npc npc, boolean inventoryOnly)
{
separateAndSend(listId, player, npc, inventoryOnly, 1, 1);
}
public static final boolean hasSpecialIngredient(int id, long amount, L2PcInstance player)
{
switch (id)
{
case CLAN_REPUTATION:
if (player.getClan() == null)
{
player.sendPacket(SystemMessageId.YOU_ARE_NOT_A_CLAN_MEMBER_AND_CANNOT_PERFORM_THIS_ACTION);
break;
}
if (!player.isClanLeader())
{
player.sendPacket(SystemMessageId.ONLY_THE_CLAN_LEADER_IS_ENABLED);
break;
}
if (player.getClan().getReputationScore() < amount)
{
player.sendPacket(SystemMessageId.THE_CLAN_REPUTATION_IS_TOO_LOW);
break;
}
return true;
case FAME:
if (player.getFame() < amount)
{
player.sendPacket(SystemMessageId.YOU_DON_T_HAVE_ENOUGH_FAME_TO_DO_THAT);
break;
}
return true;
}
return false;
}
public static final boolean takeSpecialIngredient(int id, long amount, L2PcInstance player)
{
switch (id)
{
case CLAN_REPUTATION:
player.getClan().takeReputationScore((int) amount, true);
SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.S1_POINT_S_HAVE_BEEN_DEDUCTED_FROM_THE_CLAN_S_REPUTATION);
smsg.addLong(amount);
player.sendPacket(smsg);
return true;
case FAME:
player.setFame(player.getFame() - (int) amount);
player.sendPacket(new UserInfo(player));
// player.sendPacket(new ExBrExtraUserInfo(player));
return true;
}
return false;
}
public static final void giveSpecialProduct(int id, long amount, L2PcInstance player)
{
switch (id)
{
case CLAN_REPUTATION:
player.getClan().addReputationScore((int) amount, true);
break;
case FAME:
player.setFame((int) (player.getFame() + amount));
player.sendPacket(new UserInfo(player));
// player.sendPacket(new ExBrExtraUserInfo(player));
break;
}
}
private final void verify()
{
ListContainer list;
final Iterator<ListContainer> iter = _entries.values().iterator();
while (iter.hasNext())
{
list = iter.next();
for (Entry ent : list.getEntries())
{
for (Ingredient ing : ent.getIngredients())
{
if (!verifyIngredient(ing))
{
LOGGER.warning(getClass().getSimpleName() + ": can't find ingredient with itemId: " + ing.getItemId() + " in list: " + list.getListId());
}
}
for (Ingredient ing : ent.getProducts())
{
if (!verifyIngredient(ing))
{
LOGGER.warning(getClass().getSimpleName() + ": can't find product with itemId: " + ing.getItemId() + " in list: " + list.getListId());
}
}
}
}
}
private final boolean verifyIngredient(Ingredient ing)
{
switch (ing.getItemId())
{
case CLAN_REPUTATION:
case FAME:
return true;
default:
return ing.getTemplate() != null;
}
}
public static MultisellData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final MultisellData _instance = new MultisellData();
}
}

View File

@@ -0,0 +1,205 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jserver.Config;
import com.l2jserver.L2DatabaseFactory;
import com.l2jserver.gameserver.model.holders.ItemHolder;
import com.l2jserver.gameserver.model.holders.SkillHolder;
public class NpcBufferTable
{
private static final Logger LOGGER = Logger.getLogger(NpcBufferTable.class.getName());
private final Map<Integer, NpcBufferSkills> _buffers = new HashMap<>();
public static class NpcBufferData
{
private final SkillHolder _skill;
private final ItemHolder _fee;
protected NpcBufferData(int skillId, int skillLevel, int feeId, int feeAmount)
{
_skill = new SkillHolder(skillId, skillLevel);
_fee = new ItemHolder(feeId, feeAmount);
}
public SkillHolder getSkill()
{
return _skill;
}
public ItemHolder getFee()
{
return _fee;
}
}
private static class NpcBufferSkills
{
private final int _npcId;
private final Map<Integer, NpcBufferData> _skills = new HashMap<>();
protected NpcBufferSkills(int npcId)
{
_npcId = npcId;
}
public void addSkill(int skillId, int skillLevel, int skillFeeId, int skillFeeAmount, int buffGroup)
{
_skills.put(buffGroup, new NpcBufferData(skillId, skillLevel, skillFeeId, skillFeeAmount));
}
public NpcBufferData getSkillGroupInfo(int buffGroup)
{
return _skills.get(buffGroup);
}
@SuppressWarnings("unused")
public int getNpcId()
{
return _npcId;
}
}
protected NpcBufferTable()
{
int skillCount = 0;
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
Statement s = con.createStatement();
ResultSet rset = s.executeQuery("SELECT `npc_id`,`skill_id`,`skill_level`,`skill_fee_id`,`skill_fee_amount`,`buff_group` FROM `npc_buffer` ORDER BY `npc_id` ASC"))
{
int lastNpcId = 0;
NpcBufferSkills skills = null;
while (rset.next())
{
int npcId = rset.getInt("npc_id");
int skillId = rset.getInt("skill_id");
int skillLevel = rset.getInt("skill_level");
int skillFeeId = rset.getInt("skill_fee_id");
int skillFeeAmount = rset.getInt("skill_fee_amount");
int buffGroup = rset.getInt("buff_group");
if (npcId != lastNpcId)
{
if (lastNpcId != 0)
{
_buffers.put(lastNpcId, skills);
}
skills = new NpcBufferSkills(npcId);
skills.addSkill(skillId, skillLevel, skillFeeId, skillFeeAmount, buffGroup);
}
else if (skills != null)
{
skills.addSkill(skillId, skillLevel, skillFeeId, skillFeeAmount, buffGroup);
}
lastNpcId = npcId;
skillCount++;
}
if (lastNpcId != 0)
{
_buffers.put(lastNpcId, skills);
}
}
catch (SQLException e)
{
LOGGER.log(Level.SEVERE, getClass().getSimpleName() + ": Error reading npc_buffer table: " + e.getMessage(), e);
}
if (Config.CUSTOM_NPCBUFFER_TABLES)
{
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
Statement s = con.createStatement();
ResultSet rset = s.executeQuery("SELECT `npc_id`,`skill_id`,`skill_level`,`skill_fee_id`,`skill_fee_amount`,`buff_group` FROM `custom_npc_buffer` ORDER BY `npc_id` ASC"))
{
int lastNpcId = 0;
NpcBufferSkills skills = null;
while (rset.next())
{
int npcId = rset.getInt("npc_id");
int skillId = rset.getInt("skill_id");
int skillLevel = rset.getInt("skill_level");
int skillFeeId = rset.getInt("skill_fee_id");
int skillFeeAmount = rset.getInt("skill_fee_amount");
int buffGroup = rset.getInt("buff_group");
if (npcId != lastNpcId)
{
if (lastNpcId != 0)
{
_buffers.put(lastNpcId, skills);
}
skills = new NpcBufferSkills(npcId);
skills.addSkill(skillId, skillLevel, skillFeeId, skillFeeAmount, buffGroup);
}
else if (skills != null)
{
skills.addSkill(skillId, skillLevel, skillFeeId, skillFeeAmount, buffGroup);
}
lastNpcId = npcId;
skillCount++;
}
if (lastNpcId != 0)
{
_buffers.put(lastNpcId, skills);
}
}
catch (SQLException e)
{
LOGGER.log(Level.SEVERE, getClass().getSimpleName() + ": Error reading custom_npc_buffer table: " + e.getMessage(), e);
}
}
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _buffers.size() + " buffers and " + skillCount + " skills.");
}
public NpcBufferData getSkillInfo(int npcId, int buffGroup)
{
final NpcBufferSkills skills = _buffers.get(npcId);
if (skills != null)
{
return skills.getSkillGroupInfo(buffGroup);
}
return null;
}
public static NpcBufferTable getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final NpcBufferTable _instance = new NpcBufferTable();
}
}

View File

@@ -0,0 +1,869 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.Config;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.enums.AISkillScope;
import com.l2jserver.gameserver.model.StatsSet;
import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate;
import com.l2jserver.gameserver.model.base.ClassId;
import com.l2jserver.gameserver.model.drops.DropListScope;
import com.l2jserver.gameserver.model.drops.GeneralDropItem;
import com.l2jserver.gameserver.model.drops.GroupedGeneralDropItem;
import com.l2jserver.gameserver.model.drops.IDropItem;
import com.l2jserver.gameserver.model.effects.L2EffectType;
import com.l2jserver.gameserver.model.holders.MinionHolder;
import com.l2jserver.gameserver.model.holders.SkillHolder;
import com.l2jserver.gameserver.model.skills.Skill;
import com.l2jserver.gameserver.util.Util;
/**
* NPC data parser.
* @author NosBit
*/
public class NpcData implements DocumentParser
{
private final Map<Integer, L2NpcTemplate> _npcs = new ConcurrentHashMap<>();
private final Map<String, Integer> _clans = new ConcurrentHashMap<>();
private MinionData _minionData;
protected NpcData()
{
load();
}
@Override
public synchronized void load()
{
_minionData = new MinionData();
parseDatapackDirectory("data/stats/npcs", false);
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _npcs.size() + " NPCs.");
if (Config.CUSTOM_NPC_DATA)
{
final int npcCount = _npcs.size();
parseDatapackDirectory("data/stats/npcs/custom", true);
LOGGER.info(getClass().getSimpleName() + ": Loaded " + (_npcs.size() - npcCount) + " Custom NPCs.");
}
_minionData = null;
loadNpcsSkillLearn();
}
@Override
public void parseDocument(Document doc, File f)
{
for (Node node = doc.getFirstChild(); node != null; node = node.getNextSibling())
{
if ("list".equalsIgnoreCase(node.getNodeName()))
{
for (Node list_node = node.getFirstChild(); list_node != null; list_node = list_node.getNextSibling())
{
if ("npc".equalsIgnoreCase(list_node.getNodeName()))
{
NamedNodeMap attrs = list_node.getAttributes();
final StatsSet set = new StatsSet();
final int npcId = parseInteger(attrs, "id");
Map<String, Object> parameters = null;
Map<Integer, Skill> skills = null;
Set<Integer> clans = null;
Set<Integer> ignoreClanNpcIds = null;
Map<DropListScope, List<IDropItem>> dropLists = null;
set.set("id", npcId);
set.set("displayId", parseInteger(attrs, "displayId"));
set.set("level", parseByte(attrs, "level"));
set.set("type", parseString(attrs, "type"));
set.set("name", parseString(attrs, "name"));
set.set("usingServerSideName", parseBoolean(attrs, "usingServerSideName"));
set.set("title", parseString(attrs, "title"));
set.set("usingServerSideTitle", parseBoolean(attrs, "usingServerSideTitle"));
for (Node npc_node = list_node.getFirstChild(); npc_node != null; npc_node = npc_node.getNextSibling())
{
attrs = npc_node.getAttributes();
switch (npc_node.getNodeName().toLowerCase())
{
case "parameters":
{
if (parameters == null)
{
parameters = new HashMap<>();
}
for (Node parameters_node = npc_node.getFirstChild(); parameters_node != null; parameters_node = parameters_node.getNextSibling())
{
attrs = parameters_node.getAttributes();
switch (parameters_node.getNodeName().toLowerCase())
{
case "param":
{
parameters.put(parseString(attrs, "name"), parseString(attrs, "value"));
break;
}
case "skill":
{
parameters.put(parseString(attrs, "name"), new SkillHolder(parseInteger(attrs, "id"), parseInteger(attrs, "level")));
break;
}
case "minions":
{
final List<MinionHolder> minions = new ArrayList<>(1);
for (Node minions_node = parameters_node.getFirstChild(); minions_node != null; minions_node = minions_node.getNextSibling())
{
if (minions_node.getNodeName().equalsIgnoreCase("npc"))
{
attrs = minions_node.getAttributes();
minions.add(new MinionHolder(parseInteger(attrs, "id"), parseInteger(attrs, "count"), parseInteger(attrs, "respawnTime"), parseInteger(attrs, "weightPoint")));
}
}
if (!minions.isEmpty())
{
parameters.put(parseString(parameters_node.getAttributes(), "name"), minions);
}
break;
}
}
}
break;
}
case "race":
case "sex":
set.set(npc_node.getNodeName(), npc_node.getTextContent().toUpperCase());
break;
case "equipment":
{
set.set("chestId", parseInteger(attrs, "chest"));
set.set("rhandId", parseInteger(attrs, "rhand"));
set.set("lhandId", parseInteger(attrs, "lhand"));
set.set("weaponEnchant", parseInteger(attrs, "weaponEnchant"));
break;
}
case "acquire":
{
set.set("expRate", parseDouble(attrs, "expRate"));
set.set("sp", parseDouble(attrs, "sp"));
set.set("raidPoints", parseDouble(attrs, "raidPoints"));
break;
}
case "stats":
{
set.set("baseSTR", parseInteger(attrs, "str"));
set.set("baseINT", parseInteger(attrs, "int"));
set.set("baseDEX", parseInteger(attrs, "dex"));
set.set("baseWIT", parseInteger(attrs, "wit"));
set.set("baseCON", parseInteger(attrs, "con"));
set.set("baseMEN", parseInteger(attrs, "men"));
for (Node stats_node = npc_node.getFirstChild(); stats_node != null; stats_node = stats_node.getNextSibling())
{
attrs = stats_node.getAttributes();
switch (stats_node.getNodeName().toLowerCase())
{
case "vitals":
{
set.set("baseHpMax", parseDouble(attrs, "hp"));
set.set("baseHpReg", parseDouble(attrs, "hpRegen"));
set.set("baseMpMax", parseDouble(attrs, "mp"));
set.set("baseMpReg", parseDouble(attrs, "mpRegen"));
break;
}
case "attack":
{
set.set("basePAtk", parseDouble(attrs, "physical"));
set.set("baseMAtk", parseDouble(attrs, "magical"));
set.set("baseRndDam", parseInteger(attrs, "random"));
set.set("baseCritRate", parseInteger(attrs, "critical"));
set.set("accuracy", parseDouble(attrs, "accuracy"));// TODO: Implement me
set.set("basePAtkSpd", parseInteger(attrs, "attackSpeed"));
set.set("reuseDelay", parseInteger(attrs, "reuseDelay"));// TODO: Implement me
set.set("baseAtkType", parseString(attrs, "type"));
set.set("baseAtkRange", parseInteger(attrs, "range"));
set.set("distance", parseInteger(attrs, "distance"));// TODO: Implement me
set.set("width", parseInteger(attrs, "width"));// TODO: Implement me
break;
}
case "defence":
{
set.set("basePDef", parseDouble(attrs, "physical"));
set.set("baseMDef", parseDouble(attrs, "magical"));
set.set("evasion", parseInteger(attrs, "evasion"));// TODO: Implement me
set.set("baseShldDef", parseInteger(attrs, "shield"));
set.set("baseShldRate", parseInteger(attrs, "shieldRate"));
break;
}
case "attribute":
{
for (Node attribute_node = stats_node.getFirstChild(); attribute_node != null; attribute_node = attribute_node.getNextSibling())
{
attrs = attribute_node.getAttributes();
switch (attribute_node.getNodeName().toLowerCase())
{
case "attack":
{
String attackAttributeType = parseString(attrs, "type");
switch (attackAttributeType.toUpperCase())
{
case "FIRE":
set.set("baseFire", parseInteger(attrs, "value"));
break;
case "WATER":
set.set("baseWater", parseInteger(attrs, "value"));
break;
case "WIND":
set.set("baseWind", parseInteger(attrs, "value"));
break;
case "EARTH":
set.set("baseEarth", parseInteger(attrs, "value"));
break;
case "DARK":
set.set("baseDark", parseInteger(attrs, "value"));
break;
case "HOLY":
set.set("baseHoly", parseInteger(attrs, "value"));
break;
}
break;
}
case "defence":
{
set.set("baseFireRes", parseInteger(attrs, "fire"));
set.set("baseWaterRes", parseInteger(attrs, "water"));
set.set("baseWindRes", parseInteger(attrs, "wind"));
set.set("baseEarthRes", parseInteger(attrs, "earth"));
set.set("baseHolyRes", parseInteger(attrs, "holy"));
set.set("baseDarkRes", parseInteger(attrs, "dark"));
set.set("baseElementRes", parseInteger(attrs, "default"));
break;
}
}
}
break;
}
case "speed":
{
for (Node speed_node = stats_node.getFirstChild(); speed_node != null; speed_node = speed_node.getNextSibling())
{
attrs = speed_node.getAttributes();
switch (speed_node.getNodeName().toLowerCase())
{
case "walk":
{
set.set("baseWalkSpd", parseDouble(attrs, "ground"));
set.set("baseSwimWalkSpd", parseDouble(attrs, "swim"));
set.set("baseFlyWalkSpd", parseDouble(attrs, "fly"));
break;
}
case "run":
{
set.set("baseRunSpd", parseDouble(attrs, "ground"));
set.set("baseSwimRunSpd", parseDouble(attrs, "swim"));
set.set("baseFlyRunSpd", parseDouble(attrs, "fly"));
break;
}
}
}
break;
}
case "hit_time":
set.set("hit_time", npc_node.getTextContent());// TODO: Implement me default 600 (value in ms)
break;
}
}
break;
}
case "status":
{
set.set("unique", parseBoolean(attrs, "unique"));
set.set("attackable", parseBoolean(attrs, "attackable"));
set.set("targetable", parseBoolean(attrs, "targetable"));
set.set("talkable", parseBoolean(attrs, "talkable"));
set.set("undying", parseBoolean(attrs, "undying"));
set.set("showName", parseBoolean(attrs, "showName"));
set.set("flying", parseBoolean(attrs, "flying"));
set.set("canMove", parseBoolean(attrs, "canMove"));
set.set("noSleepMode", parseBoolean(attrs, "noSleepMode"));
set.set("passableDoor", parseBoolean(attrs, "passableDoor"));
set.set("hasSummoner", parseBoolean(attrs, "hasSummoner"));
set.set("canBeSown", parseBoolean(attrs, "canBeSown"));
break;
}
case "skill_list":
{
skills = new HashMap<>();
for (Node skill_list_node = npc_node.getFirstChild(); skill_list_node != null; skill_list_node = skill_list_node.getNextSibling())
{
if ("skill".equalsIgnoreCase(skill_list_node.getNodeName()))
{
attrs = skill_list_node.getAttributes();
final int skillId = parseInteger(attrs, "id");
final int skillLevel = parseInteger(attrs, "level");
final Skill skill = SkillData.getInstance().getSkill(skillId, skillLevel);
if (skill != null)
{
skills.put(skill.getId(), skill);
}
else
{
LOGGER.warning("[" + f.getName() + "] skill not found. NPC ID: " + npcId + " Skill ID:" + skillId + " Skill Level: " + skillLevel);
}
}
}
break;
}
case "shots":
{
set.set("soulShot", parseInteger(attrs, "soul"));
set.set("spiritShot", parseInteger(attrs, "spirit"));
set.set("shotShotChance", parseInteger(attrs, "shotChance"));
set.set("spiritShotChance", parseInteger(attrs, "spiritChance"));
break;
}
case "corpse_time":
set.set("corpseTime", npc_node.getTextContent());
break;
case "ex_crt_effect":
set.set("ex_crt_effect", npc_node.getTextContent()); // TODO: Implement me default ? type boolean
break;
case "s_npc_prop_hp_rate":
set.set("s_npc_prop_hp_rate", npc_node.getTextContent()); // TODO: Implement me default 1 type double
break;
case "ai":
{
set.set("aiType", parseString(attrs, "type"));
set.set("aggroRange", parseInteger(attrs, "aggroRange"));
set.set("clanHelpRange", parseInteger(attrs, "clanHelpRange"));
set.set("dodge", parseInteger(attrs, "dodge"));
set.set("isChaos", parseBoolean(attrs, "isChaos"));
set.set("isAggressive", parseBoolean(attrs, "isAggressive"));
for (Node ai_node = npc_node.getFirstChild(); ai_node != null; ai_node = ai_node.getNextSibling())
{
attrs = ai_node.getAttributes();
switch (ai_node.getNodeName().toLowerCase())
{
case "skill":
{
set.set("minSkillChance", parseInteger(attrs, "minChance"));
set.set("maxSkillChance", parseInteger(attrs, "maxChance"));
set.set("primarySkillId", parseInteger(attrs, "primaryId"));
set.set("shortRangeSkillId", parseInteger(attrs, "shortRangeId"));
set.set("shortRangeSkillChance", parseInteger(attrs, "shortRangeChance"));
set.set("longRangeSkillId", parseInteger(attrs, "longRangeId"));
set.set("longRangeSkillChance", parseInteger(attrs, "longRangeChance"));
break;
}
case "clan_list":
{
for (Node clan_list_node = ai_node.getFirstChild(); clan_list_node != null; clan_list_node = clan_list_node.getNextSibling())
{
attrs = clan_list_node.getAttributes();
switch (clan_list_node.getNodeName().toLowerCase())
{
case "clan":
{
if (clans == null)
{
clans = new HashSet<>(1);
}
clans.add(getOrCreateClanId(clan_list_node.getTextContent()));
break;
}
case "ignore_npc_id":
{
if (ignoreClanNpcIds == null)
{
ignoreClanNpcIds = new HashSet<>(1);
}
ignoreClanNpcIds.add(Integer.parseInt(clan_list_node.getTextContent()));
break;
}
}
}
break;
}
}
}
break;
}
case "drop_lists":
{
for (Node drop_lists_node = npc_node.getFirstChild(); drop_lists_node != null; drop_lists_node = drop_lists_node.getNextSibling())
{
DropListScope dropListScope = null;
try
{
dropListScope = Enum.valueOf(DropListScope.class, drop_lists_node.getNodeName().toUpperCase());
}
catch (Exception e)
{
}
if (dropListScope != null)
{
if (dropLists == null)
{
dropLists = new EnumMap<>(DropListScope.class);
}
List<IDropItem> dropList = new ArrayList<>();
parseDropList(f, drop_lists_node, dropListScope, dropList);
dropLists.put(dropListScope, Collections.unmodifiableList(dropList));
}
}
break;
}
case "collision":
{
for (Node collision_node = npc_node.getFirstChild(); collision_node != null; collision_node = collision_node.getNextSibling())
{
attrs = collision_node.getAttributes();
switch (collision_node.getNodeName().toLowerCase())
{
case "radius":
{
set.set("collision_radius", parseDouble(attrs, "normal"));
set.set("collisionRadiusGrown", parseDouble(attrs, "grown"));
break;
}
case "height":
{
set.set("collision_height", parseDouble(attrs, "normal"));
set.set("collisionHeightGrown", parseDouble(attrs, "grown"));
break;
}
}
}
break;
}
}
}
L2NpcTemplate template = _npcs.get(npcId);
if (template == null)
{
template = new L2NpcTemplate(set);
_npcs.put(template.getId(), template);
}
else
{
template.set(set);
}
if (_minionData._tempMinions.containsKey(npcId))
{
if (parameters == null)
{
parameters = new HashMap<>();
}
parameters.putIfAbsent("Privates", _minionData._tempMinions.get(npcId));
}
if (parameters != null)
{
// Using unmodifiable map parameters of template are not meant to be changed at runtime.
template.setParameters(new StatsSet(Collections.unmodifiableMap(parameters)));
}
else
{
template.setParameters(StatsSet.EMPTY_STATSET);
}
if (skills != null)
{
Map<AISkillScope, List<Skill>> aiSkillLists = null;
for (Skill skill : skills.values())
{
if (!skill.isPassive())
{
if (aiSkillLists == null)
{
aiSkillLists = new EnumMap<>(AISkillScope.class);
}
List<AISkillScope> aiSkillScopes = new ArrayList<>();
final AISkillScope shortOrLongRangeScope = skill.getCastRange() <= 150 ? AISkillScope.SHORT_RANGE : AISkillScope.SHORT_RANGE;
if (skill.isSuicideAttack())
{
aiSkillScopes.add(AISkillScope.SUICIDE);
}
else
{
aiSkillScopes.add(AISkillScope.GENERAL);
if (skill.isContinuous())
{
if (!skill.isDebuff())
{
aiSkillScopes.add(AISkillScope.BUFF);
}
else
{
aiSkillScopes.add(AISkillScope.DEBUFF);
aiSkillScopes.add(AISkillScope.COT);
aiSkillScopes.add(shortOrLongRangeScope);
}
}
else
{
if (skill.hasEffectType(L2EffectType.DISPEL, L2EffectType.DISPEL_BY_SLOT))
{
aiSkillScopes.add(AISkillScope.NEGATIVE);
aiSkillScopes.add(shortOrLongRangeScope);
}
else if (skill.hasEffectType(L2EffectType.HEAL))
{
aiSkillScopes.add(AISkillScope.HEAL);
}
else if (skill.hasEffectType(L2EffectType.PHYSICAL_ATTACK, L2EffectType.PHYSICAL_ATTACK_HP_LINK, L2EffectType.MAGICAL_ATTACK, L2EffectType.DEATH_LINK, L2EffectType.HP_DRAIN))
{
aiSkillScopes.add(AISkillScope.ATTACK);
aiSkillScopes.add(AISkillScope.UNIVERSAL);
aiSkillScopes.add(shortOrLongRangeScope);
}
else if (skill.hasEffectType(L2EffectType.SLEEP))
{
aiSkillScopes.add(AISkillScope.IMMOBILIZE);
}
else if (skill.hasEffectType(L2EffectType.STUN, L2EffectType.ROOT))
{
aiSkillScopes.add(AISkillScope.IMMOBILIZE);
aiSkillScopes.add(shortOrLongRangeScope);
}
else if (skill.hasEffectType(L2EffectType.MUTE, L2EffectType.FEAR))
{
aiSkillScopes.add(AISkillScope.COT);
aiSkillScopes.add(shortOrLongRangeScope);
}
else if (skill.hasEffectType(L2EffectType.PARALYZE))
{
aiSkillScopes.add(AISkillScope.IMMOBILIZE);
aiSkillScopes.add(shortOrLongRangeScope);
}
else if (skill.hasEffectType(L2EffectType.DMG_OVER_TIME, L2EffectType.DMG_OVER_TIME_PERCENT))
{
aiSkillScopes.add(shortOrLongRangeScope);
}
else if (skill.hasEffectType(L2EffectType.RESURRECTION))
{
aiSkillScopes.add(AISkillScope.RES);
}
else
{
aiSkillScopes.add(AISkillScope.UNIVERSAL);
}
}
}
for (AISkillScope aiSkillScope : aiSkillScopes)
{
List<Skill> aiSkills = aiSkillLists.get(aiSkillScope);
if (aiSkills == null)
{
aiSkills = new ArrayList<>();
aiSkillLists.put(aiSkillScope, aiSkills);
}
aiSkills.add(skill);
}
}
}
template.setSkills(skills);
template.setAISkillLists(aiSkillLists);
}
else
{
template.setSkills(null);
template.setAISkillLists(null);
}
template.setClans(clans);
template.setIgnoreClanNpcIds(ignoreClanNpcIds);
template.setDropLists(dropLists);
}
}
}
}
}
private void parseDropList(File f, Node drop_list_node, DropListScope dropListScope, List<IDropItem> drops)
{
for (Node drop_node = drop_list_node.getFirstChild(); drop_node != null; drop_node = drop_node.getNextSibling())
{
NamedNodeMap attrs = drop_node.getAttributes();
switch (drop_node.getNodeName().toLowerCase())
{
case "group":
{
GroupedGeneralDropItem dropItem = dropListScope.newGroupedDropItem(parseDouble(attrs, "chance"));
List<IDropItem> groupedDropList = new ArrayList<>(2);
for (Node group_node = drop_node.getFirstChild(); group_node != null; group_node = group_node.getNextSibling())
{
parseDropListItem(group_node, dropListScope, groupedDropList);
}
List<GeneralDropItem> items = new ArrayList<>(groupedDropList.size());
for (IDropItem item : groupedDropList)
{
if (item instanceof GeneralDropItem)
{
items.add((GeneralDropItem) item);
}
else
{
LOGGER.warning("[" + f + "] grouped general drop item supports only general drop item.");
}
}
dropItem.setItems(items);
drops.add(dropItem);
break;
}
default:
{
parseDropListItem(drop_node, dropListScope, drops);
break;
}
}
}
}
private void parseDropListItem(Node drop_list_item, DropListScope dropListScope, List<IDropItem> drops)
{
NamedNodeMap attrs = drop_list_item.getAttributes();
switch (drop_list_item.getNodeName().toLowerCase())
{
case "item":
{
final IDropItem dropItem = dropListScope.newDropItem(parseInteger(attrs, "id"), parseLong(attrs, "min"), parseLong(attrs, "max"), parseDouble(attrs, "chance"));
if (dropItem != null)
{
drops.add(dropItem);
}
break;
}
}
}
/**
* Gets or creates a clan id if it doesnt exists.
* @param clanName the clan name to get or create its id
* @return the clan id for the given clan name
*/
private int getOrCreateClanId(String clanName)
{
Integer id = _clans.get(clanName.toUpperCase());
if (id == null)
{
id = _clans.size();
_clans.put(clanName.toUpperCase(), id);
}
return id;
}
/**
* Gets the clan id
* @param clanName the clan name to get its id
* @return the clan id for the given clan name if it exists, -1 otherwise
*/
public int getClanId(String clanName)
{
Integer id = _clans.get(clanName.toUpperCase());
return id != null ? id : -1;
}
/**
* Gets the template.
* @param id the template Id to get.
* @return the template for the given id.
*/
public L2NpcTemplate getTemplate(int id)
{
return _npcs.get(id);
}
/**
* Gets the template by name.
* @param name of the template to get.
* @return the template for the given name.
*/
public L2NpcTemplate getTemplateByName(String name)
{
for (L2NpcTemplate npcTemplate : _npcs.values())
{
if (npcTemplate.getName().equalsIgnoreCase(name))
{
return npcTemplate;
}
}
return null;
}
/**
* Gets all templates matching the filter.
* @param filter
* @return the template list for the given filter
*/
public List<L2NpcTemplate> getTemplates(Predicate<L2NpcTemplate> filter)
{
//@formatter:off
return _npcs.values().stream()
.filter(filter)
.collect(Collectors.toList());
//@formatter:on
}
/**
* Gets the all of level.
* @param lvls of all the templates to get.
* @return the template list for the given level.
*/
public List<L2NpcTemplate> getAllOfLevel(int... lvls)
{
return getTemplates(template -> Util.contains(lvls, template.getLevel()));
}
/**
* Gets the all monsters of level.
* @param lvls of all the monster templates to get.
* @return the template list for the given level.
*/
public List<L2NpcTemplate> getAllMonstersOfLevel(int... lvls)
{
return getTemplates(template -> Util.contains(lvls, template.getLevel()) && template.isType("L2Monster"));
}
/**
* Gets the all npc starting with.
* @param text of all the NPC templates which its name start with.
* @return the template list for the given letter.
*/
public List<L2NpcTemplate> getAllNpcStartingWith(String text)
{
return getTemplates(template -> template.isType("L2Npc") && template.getName().startsWith(text));
}
/**
* Gets the all npc of class type.
* @param classTypes of all the templates to get.
* @return the template list for the given class type.
*/
public List<L2NpcTemplate> getAllNpcOfClassType(String... classTypes)
{
return getTemplates(template -> Util.contains(classTypes, template.getType(), true));
}
public void loadNpcsSkillLearn()
{
_npcs.values().forEach(template ->
{
final List<ClassId> teachInfo = SkillLearnData.getInstance().getSkillLearnData(template.getId());
if (teachInfo != null)
{
template.addTeachInfo(teachInfo);
}
});
}
/**
* This class handles minions from Spawn System<br>
* Once Spawn System gets reworked delete this class<br>
* @author Zealar
*/
private final class MinionData implements DocumentParser
{
public final Map<Integer, List<MinionHolder>> _tempMinions = new HashMap<>();
protected MinionData()
{
load();
}
@Override
public void load()
{
_tempMinions.clear();
parseDatapackFile("data/minionData.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _tempMinions.size() + " minions data.");
}
@Override
public void parseDocument(Document doc)
{
for (Node node = doc.getFirstChild(); node != null; node = node.getNextSibling())
{
if ("list".equals(node.getNodeName()))
{
for (Node list_node = node.getFirstChild(); list_node != null; list_node = list_node.getNextSibling())
{
if ("npc".equals(list_node.getNodeName()))
{
final List<MinionHolder> minions = new ArrayList<>(1);
NamedNodeMap attrs = list_node.getAttributes();
int id = parseInteger(attrs, "id");
for (Node npc_node = list_node.getFirstChild(); npc_node != null; npc_node = npc_node.getNextSibling())
{
if ("minion".equals(npc_node.getNodeName()))
{
attrs = npc_node.getAttributes();
minions.add(new MinionHolder(parseInteger(attrs, "id"), parseInteger(attrs, "count"), parseInteger(attrs, "respawnTime"), 0));
}
}
_tempMinions.put(id, minions);
}
}
}
}
}
}
/**
* Gets the single instance of NpcData.
* @return single instance of NpcData
*/
public static NpcData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final NpcData _instance = new NpcData();
}
}

View File

@@ -0,0 +1,133 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.HashMap;
import java.util.Map;
import com.l2jserver.gameserver.model.L2Spawn;
import com.l2jserver.gameserver.model.actor.L2Npc;
import com.l2jserver.util.Rnd;
/**
* This class holds parameter, specific to certain NPCs.<br>
* It can be either general parameters overridden for certain NPC instance instead of template parameters(aggro range, for example), or some optional parameters, handled by datapack scripts.<br>
* @author GKR
*/
public class NpcPersonalAIData
{
private final Map<String, Map<String, Integer>> _AIData = new HashMap<>();
/**
* Instantiates a new table.
*/
protected NpcPersonalAIData()
{
}
/**
* Stores data for given spawn.
* @param spawnDat spawn to process
* @param data Map of AI values
*/
public void storeData(L2Spawn spawnDat, Map<String, Integer> data)
{
if ((data != null) && !data.isEmpty())
{
// check for spawn name. Since spawn name is key for AI Data, generate random name, if spawn name isn't specified
if (spawnDat.getName() == null)
{
spawnDat.setName(Long.toString(Rnd.nextLong()));
}
_AIData.put(spawnDat.getName(), data);
}
}
/**
* Gets AI value with given spawnName and paramName
* @param spawnName spawn name to check
* @param paramName parameter to check
* @return value of given parameter for given spawn name
*/
public int getAIValue(String spawnName, String paramName)
{
return hasAIValue(spawnName, paramName) ? _AIData.get(spawnName).get(paramName) : -1;
}
/**
* Verifies if there is AI value with given spawnName and paramName
* @param spawnName spawn name to check
* @param paramName parameter name to check
* @return {@code true} if parameter paramName is set for spawn spawnName, {@code false} otherwise
*/
public boolean hasAIValue(String spawnName, String paramName)
{
return (spawnName != null) && _AIData.containsKey(spawnName) && _AIData.get(spawnName).containsKey(paramName);
}
/**
* Initializes npc parameters by specified values.
* @param npc NPC to process
* @param spawn link to NPC's spawn
* @param spawnName name of spawn
*/
public void initializeNpcParameters(L2Npc npc, L2Spawn spawn, String spawnName)
{
if (_AIData.containsKey(spawnName))
{
Map<String, Integer> map = _AIData.get(spawnName);
try
{
for (String key : map.keySet())
{
switch (key)
{
case "disableRandomAnimation":
npc.setRandomAnimationEnabled((map.get(key) == 0));
break;
case "disableRandomWalk":
npc.setIsNoRndWalk((map.get(key) == 1));
spawn.setIsNoRndWalk((map.get(key) == 1));
break;
}
}
}
catch (Exception e)
{
// Do nothing
}
}
}
/**
* Gets the single instance of NpcTable.
* @return single instance of NpcTable
*/
public static NpcPersonalAIData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final NpcPersonalAIData _instance = new NpcPersonalAIData();
}
}

View File

@@ -0,0 +1,281 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Calendar;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jserver.Config;
import com.l2jserver.L2DatabaseFactory;
import com.l2jserver.gameserver.LoginServerThread;
import com.l2jserver.gameserver.enums.PrivateStoreType;
import com.l2jserver.gameserver.model.L2ManufactureItem;
import com.l2jserver.gameserver.model.L2World;
import com.l2jserver.gameserver.model.TradeItem;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.network.L2GameClient;
import com.l2jserver.gameserver.network.L2GameClient.GameClientState;
public class OfflineTradersTable
{
private static Logger LOGGER = Logger.getLogger(OfflineTradersTable.class.getName());
// SQL DEFINITIONS
private static final String SAVE_OFFLINE_STATUS = "INSERT INTO character_offline_trade (`charId`,`time`,`type`,`title`) VALUES (?,?,?,?)";
private static final String SAVE_ITEMS = "INSERT INTO character_offline_trade_items (`charId`,`item`,`count`,`price`) VALUES (?,?,?,?)";
private static final String CLEAR_OFFLINE_TABLE = "DELETE FROM character_offline_trade";
private static final String CLEAR_OFFLINE_TABLE_ITEMS = "DELETE FROM character_offline_trade_items";
private static final String LOAD_OFFLINE_STATUS = "SELECT * FROM character_offline_trade";
private static final String LOAD_OFFLINE_ITEMS = "SELECT * FROM character_offline_trade_items WHERE charId = ?";
public void storeOffliners()
{
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement stm1 = con.prepareStatement(CLEAR_OFFLINE_TABLE);
PreparedStatement stm2 = con.prepareStatement(CLEAR_OFFLINE_TABLE_ITEMS);
PreparedStatement stm3 = con.prepareStatement(SAVE_OFFLINE_STATUS);
PreparedStatement stm_items = con.prepareStatement(SAVE_ITEMS))
{
stm1.execute();
stm2.execute();
con.setAutoCommit(false); // avoid halfway done
for (L2PcInstance pc : L2World.getInstance().getPlayers())
{
try
{
if ((pc.getPrivateStoreType() != PrivateStoreType.NONE) && ((pc.getClient() == null) || pc.getClient().isDetached()))
{
stm3.setInt(1, pc.getObjectId()); // Char Id
stm3.setLong(2, pc.getOfflineStartTime());
stm3.setInt(3, pc.getPrivateStoreType().getId()); // store type
String title = null;
switch (pc.getPrivateStoreType())
{
case BUY:
if (!Config.OFFLINE_TRADE_ENABLE)
{
continue;
}
title = pc.getBuyList().getTitle();
for (TradeItem i : pc.getBuyList().getItems())
{
stm_items.setInt(1, pc.getObjectId());
stm_items.setInt(2, i.getItem().getId());
stm_items.setLong(3, i.getCount());
stm_items.setLong(4, i.getPrice());
stm_items.executeUpdate();
stm_items.clearParameters();
}
break;
case SELL:
case PACKAGE_SELL:
if (!Config.OFFLINE_TRADE_ENABLE)
{
continue;
}
title = pc.getSellList().getTitle();
for (TradeItem i : pc.getSellList().getItems())
{
stm_items.setInt(1, pc.getObjectId());
stm_items.setInt(2, i.getObjectId());
stm_items.setLong(3, i.getCount());
stm_items.setLong(4, i.getPrice());
stm_items.executeUpdate();
stm_items.clearParameters();
}
break;
case MANUFACTURE:
if (!Config.OFFLINE_CRAFT_ENABLE)
{
continue;
}
title = pc.getStoreName();
for (L2ManufactureItem i : pc.getManufactureItems().values())
{
stm_items.setInt(1, pc.getObjectId());
stm_items.setInt(2, i.getRecipeId());
stm_items.setLong(3, 0);
stm_items.setLong(4, i.getCost());
stm_items.executeUpdate();
stm_items.clearParameters();
}
}
stm3.setString(4, title);
stm3.executeUpdate();
stm3.clearParameters();
con.commit(); // flush
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Error while saving offline trader: " + pc.getObjectId() + " " + e, e);
}
}
LOGGER.info(getClass().getSimpleName() + ": Offline traders stored.");
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Error while saving offline traders: " + e, e);
}
}
public void restoreOfflineTraders()
{
LOGGER.info(getClass().getSimpleName() + ": Loading offline traders...");
int nTraders = 0;
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
Statement stm = con.createStatement();
ResultSet rs = stm.executeQuery(LOAD_OFFLINE_STATUS))
{
while (rs.next())
{
long time = rs.getLong("time");
if (Config.OFFLINE_MAX_DAYS > 0)
{
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(time);
cal.add(Calendar.DAY_OF_YEAR, Config.OFFLINE_MAX_DAYS);
if (cal.getTimeInMillis() <= System.currentTimeMillis())
{
continue;
}
}
PrivateStoreType type = PrivateStoreType.findById(rs.getInt("type"));
if (type == null)
{
LOGGER.warning(getClass().getSimpleName() + ": PrivateStoreType with id " + rs.getInt("type") + " could not be found.");
continue;
}
if (type == PrivateStoreType.NONE)
{
continue;
}
L2PcInstance player = null;
try
{
L2GameClient client = new L2GameClient(null);
client.setDetached(true);
player = L2PcInstance.load(rs.getInt("charId"));
client.setActiveChar(player);
player.setOnlineStatus(true, false);
client.setAccountName(player.getAccountNamePlayer());
client.setState(GameClientState.IN_GAME);
player.setClient(client);
player.setOfflineStartTime(time);
player.spawnMe(player.getX(), player.getY(), player.getZ());
LoginServerThread.getInstance().addGameServerLogin(player.getAccountName(), client);
try (PreparedStatement stm_items = con.prepareStatement(LOAD_OFFLINE_ITEMS))
{
stm_items.setInt(1, player.getObjectId());
try (ResultSet items = stm_items.executeQuery())
{
switch (type)
{
case BUY:
while (items.next())
{
if (player.getBuyList().addItemByItemId(items.getInt(2), items.getLong(3), items.getLong(4)) == null)
{
throw new NullPointerException();
}
}
player.getBuyList().setTitle(rs.getString("title"));
break;
case SELL:
case PACKAGE_SELL:
while (items.next())
{
if (player.getSellList().addItem(items.getInt(2), items.getLong(3), items.getLong(4)) == null)
{
throw new NullPointerException();
}
}
player.getSellList().setTitle(rs.getString("title"));
player.getSellList().setPackaged(type == PrivateStoreType.PACKAGE_SELL);
break;
case MANUFACTURE:
while (items.next())
{
player.getManufactureItems().put(items.getInt(2), new L2ManufactureItem(items.getInt(2), items.getLong(4)));
}
player.setStoreName(rs.getString("title"));
break;
}
}
}
player.sitDown();
if (Config.OFFLINE_SET_NAME_COLOR)
{
player.getAppearance().setNameColor(Config.OFFLINE_NAME_COLOR);
}
player.setPrivateStoreType(type);
player.setOnlineStatus(true, true);
player.restoreEffects();
player.broadcastUserInfo();
nTraders++;
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Error loading trader: " + player, e);
if (player != null)
{
player.deleteMe();
}
}
}
LOGGER.info(getClass().getSimpleName() + ": Loaded: " + nTraders + " offline trader(s)");
try (Statement stm1 = con.createStatement())
{
stm1.execute(CLEAR_OFFLINE_TABLE);
stm1.execute(CLEAR_OFFLINE_TABLE_ITEMS);
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Error while loading offline traders: ", e);
}
}
/**
* Gets the single instance of OfflineTradersTable.
* @return single instance of OfflineTradersTable
*/
public static OfflineTradersTable getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final OfflineTradersTable _instance = new OfflineTradersTable();
}
}

View File

@@ -0,0 +1,164 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.model.holders.SkillHolder;
import com.l2jserver.gameserver.model.options.Options;
import com.l2jserver.gameserver.model.options.OptionsSkillHolder;
import com.l2jserver.gameserver.model.options.OptionsSkillType;
import com.l2jserver.gameserver.model.stats.Stats;
import com.l2jserver.gameserver.model.stats.functions.FuncTemplate;
/**
* @author UnAfraid
*/
public class OptionsData implements DocumentParser
{
private final Map<Integer, Options> _data = new HashMap<>();
protected OptionsData()
{
load();
}
@Override
public synchronized void load()
{
_data.clear();
parseDatapackDirectory("data/stats/options", false);
LOGGER.log(Level.INFO, getClass().getSimpleName() + ": Loaded: " + _data.size() + " Options.");
}
@Override
public void parseDocument(Document doc)
{
int id;
Options op;
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("option".equalsIgnoreCase(d.getNodeName()))
{
id = parseInteger(d.getAttributes(), "id");
op = new Options(id);
for (Node cd = d.getFirstChild(); cd != null; cd = cd.getNextSibling())
{
switch (cd.getNodeName())
{
case "for":
{
for (Node fd = cd.getFirstChild(); fd != null; fd = fd.getNextSibling())
{
switch (fd.getNodeName())
{
case "add":
case "sub":
case "mul":
case "div":
case "set":
case "share":
case "enchant":
case "enchanthp":
{
parseFuncs(fd.getAttributes(), fd.getNodeName(), op);
}
}
}
break;
}
case "active_skill":
{
op.setActiveSkill(new SkillHolder(parseInteger(cd.getAttributes(), "id"), parseInteger(cd.getAttributes(), "level")));
break;
}
case "passive_skill":
{
op.setPassiveSkill(new SkillHolder(parseInteger(cd.getAttributes(), "id"), parseInteger(cd.getAttributes(), "level")));
break;
}
case "attack_skill":
{
op.addActivationSkill(new OptionsSkillHolder(parseInteger(cd.getAttributes(), "id"), parseInteger(cd.getAttributes(), "level"), parseDouble(cd.getAttributes(), "chance"), OptionsSkillType.ATTACK));
break;
}
case "magic_skill":
{
op.addActivationSkill(new OptionsSkillHolder(parseInteger(cd.getAttributes(), "id"), parseInteger(cd.getAttributes(), "level"), parseDouble(cd.getAttributes(), "chance"), OptionsSkillType.MAGIC));
break;
}
case "critical_skill":
{
op.addActivationSkill(new OptionsSkillHolder(parseInteger(cd.getAttributes(), "id"), parseInteger(cd.getAttributes(), "level"), parseDouble(cd.getAttributes(), "chance"), OptionsSkillType.CRITICAL));
break;
}
}
}
_data.put(op.getId(), op);
}
}
}
}
}
private void parseFuncs(NamedNodeMap attrs, String functionName, Options op)
{
Stats stat = Stats.valueOfXml(parseString(attrs, "stat"));
double val = parseDouble(attrs, "val");
int order = -1;
final Node orderNode = attrs.getNamedItem("order");
if (orderNode != null)
{
order = Integer.parseInt(orderNode.getNodeValue());
}
op.addFunc(new FuncTemplate(null, null, functionName, order, stat, val));
}
public Options getOptions(int id)
{
return _data.get(id);
}
/**
* Gets the single instance of OptionsData.
* @return single instance of OptionsData
*/
public static final OptionsData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final OptionsData _instance = new OptionsData();
}
}

View File

@@ -0,0 +1,243 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.HashMap;
import java.util.Map;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.enums.MountType;
import com.l2jserver.gameserver.model.L2PetData;
import com.l2jserver.gameserver.model.L2PetLevelData;
import com.l2jserver.gameserver.model.StatsSet;
/**
* This class parse and hold all pet parameters.<br>
* TODO: load and use all pet parameters.
* @author Zoey76 (rework)
*/
public final class PetDataTable implements DocumentParser
{
private static final Map<Integer, L2PetData> _pets = new HashMap<>();
/**
* Instantiates a new pet data table.
*/
protected PetDataTable()
{
load();
}
@Override
public void load()
{
_pets.clear();
parseDatapackDirectory("data/stats/pets", false);
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _pets.size() + " Pets.");
}
@Override
public void parseDocument(Document doc)
{
NamedNodeMap attrs;
Node n = doc.getFirstChild();
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if (d.getNodeName().equals("pet"))
{
int npcId = parseInteger(d.getAttributes(), "id");
int itemId = parseInteger(d.getAttributes(), "itemId");
// index ignored for now
L2PetData data = new L2PetData(npcId, itemId);
for (Node p = d.getFirstChild(); p != null; p = p.getNextSibling())
{
if (p.getNodeName().equals("set"))
{
attrs = p.getAttributes();
String type = attrs.getNamedItem("name").getNodeValue();
if ("food".equals(type))
{
for (String foodId : attrs.getNamedItem("val").getNodeValue().split(";"))
{
data.addFood(Integer.valueOf(foodId));
}
}
else if ("load".equals(type))
{
data.setLoad(parseInteger(attrs, "val"));
}
else if ("hungry_limit".equals(type))
{
data.setHungryLimit(parseInteger(attrs, "val"));
}
else if ("sync_level".equals(type))
{
data.setSyncLevel(parseInteger(attrs, "val") == 1);
}
// evolve ignored
}
else if (p.getNodeName().equals("skills"))
{
for (Node s = p.getFirstChild(); s != null; s = s.getNextSibling())
{
if (s.getNodeName().equals("skill"))
{
attrs = s.getAttributes();
data.addNewSkill(parseInteger(attrs, "skillId"), parseInteger(attrs, "skillLvl"), parseInteger(attrs, "minLvl"));
}
}
}
else if (p.getNodeName().equals("stats"))
{
for (Node s = p.getFirstChild(); s != null; s = s.getNextSibling())
{
if (s.getNodeName().equals("stat"))
{
final int level = Integer.parseInt(s.getAttributes().getNamedItem("level").getNodeValue());
final StatsSet set = new StatsSet();
for (Node bean = s.getFirstChild(); bean != null; bean = bean.getNextSibling())
{
if (bean.getNodeName().equals("set"))
{
attrs = bean.getAttributes();
if (attrs.getNamedItem("name").getNodeValue().equals("speed_on_ride"))
{
set.set("walkSpeedOnRide", attrs.getNamedItem("walk").getNodeValue());
set.set("runSpeedOnRide", attrs.getNamedItem("run").getNodeValue());
set.set("slowSwimSpeedOnRide", attrs.getNamedItem("slowSwim").getNodeValue());
set.set("fastSwimSpeedOnRide", attrs.getNamedItem("fastSwim").getNodeValue());
if (attrs.getNamedItem("slowFly") != null)
{
set.set("slowFlySpeedOnRide", attrs.getNamedItem("slowFly").getNodeValue());
}
if (attrs.getNamedItem("fastFly") != null)
{
set.set("fastFlySpeedOnRide", attrs.getNamedItem("fastFly").getNodeValue());
}
}
else
{
set.set(attrs.getNamedItem("name").getNodeValue(), attrs.getNamedItem("val").getNodeValue());
}
}
}
data.addNewStat(level, new L2PetLevelData(set));
}
}
}
}
_pets.put(npcId, data);
}
}
}
/**
* @param itemId
* @return
*/
public L2PetData getPetDataByItemId(int itemId)
{
for (L2PetData data : _pets.values())
{
if (data.getItemId() == itemId)
{
return data;
}
}
return null;
}
/**
* Gets the pet level data.
* @param petId the pet Id.
* @param petLevel the pet level.
* @return the pet's parameters for the given Id and level.
*/
public L2PetLevelData getPetLevelData(int petId, int petLevel)
{
final L2PetData pd = getPetData(petId);
if (pd != null)
{
return pd.getPetLevelData(petLevel);
}
return null;
}
/**
* Gets the pet data.
* @param petId the pet Id.
* @return the pet data
*/
public L2PetData getPetData(int petId)
{
if (!_pets.containsKey(petId))
{
LOGGER.info(getClass().getSimpleName() + ": Missing pet data for npcid: " + petId);
}
return _pets.get(petId);
}
/**
* Gets the pet min level.
* @param petId the pet Id.
* @return the pet min level
*/
public int getPetMinLevel(int petId)
{
return _pets.get(petId).getMinLevel();
}
/**
* Gets the pet items by npc.
* @param npcId the NPC ID to get its summoning item
* @return summoning item for the given NPC ID
*/
public static int getPetItemsByNpc(int npcId)
{
return _pets.get(npcId).getItemId();
}
/**
* Checks if is mountable.
* @param npcId the NPC Id to verify.
* @return {@code true} if the given Id is from a mountable pet, {@code false} otherwise.
*/
public static boolean isMountable(int npcId)
{
return MountType.findByNpcId(npcId) != MountType.NONE;
}
/**
* Gets the single instance of PetDataTable.
* @return this class unique instance.
*/
public static PetDataTable getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final PetDataTable _instance = new PetDataTable();
}
}

View File

@@ -0,0 +1,116 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import com.l2jserver.Config;
import com.l2jserver.L2DatabaseFactory;
public class PetNameTable
{
private static Logger LOGGER = Logger.getLogger(PetNameTable.class.getName());
public static PetNameTable getInstance()
{
return SingletonHolder._instance;
}
public boolean doesPetNameExist(String name, int petNpcId)
{
boolean result = true;
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement ps = con.prepareStatement("SELECT name FROM pets p, items i WHERE p.item_obj_id = i.object_id AND name=? AND i.item_id IN (?)"))
{
ps.setString(1, name);
StringBuilder cond = new StringBuilder();
if (!cond.toString().isEmpty())
{
cond.append(", ");
}
cond.append(PetDataTable.getPetItemsByNpc(petNpcId));
ps.setString(2, cond.toString());
try (ResultSet rs = ps.executeQuery())
{
result = rs.next();
}
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Could not check existing petname:" + e.getMessage(), e);
}
return result;
}
public boolean isValidPetName(String name)
{
boolean result = true;
if (!isAlphaNumeric(name))
{
return result;
}
Pattern pattern;
try
{
pattern = Pattern.compile(Config.PET_NAME_TEMPLATE);
}
catch (PatternSyntaxException e) // case of illegal pattern
{
LOGGER.warning(getClass().getSimpleName() + ": Pet name pattern of config is wrong!");
pattern = Pattern.compile(".*");
}
Matcher regexp = pattern.matcher(name);
if (!regexp.matches())
{
result = false;
}
return result;
}
private boolean isAlphaNumeric(String text)
{
boolean result = true;
char[] chars = text.toCharArray();
for (int i = 0; i < chars.length; i++)
{
if (!Character.isLetterOrDigit(chars[i]))
{
result = false;
break;
}
}
return result;
}
private static class SingletonHolder
{
protected static final PetNameTable _instance = new PetNameTable();
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.Arrays;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
/**
* This class holds the Player Xp Percent Lost Data for each level for players.
* @author Zealar
*/
public final class PlayerXpPercentLostData implements DocumentParser
{
private final int _maxlevel = ExperienceTable.getInstance().getMaxLevel();
private final double[] _playerXpPercentLost = new double[_maxlevel + 1];
protected PlayerXpPercentLostData()
{
Arrays.fill(_playerXpPercentLost, 1.);
load();
}
@Override
public void load()
{
parseDatapackFile("data/stats/chars/playerXpPercentLost.xml");
}
@Override
public void parseDocument(Document doc)
{
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("xpLost".equalsIgnoreCase(d.getNodeName()))
{
NamedNodeMap attrs = d.getAttributes();
_playerXpPercentLost[parseInteger(attrs, "level")] = parseDouble(attrs, "val");
}
}
}
}
}
public double getXpPercent(final int level)
{
if (level > _maxlevel)
{
LOGGER.warning("Require to high level inside PlayerXpPercentLostData (" + level + ")");
return _playerXpPercentLost[_maxlevel];
}
return _playerXpPercentLost[level];
}
/**
* Gets the single instance of PlayerXpPercentLostData.
* @return single instance of PlayerXpPercentLostData.
*/
public static PlayerXpPercentLostData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final PlayerXpPercentLostData _instance = new PlayerXpPercentLostData();
}
}

View File

@@ -0,0 +1,281 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.model.L2RecipeInstance;
import com.l2jserver.gameserver.model.L2RecipeList;
import com.l2jserver.gameserver.model.L2RecipeStatInstance;
import com.l2jserver.gameserver.model.StatsSet;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
/**
* The Class RecipeData.
* @author Zoey76
*/
public class RecipeData implements DocumentParser
{
private static final Map<Integer, L2RecipeList> _recipes = new HashMap<>();
/**
* Instantiates a new recipe data.
*/
protected RecipeData()
{
load();
}
@Override
public void load()
{
_recipes.clear();
parseDatapackFile("data/recipes.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _recipes.size() + " recipes.");
}
@Override
public void parseDocument(Document doc)
{
// TODO: Cleanup checks enforced by XSD.
final List<L2RecipeInstance> recipePartList = new ArrayList<>();
final List<L2RecipeStatInstance> recipeStatUseList = new ArrayList<>();
final List<L2RecipeStatInstance> recipeAltStatChangeList = new ArrayList<>();
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
RECIPES_FILE: for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("item".equalsIgnoreCase(d.getNodeName()))
{
recipePartList.clear();
recipeStatUseList.clear();
recipeAltStatChangeList.clear();
NamedNodeMap attrs = d.getAttributes();
Node att;
int id = -1;
boolean haveRare = false;
StatsSet set = new StatsSet();
att = attrs.getNamedItem("id");
if (att == null)
{
LOGGER.severe(getClass().getSimpleName() + ": Missing id for recipe item, skipping");
continue;
}
id = Integer.parseInt(att.getNodeValue());
set.set("id", id);
att = attrs.getNamedItem("recipeId");
if (att == null)
{
LOGGER.severe(getClass().getSimpleName() + ": Missing recipeId for recipe item id: " + id + ", skipping");
continue;
}
set.set("recipeId", Integer.parseInt(att.getNodeValue()));
att = attrs.getNamedItem("name");
if (att == null)
{
LOGGER.severe(getClass().getSimpleName() + ": Missing name for recipe item id: " + id + ", skipping");
continue;
}
set.set("recipeName", att.getNodeValue());
att = attrs.getNamedItem("craftLevel");
if (att == null)
{
LOGGER.severe(getClass().getSimpleName() + ": Missing level for recipe item id: " + id + ", skipping");
continue;
}
set.set("craftLevel", Integer.parseInt(att.getNodeValue()));
att = attrs.getNamedItem("type");
if (att == null)
{
LOGGER.severe(getClass().getSimpleName() + ": Missing type for recipe item id: " + id + ", skipping");
continue;
}
set.set("isDwarvenRecipe", att.getNodeValue().equalsIgnoreCase("dwarven"));
att = attrs.getNamedItem("successRate");
if (att == null)
{
LOGGER.severe(getClass().getSimpleName() + ": Missing successRate for recipe item id: " + id + ", skipping");
continue;
}
set.set("successRate", Integer.parseInt(att.getNodeValue()));
for (Node c = d.getFirstChild(); c != null; c = c.getNextSibling())
{
if ("statUse".equalsIgnoreCase(c.getNodeName()))
{
String statName = c.getAttributes().getNamedItem("name").getNodeValue();
int value = Integer.parseInt(c.getAttributes().getNamedItem("value").getNodeValue());
try
{
recipeStatUseList.add(new L2RecipeStatInstance(statName, value));
}
catch (Exception e)
{
LOGGER.severe(getClass().getSimpleName() + ": Error in StatUse parameter for recipe item id: " + id + ", skipping");
continue RECIPES_FILE;
}
}
else if ("altStatChange".equalsIgnoreCase(c.getNodeName()))
{
String statName = c.getAttributes().getNamedItem("name").getNodeValue();
int value = Integer.parseInt(c.getAttributes().getNamedItem("value").getNodeValue());
try
{
recipeAltStatChangeList.add(new L2RecipeStatInstance(statName, value));
}
catch (Exception e)
{
LOGGER.severe(getClass().getSimpleName() + ": Error in AltStatChange parameter for recipe item id: " + id + ", skipping");
continue RECIPES_FILE;
}
}
else if ("ingredient".equalsIgnoreCase(c.getNodeName()))
{
int ingId = Integer.parseInt(c.getAttributes().getNamedItem("id").getNodeValue());
int ingCount = Integer.parseInt(c.getAttributes().getNamedItem("count").getNodeValue());
recipePartList.add(new L2RecipeInstance(ingId, ingCount));
}
else if ("production".equalsIgnoreCase(c.getNodeName()))
{
set.set("itemId", Integer.parseInt(c.getAttributes().getNamedItem("id").getNodeValue()));
set.set("count", Integer.parseInt(c.getAttributes().getNamedItem("count").getNodeValue()));
}
else if ("productionRare".equalsIgnoreCase(c.getNodeName()))
{
set.set("rareItemId", Integer.parseInt(c.getAttributes().getNamedItem("id").getNodeValue()));
set.set("rareCount", Integer.parseInt(c.getAttributes().getNamedItem("count").getNodeValue()));
set.set("rarity", Integer.parseInt(c.getAttributes().getNamedItem("rarity").getNodeValue()));
haveRare = true;
}
}
L2RecipeList recipeList = new L2RecipeList(set, haveRare);
for (L2RecipeInstance recipePart : recipePartList)
{
recipeList.addRecipe(recipePart);
}
for (L2RecipeStatInstance recipeStatUse : recipeStatUseList)
{
recipeList.addStatUse(recipeStatUse);
}
for (L2RecipeStatInstance recipeAltStatChange : recipeAltStatChangeList)
{
recipeList.addAltStatChange(recipeAltStatChange);
}
_recipes.put(id, recipeList);
}
}
}
}
}
/**
* Gets the recipe list.
* @param listId the list id
* @return the recipe list
*/
public L2RecipeList getRecipeList(int listId)
{
return _recipes.get(listId);
}
/**
* Gets the recipe by item id.
* @param itemId the item id
* @return the recipe by item id
*/
public L2RecipeList getRecipeByItemId(int itemId)
{
for (L2RecipeList find : _recipes.values())
{
if (find.getRecipeId() == itemId)
{
return find;
}
}
return null;
}
/**
* Gets the all item ids.
* @return the all item ids
*/
public int[] getAllItemIds()
{
int[] idList = new int[_recipes.size()];
int i = 0;
for (L2RecipeList rec : _recipes.values())
{
idList[i++] = rec.getRecipeId();
}
return idList;
}
/**
* Gets the valid recipe list.
* @param player the player
* @param id the recipe list id
* @return the valid recipe list
*/
public L2RecipeList getValidRecipeList(L2PcInstance player, int id)
{
L2RecipeList recipeList = _recipes.get(id);
if ((recipeList == null) || (recipeList.getRecipes().length == 0))
{
player.sendMessage(getClass().getSimpleName() + ": No recipe for: " + id);
player.isInCraftMode(false);
return null;
}
return recipeList;
}
/**
* Gets the single instance of RecipeData.
* @return single instance of RecipeData
*/
public static RecipeData getInstance()
{
return SingletonHolder._instance;
}
/**
* The Class SingletonHolder.
*/
private static class SingletonHolder
{
protected static final RecipeData _instance = new RecipeData();
}
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.io.File;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
/**
* @author NosBit
*/
public class SecondaryAuthData implements DocumentParser
{
private boolean _enabled = false;
private int _maxAttempts = 5;
private int _banTime = 480;
private String _recoveryLink = "";
private final Set<String> _forbiddenPasswords = new HashSet<>();
protected SecondaryAuthData()
{
load();
}
@Override
public synchronized void load()
{
_forbiddenPasswords.clear();
parseFile(new File("config/SecondaryAuth.xml"));
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _forbiddenPasswords.size() + " forbidden passwords.");
}
@Override
public void parseDocument(Document doc)
{
try
{
for (Node node = doc.getFirstChild(); node != null; node = node.getNextSibling())
{
if ("list".equalsIgnoreCase(node.getNodeName()))
{
for (Node list_node = node.getFirstChild(); list_node != null; list_node = list_node.getNextSibling())
{
if ("enabled".equalsIgnoreCase(list_node.getNodeName()))
{
_enabled = Boolean.parseBoolean(list_node.getTextContent());
}
else if ("maxAttempts".equalsIgnoreCase(list_node.getNodeName()))
{
_maxAttempts = Integer.parseInt(list_node.getTextContent());
}
else if ("banTime".equalsIgnoreCase(list_node.getNodeName()))
{
_banTime = Integer.parseInt(list_node.getTextContent());
}
else if ("recoveryLink".equalsIgnoreCase(list_node.getNodeName()))
{
_recoveryLink = list_node.getTextContent();
}
else if ("forbiddenPasswords".equalsIgnoreCase(list_node.getNodeName()))
{
for (Node forbiddenPasswords_node = list_node.getFirstChild(); forbiddenPasswords_node != null; forbiddenPasswords_node = forbiddenPasswords_node.getNextSibling())
{
if ("password".equalsIgnoreCase(forbiddenPasswords_node.getNodeName()))
{
_forbiddenPasswords.add(forbiddenPasswords_node.getTextContent());
}
}
}
}
}
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Failed to load secondary auth data from xml.", e);
}
}
public boolean isEnabled()
{
return _enabled;
}
public int getMaxAttempts()
{
return _maxAttempts;
}
public int getBanTime()
{
return _banTime;
}
public String getRecoveryLink()
{
return _recoveryLink;
}
public Set<String> getForbiddenPasswords()
{
return _forbiddenPasswords;
}
public boolean isForbiddenPassword(String password)
{
return _forbiddenPasswords.contains(password);
}
public static SecondaryAuthData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final SecondaryAuthData _instance = new SecondaryAuthData();
}
}

View File

@@ -0,0 +1,202 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.idfactory.IdFactory;
import com.l2jserver.gameserver.model.Location;
import com.l2jserver.gameserver.model.StatsSet;
import com.l2jserver.gameserver.model.VehiclePathPoint;
import com.l2jserver.gameserver.model.actor.instance.L2ShuttleInstance;
import com.l2jserver.gameserver.model.actor.templates.L2CharTemplate;
import com.l2jserver.gameserver.model.shuttle.L2ShuttleData;
import com.l2jserver.gameserver.model.shuttle.L2ShuttleEngine;
import com.l2jserver.gameserver.model.shuttle.L2ShuttleStop;
/**
* @author UnAfraid
*/
public final class ShuttleData implements DocumentParser
{
private final Map<Integer, L2ShuttleData> _shuttles = new HashMap<>();
private final Map<Integer, L2ShuttleInstance> _shuttleInstances = new HashMap<>();
protected ShuttleData()
{
load();
}
@Override
public synchronized void load()
{
if (!_shuttleInstances.isEmpty())
{
for (L2ShuttleInstance shuttle : _shuttleInstances.values())
{
shuttle.deleteMe();
}
_shuttleInstances.clear();
}
parseDatapackFile("data/shuttledata.xml");
init();
LOGGER.log(Level.INFO, getClass().getSimpleName() + ": Loaded: " + _shuttles.size() + " Shuttles.");
}
@Override
public void parseDocument(Document doc)
{
NamedNodeMap attrs;
StatsSet set;
Node att;
L2ShuttleData data;
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("shuttle".equalsIgnoreCase(d.getNodeName()))
{
attrs = d.getAttributes();
set = new StatsSet();
for (int i = 0; i < attrs.getLength(); i++)
{
att = attrs.item(i);
set.set(att.getNodeName(), att.getNodeValue());
}
data = new L2ShuttleData(set);
for (Node b = d.getFirstChild(); b != null; b = b.getNextSibling())
{
if ("doors".equalsIgnoreCase(b.getNodeName()))
{
for (Node a = b.getFirstChild(); a != null; a = a.getNextSibling())
{
if ("door".equalsIgnoreCase(a.getNodeName()))
{
attrs = a.getAttributes();
data.addDoor(parseInteger(attrs, "id"));
}
}
}
else if ("stops".equalsIgnoreCase(b.getNodeName()))
{
for (Node a = b.getFirstChild(); a != null; a = a.getNextSibling())
{
if ("stop".equalsIgnoreCase(a.getNodeName()))
{
attrs = a.getAttributes();
L2ShuttleStop stop = new L2ShuttleStop(parseInteger(attrs, "id"));
for (Node z = a.getFirstChild(); z != null; z = z.getNextSibling())
{
if ("dimension".equalsIgnoreCase(z.getNodeName()))
{
attrs = z.getAttributes();
stop.addDimension(new Location(parseInteger(attrs, "x"), parseInteger(attrs, "y"), parseInteger(attrs, "z")));
}
}
data.addStop(stop);
}
}
}
else if ("routes".equalsIgnoreCase(b.getNodeName()))
{
for (Node a = b.getFirstChild(); a != null; a = a.getNextSibling())
{
if ("route".equalsIgnoreCase(a.getNodeName()))
{
attrs = a.getAttributes();
List<Location> locs = new ArrayList<>();
for (Node z = a.getFirstChild(); z != null; z = z.getNextSibling())
{
if ("loc".equalsIgnoreCase(z.getNodeName()))
{
attrs = z.getAttributes();
locs.add(new Location(parseInteger(attrs, "x"), parseInteger(attrs, "y"), parseInteger(attrs, "z")));
}
}
VehiclePathPoint[] route = new VehiclePathPoint[locs.size()];
int i = 0;
for (Location loc : locs)
{
route[i++] = new VehiclePathPoint(loc);
}
data.addRoute(route);
}
}
}
}
_shuttles.put(data.getId(), data);
}
}
}
}
}
private void init()
{
for (L2ShuttleData data : _shuttles.values())
{
final L2ShuttleInstance shuttle = new L2ShuttleInstance(IdFactory.getInstance().getNextId(), new L2CharTemplate(new StatsSet()));
shuttle.setData(data);
shuttle.setHeading(data.getLocation().getHeading());
shuttle.setLocationInvisible(data.getLocation());
shuttle.spawnMe();
shuttle.getStat().setMoveSpeed(300);
shuttle.getStat().setRotationSpeed(0);
shuttle.registerEngine(new L2ShuttleEngine(data, shuttle));
shuttle.runEngine(1000);
_shuttleInstances.put(shuttle.getObjectId(), shuttle);
}
}
public L2ShuttleInstance getShuttle(int id)
{
for (L2ShuttleInstance shuttle : _shuttleInstances.values())
{
if ((shuttle.getObjectId() == id) || (shuttle.getId() == id))
{
return shuttle;
}
}
return null;
}
public static ShuttleData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final ShuttleData _instance = new ShuttleData();
}
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.logging.Level;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.model.SiegeScheduleDate;
import com.l2jserver.gameserver.model.StatsSet;
import com.l2jserver.gameserver.util.Util;
/**
* @author UnAfraid
*/
public class SiegeScheduleData implements DocumentParser
{
private final List<SiegeScheduleDate> _scheduleData = new ArrayList<>();
protected SiegeScheduleData()
{
load();
}
@Override
public synchronized void load()
{
_scheduleData.clear();
parseDatapackFile("config/SiegeSchedule.xml");
LOGGER.log(Level.INFO, getClass().getSimpleName() + ": Loaded: " + _scheduleData.size() + " siege schedulers.");
if (_scheduleData.isEmpty())
{
_scheduleData.add(new SiegeScheduleDate(new StatsSet()));
LOGGER.log(Level.INFO, getClass().getSimpleName() + ": Emergency Loaded: " + _scheduleData.size() + " default siege schedulers.");
}
}
@Override
public void parseDocument(Document doc)
{
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node cd = n.getFirstChild(); cd != null; cd = cd.getNextSibling())
{
switch (cd.getNodeName())
{
case "schedule":
{
final StatsSet set = new StatsSet();
final NamedNodeMap attrs = cd.getAttributes();
for (int i = 0; i < attrs.getLength(); i++)
{
Node node = attrs.item(i);
String key = node.getNodeName();
String val = node.getNodeValue();
if ("day".equals(key))
{
if (!Util.isDigit(val))
{
val = Integer.toString(getValueForField(val));
}
}
set.set(key, val);
}
_scheduleData.add(new SiegeScheduleDate(set));
break;
}
}
}
}
}
}
private int getValueForField(String field)
{
try
{
return Calendar.class.getField(field).getInt(Calendar.class);
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "", e);
return -1;
}
}
public List<SiegeScheduleDate> getScheduleDates()
{
return _scheduleData;
}
public static final SiegeScheduleData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final SiegeScheduleData _instance = new SiegeScheduleData();
}
}

View File

@@ -0,0 +1,181 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jserver.Config;
import com.l2jserver.gameserver.engines.DocumentEngine;
import com.l2jserver.gameserver.model.skills.Skill;
/**
* Skill data.
*/
public final class SkillData
{
private static Logger LOGGER = Logger.getLogger(SkillData.class.getName());
private final Map<Integer, Skill> _skills = new HashMap<>();
private final Map<Integer, Integer> _skillMaxLevel = new HashMap<>();
private final Set<Integer> _enchantable = new HashSet<>();
protected SkillData()
{
load();
}
public void reload()
{
load();
// Reload Skill Tree as well.
SkillTreesData.getInstance().load();
}
private void load()
{
final Map<Integer, Skill> _temp = new HashMap<>();
DocumentEngine.getInstance().loadAllSkills(_temp);
_skills.clear();
_skills.putAll(_temp);
_skillMaxLevel.clear();
_enchantable.clear();
for (Skill skill : _skills.values())
{
final int skillId = skill.getId();
final int skillLvl = skill.getLevel();
if (skillLvl > 99)
{
if (!_enchantable.contains(skillId))
{
_enchantable.add(skillId);
}
continue;
}
// only non-enchanted skills
final int maxLvl = getMaxLevel(skillId);
if (skillLvl > maxLvl)
{
_skillMaxLevel.put(skillId, skillLvl);
}
}
}
/**
* Provides the skill hash
* @param skill The L2Skill to be hashed
* @return getSkillHashCode(skill.getId(), skill.getLevel())
*/
public static int getSkillHashCode(Skill skill)
{
return getSkillHashCode(skill.getId(), skill.getLevel());
}
/**
* Centralized method for easier change of the hashing sys
* @param skillId The Skill Id
* @param skillLevel The Skill Level
* @return The Skill hash number
*/
public static int getSkillHashCode(int skillId, int skillLevel)
{
return (skillId * 1021) + skillLevel;
}
public Skill getSkill(int skillId, int level)
{
final Skill result = _skills.get(getSkillHashCode(skillId, level));
if (result != null)
{
return result;
}
// skill/level not found, fix for transformation scripts
final int maxLvl = getMaxLevel(skillId);
// requested level too high
if ((maxLvl > 0) && (level > maxLvl))
{
if (Config.DEBUG)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": call to unexisting skill level id: " + skillId + " requested level: " + level + " max level: " + maxLvl, new Throwable());
}
return _skills.get(getSkillHashCode(skillId, maxLvl));
}
LOGGER.warning(getClass().getSimpleName() + ": No skill info found for skill id " + skillId + " and skill level " + level + ".");
return null;
}
public int getMaxLevel(int skillId)
{
final Integer maxLevel = _skillMaxLevel.get(skillId);
return maxLevel != null ? maxLevel : 0;
}
/**
* Verifies if the given skill ID correspond to an enchantable skill.
* @param skillId the skill ID
* @return {@code true} if the skill is enchantable, {@code false} otherwise
*/
public boolean isEnchantable(int skillId)
{
return _enchantable.contains(skillId);
}
/**
* @param addNoble
* @param hasCastle
* @return an array with siege skills. If addNoble == true, will add also Advanced headquarters.
*/
public Skill[] getSiegeSkills(boolean addNoble, boolean hasCastle)
{
Skill[] temp = new Skill[2 + (addNoble ? 1 : 0) + (hasCastle ? 2 : 0)];
int i = 0;
temp[i++] = _skills.get(SkillData.getSkillHashCode(246, 1));
temp[i++] = _skills.get(SkillData.getSkillHashCode(247, 1));
if (addNoble)
{
temp[i++] = _skills.get(SkillData.getSkillHashCode(326, 1));
}
if (hasCastle)
{
temp[i++] = _skills.get(SkillData.getSkillHashCode(844, 1));
temp[i++] = _skills.get(SkillData.getSkillHashCode(845, 1));
}
return temp;
}
public static SkillData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final SkillData _instance = new SkillData();
}
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.model.base.ClassId;
/**
* Holds all skill learn data for all npcs.
* @author xban1x
*/
public final class SkillLearnData implements DocumentParser
{
private final Map<Integer, List<ClassId>> _skillLearn = new HashMap<>();
protected SkillLearnData()
{
load();
}
@Override
public synchronized void load()
{
_skillLearn.clear();
parseDatapackFile("data/skillLearn.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _skillLearn.size() + " Skill Learn data.");
}
@Override
public void parseDocument(Document doc)
{
for (Node node = doc.getFirstChild(); node != null; node = node.getNextSibling())
{
if ("list".equalsIgnoreCase(node.getNodeName()))
{
for (Node list_node = node.getFirstChild(); list_node != null; list_node = list_node.getNextSibling())
{
if ("npc".equalsIgnoreCase(list_node.getNodeName()))
{
final List<ClassId> classIds = new ArrayList<>();
for (Node c = list_node.getFirstChild(); c != null; c = c.getNextSibling())
{
if ("classId".equalsIgnoreCase(c.getNodeName()))
{
classIds.add(ClassId.getClassId(Integer.parseInt(c.getTextContent())));
}
}
_skillLearn.put(parseInteger(list_node.getAttributes(), "id"), classIds);
}
}
}
}
}
/**
* @param npcId
* @return {@link List} of {@link ClassId}'s that this npcId can teach.
*/
public List<ClassId> getSkillLearnData(int npcId)
{
return _skillLearn.get(npcId);
}
/**
* Gets the single instance of SkillLearnData.
* @return single instance of SkillLearnData
*/
public static SkillLearnData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final SkillLearnData _instance = new SkillLearnData();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,520 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;
import javolution.util.FastMap;
import javolution.util.FastSet;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.Config;
import com.l2jserver.L2DatabaseFactory;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.instancemanager.DayNightSpawnManager;
import com.l2jserver.gameserver.instancemanager.ZoneManager;
import com.l2jserver.gameserver.model.L2Spawn;
import com.l2jserver.gameserver.model.StatsSet;
import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate;
/**
* Spawn data retriever.
* @author Zoey76
*/
public final class SpawnTable implements DocumentParser
{
private static final Logger LOGGER = Logger.getLogger(SpawnTable.class.getName());
// SQL
private static final String SELECT_SPAWNS = "SELECT count, npc_templateid, locx, locy, locz, heading, respawn_delay, respawn_random, loc_id, periodOfDay FROM spawnlist";
private static final String SELECT_CUSTOM_SPAWNS = "SELECT count, npc_templateid, locx, locy, locz, heading, respawn_delay, respawn_random, loc_id, periodOfDay FROM custom_spawnlist";
private static final Map<Integer, Set<L2Spawn>> _spawnTable = new FastMap<Integer, Set<L2Spawn>>().shared();
private int _xmlSpawnCount = 0;
/**
* Wrapper to load all spawns.
*/
@Override
public void load()
{
if (!Config.ALT_DEV_NO_SPAWNS)
{
fillSpawnTable(false);
final int spawnCount = _spawnTable.size();
LOGGER.info(getClass().getSimpleName() + ": Loaded " + spawnCount + " npc spawns.");
if (Config.CUSTOM_SPAWNLIST_TABLE)
{
fillSpawnTable(true);
LOGGER.info(getClass().getSimpleName() + ": Loaded " + (_spawnTable.size() - spawnCount) + " custom npc spawns.");
}
// Load XML list
parseDatapackDirectory("data/spawnlist", false);
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _xmlSpawnCount + " npc spawns from XML.");
}
}
private boolean checkTemplate(int npcId)
{
L2NpcTemplate npcTemplate = NpcData.getInstance().getTemplate(npcId);
if (npcTemplate == null)
{
LOGGER.warning(getClass().getSimpleName() + ": Data missing in NPC table for ID: " + npcId + ".");
return false;
}
if (npcTemplate.isType("L2SiegeGuard") || npcTemplate.isType("L2RaidBoss") || (!Config.ALLOW_CLASS_MASTERS && npcTemplate.isType("L2ClassMaster")))
{
// Don't spawn
return false;
}
return true;
}
@Override
public void parseDocument(Document doc)
{
NamedNodeMap attrs;
for (Node list = doc.getFirstChild(); list != null; list = list.getNextSibling())
{
if (list.getNodeName().equalsIgnoreCase("list"))
{
attrs = list.getAttributes();
// skip disabled spawnlists
if (!Boolean.parseBoolean(attrs.getNamedItem("enabled").getNodeValue()))
{
continue;
}
for (Node param = list.getFirstChild(); param != null; param = param.getNextSibling())
{
attrs = param.getAttributes();
if (param.getNodeName().equalsIgnoreCase("spawn"))
{
String territoryName = null;
String spawnName = null;
Map<String, Integer> map = null;
// Check, if spawn name specified
if (attrs.getNamedItem("name") != null)
{
spawnName = parseString(attrs, "name");
}
// Check, if spawn territory specified and exists
if ((attrs.getNamedItem("zone") != null) && (ZoneManager.getInstance().getSpawnTerritory(attrs.getNamedItem("zone").getNodeValue()) != null))
{
territoryName = parseString(attrs, "zone");
}
for (Node npctag = param.getFirstChild(); npctag != null; npctag = npctag.getNextSibling())
{
attrs = npctag.getAttributes();
// Check if there are any AI parameters
if (npctag.getNodeName().equalsIgnoreCase("AIData"))
{
attrs = npctag.getAttributes();
if (map == null)
{
map = new HashMap<>();
}
for (Node c = npctag.getFirstChild(); c != null; c = c.getNextSibling())
{
// Skip odd nodes
if (c.getNodeName().equals("#text"))
{
continue;
}
int val;
switch (c.getNodeName())
{
case "disableRandomAnimation":
case "disableRandomWalk":
val = Boolean.parseBoolean(c.getTextContent()) ? 1 : 0;
break;
default:
val = Integer.parseInt(c.getTextContent());
}
map.put(c.getNodeName(), val);
}
}
// Check for NPC spawns
else if (npctag.getNodeName().equalsIgnoreCase("npc"))
{
// mandatory
final int templateId = parseInteger(attrs, "id");
// coordinates are optional, if territory is specified; mandatory otherwise
int x = 0;
int y = 0;
int z = 0;
try
{
x = parseInteger(attrs, "x");
y = parseInteger(attrs, "y");
z = parseInteger(attrs, "z");
}
catch (NullPointerException npe)
{
// x, y, z can be unspecified, if this spawn is territory based, do nothing
}
if ((x == 0) && (y == 0) && (territoryName == null)) // Both coordinates and zone are unspecified
{
LOGGER.warning("XML Spawnlist: Spawn could not be initialized, both coordinates and zone are unspecified for ID " + templateId);
continue;
}
StatsSet spawnInfo = new StatsSet();
spawnInfo.set("npcTemplateid", templateId);
spawnInfo.set("x", x);
spawnInfo.set("y", y);
spawnInfo.set("z", z);
spawnInfo.set("territoryName", territoryName);
spawnInfo.set("spawnName", spawnName);
// trying to read optional parameters
if (attrs.getNamedItem("heading") != null)
{
spawnInfo.set("heading", parseInteger(attrs, "heading"));
}
if (attrs.getNamedItem("count") != null)
{
spawnInfo.set("count", parseInteger(attrs, "count"));
}
if (attrs.getNamedItem("respawnDelay") != null)
{
spawnInfo.set("respawnDelay", parseInteger(attrs, "respawnDelay"));
}
if (attrs.getNamedItem("respawnRandom") != null)
{
spawnInfo.set("respawnRandom", parseInteger(attrs, "respawnRandom"));
}
if (attrs.getNamedItem("periodOfDay") != null)
{
String period = attrs.getNamedItem("periodOfDay").getNodeValue();
if (period.equalsIgnoreCase("day") || period.equalsIgnoreCase("night"))
{
spawnInfo.set("periodOfDay", period.equalsIgnoreCase("day") ? 1 : 2);
}
}
_xmlSpawnCount += addSpawn(spawnInfo, map);
}
}
}
}
}
}
}
/**
* Retrieves spawn data from database.
* @param isCustom if {@code true} the spawns are loaded as custom from custom spawn table
* @return the spawn count
*/
private int fillSpawnTable(boolean isCustom)
{
int npcSpawnCount = 0;
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
Statement s = con.createStatement();
ResultSet rs = s.executeQuery(isCustom ? SELECT_CUSTOM_SPAWNS : SELECT_SPAWNS))
{
while (rs.next())
{
StatsSet spawnInfo = new StatsSet();
int npcId = rs.getInt("npc_templateid");
// Check basic requirements first
if (!checkTemplate(npcId))
{
// Don't spawn
continue;
}
spawnInfo.set("npcTemplateid", npcId);
spawnInfo.set("count", rs.getInt("count"));
spawnInfo.set("x", rs.getInt("locx"));
spawnInfo.set("y", rs.getInt("locy"));
spawnInfo.set("z", rs.getInt("locz"));
spawnInfo.set("heading", rs.getInt("heading"));
spawnInfo.set("respawnDelay", rs.getInt("respawn_delay"));
spawnInfo.set("respawnRandom", rs.getInt("respawn_random"));
spawnInfo.set("locId", rs.getInt("loc_id"));
spawnInfo.set("periodOfDay", rs.getInt("periodOfDay"));
spawnInfo.set("isCustomSpawn", isCustom);
npcSpawnCount += addSpawn(spawnInfo);
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Spawn could not be initialized: " + e.getMessage(), e);
}
return npcSpawnCount;
}
/**
* Creates NPC spawn
* @param spawnInfo StatsSet of spawn parameters
* @param AIData Map of specific AI parameters for this spawn
* @return count NPC instances, spawned by this spawn
*/
private int addSpawn(StatsSet spawnInfo, Map<String, Integer> AIData)
{
L2Spawn spawnDat;
int ret = 0;
try
{
spawnDat = new L2Spawn(NpcData.getInstance().getTemplate(spawnInfo.getInt("npcTemplateid")));
spawnDat.setAmount(spawnInfo.getInt("count", 1));
spawnDat.setX(spawnInfo.getInt("x", 0));
spawnDat.setY(spawnInfo.getInt("y", 0));
spawnDat.setZ(spawnInfo.getInt("z", 0));
spawnDat.setHeading(spawnInfo.getInt("heading", -1));
spawnDat.setRespawnDelay(spawnInfo.getInt("respawnDelay", 0), spawnInfo.getInt("respawnRandom", 0));
spawnDat.setLocationId(spawnInfo.getInt("locId", 0));
String territoryName = spawnInfo.getString("territoryName", "");
String spawnName = spawnInfo.getString("spawnName", "");
spawnDat.setCustom(spawnInfo.getBoolean("isCustomSpawn", false));
if (!spawnName.isEmpty())
{
spawnDat.setName(spawnName);
}
if (!territoryName.isEmpty())
{
spawnDat.setSpawnTerritory(ZoneManager.getInstance().getSpawnTerritory(territoryName));
}
// Register AI Data for this spawn
NpcPersonalAIData.getInstance().storeData(spawnDat, AIData);
switch (spawnInfo.getInt("periodOfDay", 0))
{
case 0: // default
ret += spawnDat.init();
break;
case 1: // Day
DayNightSpawnManager.getInstance().addDayCreature(spawnDat);
ret = 1;
break;
case 2: // Night
DayNightSpawnManager.getInstance().addNightCreature(spawnDat);
ret = 1;
break;
}
addSpawn(spawnDat);
}
catch (Exception e)
{
// problem with initializing spawn, go to next one
LOGGER.log(Level.WARNING, "Spawn could not be initialized: " + e.getMessage(), e);
}
return ret;
}
/**
* Wrapper for {@link #addSpawn(StatsSet, Map)}.
* @param spawnInfo StatsSet of spawn parameters
* @return count NPC instances, spawned by this spawn
*/
private int addSpawn(StatsSet spawnInfo)
{
return addSpawn(spawnInfo, null);
}
public Map<Integer, Set<L2Spawn>> getSpawnTable()
{
return _spawnTable;
}
/**
* Get the spawns for the NPC Id.
* @param npcId the NPC Id
* @return the spawn set for the given npcId
*/
public Set<L2Spawn> getSpawns(int npcId)
{
return _spawnTable.containsKey(npcId) ? _spawnTable.get(npcId) : Collections.<L2Spawn> emptySet();
}
/**
* Get the first NPC spawn.
* @param npcId the NPC Id to search
* @return the first not null spawn, if any
*/
public L2Spawn getFirstSpawn(int npcId)
{
if (_spawnTable.containsKey(npcId))
{
for (L2Spawn spawn : _spawnTable.get(npcId))
{
if (spawn != null)
{
return spawn;
}
}
}
return null;
}
/**
* Add a new spawn to the spawn table.
* @param spawn the spawn to add
* @param storeInDb if {@code true} it'll be saved in the database
*/
public void addNewSpawn(L2Spawn spawn, boolean storeInDb)
{
addSpawn(spawn);
if (storeInDb)
{
final String spawnTable = spawn.isCustom() && Config.CUSTOM_SPAWNLIST_TABLE ? "custom_spawnlist" : "spawnlist";
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement insert = con.prepareStatement("INSERT INTO " + spawnTable + "(count,npc_templateid,locx,locy,locz,heading,respawn_delay,respawn_random,loc_id) values(?,?,?,?,?,?,?,?,?)"))
{
insert.setInt(1, spawn.getAmount());
insert.setInt(2, spawn.getId());
insert.setInt(3, spawn.getX());
insert.setInt(4, spawn.getY());
insert.setInt(5, spawn.getZ());
insert.setInt(6, spawn.getHeading());
insert.setInt(7, spawn.getRespawnDelay() / 1000);
insert.setInt(8, spawn.getRespawnMaxDelay() - spawn.getRespawnMinDelay());
insert.setInt(9, spawn.getLocationId());
insert.execute();
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Could not store spawn in the DB:" + e.getMessage(), e);
}
}
}
/**
* Delete an spawn from the spawn table.
* @param spawn the spawn to delete
* @param updateDb if {@code true} database will be updated
*/
public void deleteSpawn(L2Spawn spawn, boolean updateDb)
{
if (!removeSpawn(spawn))
{
return;
}
if (updateDb)
{
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement delete = con.prepareStatement("DELETE FROM " + (spawn.isCustom() ? "custom_spawnlist" : "spawnlist") + " WHERE locx=? AND locy=? AND locz=? AND npc_templateid=? AND heading=?"))
{
delete.setInt(1, spawn.getX());
delete.setInt(2, spawn.getY());
delete.setInt(3, spawn.getZ());
delete.setInt(4, spawn.getId());
delete.setInt(5, spawn.getHeading());
delete.execute();
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Spawn " + spawn + " could not be removed from DB: " + e.getMessage(), e);
}
}
}
/**
* Add a spawn to the spawn set if present, otherwise add a spawn set and add the spawn to the newly created spawn set.
* @param spawn the NPC spawn to add
*/
private void addSpawn(L2Spawn spawn)
{
if (!_spawnTable.containsKey(spawn.getId()))
{
_spawnTable.put(spawn.getId(), new FastSet<L2Spawn>().shared());
}
_spawnTable.get(spawn.getId()).add(spawn);
}
/**
* Remove a spawn from the spawn set, if the spawn set is empty, remove it as well.
* @param spawn the NPC spawn to remove
* @return {@code true} if the spawn was successfully removed, {@code false} otherwise
*/
private boolean removeSpawn(L2Spawn spawn)
{
if (_spawnTable.containsKey(spawn.getId()))
{
final Set<L2Spawn> set = _spawnTable.get(spawn.getId());
boolean removed = set.remove(spawn);
if (set.isEmpty())
{
_spawnTable.remove(spawn.getId());
}
return removed;
}
return false;
}
/**
* Execute a procedure over all spawns.<br>
* <font size="4" color="red">Do not use it!</font>
* @param function the function to execute
* @return {@code true} if all procedures were executed, {@code false} otherwise
*/
public boolean forEachSpawn(Function<L2Spawn, Boolean> function)
{
for (Set<L2Spawn> set : _spawnTable.values())
{
for (L2Spawn spawn : set)
{
if (!function.apply(spawn))
{
return false;
}
}
}
return true;
}
public static SpawnTable getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final SpawnTable _instance = new SpawnTable();
}
}

View File

@@ -0,0 +1,123 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.idfactory.IdFactory;
import com.l2jserver.gameserver.model.StatsSet;
import com.l2jserver.gameserver.model.actor.instance.L2StaticObjectInstance;
import com.l2jserver.gameserver.model.actor.templates.L2CharTemplate;
/**
* This class loads and holds all static object data.
* @author UnAfraid
*/
public final class StaticObjects implements DocumentParser
{
private static final Map<Integer, L2StaticObjectInstance> _staticObjects = new HashMap<>();
/**
* Instantiates a new static objects.
*/
protected StaticObjects()
{
load();
}
@Override
public void load()
{
_staticObjects.clear();
parseDatapackFile("data/staticObjects.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _staticObjects.size() + " static object templates.");
}
@Override
public void parseDocument(Document doc)
{
NamedNodeMap attrs;
Node att;
StatsSet set;
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("object".equalsIgnoreCase(d.getNodeName()))
{
attrs = d.getAttributes();
set = new StatsSet();
for (int i = 0; i < attrs.getLength(); i++)
{
att = attrs.item(i);
set.set(att.getNodeName(), att.getNodeValue());
}
addObject(set);
}
}
}
}
}
/**
* Initialize an static object based on the stats set and add it to the map.
* @param set the stats set to add.
*/
private void addObject(StatsSet set)
{
L2StaticObjectInstance obj = new L2StaticObjectInstance(IdFactory.getInstance().getNextId(), new L2CharTemplate(new StatsSet()), set.getInt("id"));
obj.setType(set.getInt("type", 0));
obj.setName(set.getString("name"));
obj.setMap(set.getString("texture", "none"), set.getInt("map_x", 0), set.getInt("map_y", 0));
obj.spawnMe(set.getInt("x"), set.getInt("y"), set.getInt("z"));
_staticObjects.put(obj.getObjectId(), obj);
}
/**
* Gets the static objects.
* @return a collection of static objects.
*/
public Collection<L2StaticObjectInstance> getStaticObjects()
{
return _staticObjects.values();
}
/**
* Gets the single instance of StaticObjects.
* @return single instance of StaticObjects
*/
public static StaticObjects getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final StaticObjects _instance = new StaticObjects();
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.skills.Skill;
/**
* @author Nyaran
*/
public class SummonEffectsTable
{
/** Servitors **/
// Map tree
// -> key: charObjectId, value: classIndex Map
// --> key: classIndex, value: servitors Map
// ---> key: servitorSkillId, value: Effects list
private final Map<Integer, Map<Integer, Map<Integer, List<SummonEffect>>>> _servitorEffects = new HashMap<>();
public Map<Integer, Map<Integer, Map<Integer, List<SummonEffect>>>> getServitorEffectsOwner()
{
return _servitorEffects;
}
public Map<Integer, List<SummonEffect>> getServitorEffects(L2PcInstance owner)
{
final Map<Integer, Map<Integer, List<SummonEffect>>> servitorMap = _servitorEffects.get(owner.getObjectId());
if (servitorMap == null)
{
return null;
}
return servitorMap.get(owner.getClassIndex());
}
/** Pets **/
private final Map<Integer, List<SummonEffect>> _petEffects = new HashMap<>(); // key: petItemObjectId, value: Effects list
public Map<Integer, List<SummonEffect>> getPetEffects()
{
return _petEffects;
}
public class SummonEffect
{
Skill _skill;
int _effectCurTime;
public SummonEffect(Skill skill, int effectCurTime)
{
_skill = skill;
_effectCurTime = effectCurTime;
}
public Skill getSkill()
{
return _skill;
}
public int getEffectCurTime()
{
return _effectCurTime;
}
}
public static SummonEffectsTable getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final SummonEffectsTable _instance = new SummonEffectsTable();
}
}

View File

@@ -0,0 +1,183 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jserver.L2DatabaseFactory;
import com.l2jserver.gameserver.model.actor.L2Summon;
public class SummonSkillsTable
{
private static Logger LOGGER = Logger.getLogger(SummonSkillsTable.class.getName());
private final Map<Integer, Map<Integer, L2PetSkillLearn>> _skillTrees = new HashMap<>();
protected SummonSkillsTable()
{
load();
}
public void load()
{
_skillTrees.clear();
int count = 0;
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
Statement s = con.createStatement();
ResultSet rs = s.executeQuery("SELECT templateId, minLvl, skillId, skillLvl FROM pets_skills"))
{
while (rs.next())
{
final int npcId = rs.getInt("templateId");
Map<Integer, L2PetSkillLearn> skillTree = _skillTrees.get(npcId);
if (skillTree == null)
{
skillTree = new HashMap<>();
_skillTrees.put(npcId, skillTree);
}
int id = rs.getInt("skillId");
int lvl = rs.getInt("skillLvl");
skillTree.put(SkillData.getSkillHashCode(id, lvl + 1), new L2PetSkillLearn(id, lvl, rs.getInt("minLvl")));
count++;
}
}
catch (Exception e)
{
LOGGER.log(Level.SEVERE, getClass().getSimpleName() + ": Error while loading pet skill tree:", e);
}
LOGGER.info(getClass().getSimpleName() + ": Loaded " + count + " skills.");
}
public int getAvailableLevel(L2Summon cha, int skillId)
{
int lvl = 0;
if (!_skillTrees.containsKey(cha.getId()))
{
LOGGER.warning(getClass().getSimpleName() + ": Pet id " + cha.getId() + " does not have any skills assigned.");
return lvl;
}
Collection<L2PetSkillLearn> skills = _skillTrees.get(cha.getId()).values();
for (L2PetSkillLearn temp : skills)
{
if (temp.getId() != skillId)
{
continue;
}
if (temp.getLevel() == 0)
{
if (cha.getLevel() < 70)
{
lvl = (cha.getLevel() / 10);
if (lvl <= 0)
{
lvl = 1;
}
}
else
{
lvl = (7 + ((cha.getLevel() - 70) / 5));
}
// formula usable for skill that have 10 or more skill levels
int maxLvl = SkillData.getInstance().getMaxLevel(temp.getId());
if (lvl > maxLvl)
{
lvl = maxLvl;
}
break;
}
else if (temp.getMinLevel() <= cha.getLevel())
{
if (temp.getLevel() > lvl)
{
lvl = temp.getLevel();
}
}
}
return lvl;
}
public List<Integer> getAvailableSkills(L2Summon cha)
{
List<Integer> skillIds = new ArrayList<>();
if (!_skillTrees.containsKey(cha.getId()))
{
LOGGER.warning(getClass().getSimpleName() + ": Pet id " + cha.getId() + " does not have any skills assigned.");
return skillIds;
}
Collection<L2PetSkillLearn> skills = _skillTrees.get(cha.getId()).values();
for (L2PetSkillLearn temp : skills)
{
if (skillIds.contains(temp.getId()))
{
continue;
}
skillIds.add(temp.getId());
}
return skillIds;
}
public static final class L2PetSkillLearn
{
private final int _id;
private final int _level;
private final int _minLevel;
public L2PetSkillLearn(int id, int lvl, int minLvl)
{
_id = id;
_level = lvl;
_minLevel = minLvl;
}
public int getId()
{
return _id;
}
public int getLevel()
{
return _level;
}
public int getMinLevel()
{
return _minLevel;
}
}
public static SummonSkillsTable getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final SummonSkillsTable _instance = new SummonSkillsTable();
}
}

View File

@@ -0,0 +1,125 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jserver.Config;
import com.l2jserver.L2DatabaseFactory;
import com.l2jserver.gameserver.model.L2TeleportLocation;
public class TeleportLocationTable
{
private static Logger LOGGER = Logger.getLogger(TeleportLocationTable.class.getName());
private final Map<Integer, L2TeleportLocation> _teleports = new HashMap<>();
protected TeleportLocationTable()
{
reloadAll();
}
public void reloadAll()
{
_teleports.clear();
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
Statement s = con.createStatement();
ResultSet rs = s.executeQuery("SELECT id, loc_x, loc_y, loc_z, price, fornoble, itemId FROM teleport"))
{
L2TeleportLocation teleport;
while (rs.next())
{
teleport = new L2TeleportLocation();
teleport.setTeleId(rs.getInt("id"));
teleport.setLocX(rs.getInt("loc_x"));
teleport.setLocY(rs.getInt("loc_y"));
teleport.setLocZ(rs.getInt("loc_z"));
teleport.setPrice(rs.getInt("price"));
teleport.setIsForNoble(rs.getInt("fornoble") == 1);
teleport.setItemId(rs.getInt("itemId"));
_teleports.put(teleport.getTeleId(), teleport);
}
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _teleports.size() + " Teleport Location Templates.");
}
catch (Exception e)
{
LOGGER.log(Level.SEVERE, getClass().getSimpleName() + ": Error loading Teleport Table.", e);
}
if (Config.CUSTOM_TELEPORT_TABLE)
{
int _cTeleCount = _teleports.size();
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
Statement s = con.createStatement();
ResultSet rs = s.executeQuery("SELECT id, loc_x, loc_y, loc_z, price, fornoble, itemId FROM custom_teleport"))
{
L2TeleportLocation teleport;
while (rs.next())
{
teleport = new L2TeleportLocation();
teleport.setTeleId(rs.getInt("id"));
teleport.setLocX(rs.getInt("loc_x"));
teleport.setLocY(rs.getInt("loc_y"));
teleport.setLocZ(rs.getInt("loc_z"));
teleport.setPrice(rs.getInt("price"));
teleport.setIsForNoble(rs.getInt("fornoble") == 1);
teleport.setItemId(rs.getInt("itemId"));
_teleports.put(teleport.getTeleId(), teleport);
}
_cTeleCount = _teleports.size() - _cTeleCount;
if (_cTeleCount > 0)
{
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _cTeleCount + " Custom Teleport Location Templates.");
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Error while creating custom teleport table " + e.getMessage(), e);
}
}
}
/**
* @param id
* @return
*/
public L2TeleportLocation getTemplate(int id)
{
return _teleports.get(id);
}
public static TeleportLocationTable getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final TeleportLocationTable _instance = new TeleportLocationTable();
}
}

View File

@@ -0,0 +1,115 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.model.StatsSet;
import com.l2jserver.gameserver.model.teleporter.TeleportHolder;
import com.l2jserver.gameserver.model.teleporter.TeleportLocation;
import com.l2jserver.gameserver.model.teleporter.TeleportType;
/**
* @author UnAfraid
*/
public class TeleportersData implements DocumentParser
{
private final Map<Integer, TeleportHolder> _teleporters = new HashMap<>();
protected TeleportersData()
{
load();
}
@Override
public void load()
{
_teleporters.clear();
parseDatapackDirectory("data/teleporters", true);
LOGGER.log(Level.INFO, "Loaded: " + _teleporters.size() + " npc teleporters.");
}
@Override
public void parseDocument(Document doc)
{
for (Node listNode = doc.getFirstChild(); listNode != null; listNode = listNode.getNextSibling())
{
if ("list".equals(listNode.getNodeName()))
{
for (Node npcNode = listNode.getFirstChild(); npcNode != null; npcNode = npcNode.getNextSibling())
{
if ("npc".equals(npcNode.getNodeName()))
{
final int id = parseInteger(npcNode.getAttributes(), "id");
final TeleportHolder holder = new TeleportHolder(id);
for (Node tpNode = npcNode.getFirstChild(); tpNode != null; tpNode = tpNode.getNextSibling())
{
if ("teleport".equals(tpNode.getNodeName()))
{
final TeleportType type = parseEnum(tpNode.getAttributes(), TeleportType.class, "type", TeleportType.NORMAL);
for (Node locNode = tpNode.getFirstChild(); locNode != null; locNode = locNode.getNextSibling())
{
if ("location".equals(locNode.getNodeName()))
{
final NamedNodeMap attrs = locNode.getAttributes();
final int nextId = holder.getLocations(type).size() + 1;
final StatsSet set = new StatsSet();
for (int i = 0; i < attrs.getLength(); i++)
{
final Node locationNode = attrs.item(i);
set.set(locationNode.getNodeName(), locationNode.getNodeValue());
}
holder.addLocation(type, new TeleportLocation(nextId, set));
}
}
}
}
_teleporters.put(id, holder);
}
}
}
}
}
public TeleportHolder getHolder(int npcId)
{
return _teleporters.get(npcId);
}
/**
* Gets the single instance of TeleportersData.
* @return single instance of TeleportersData
*/
public static TeleportersData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final TeleportersData _instance = new TeleportersData();
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.datatables;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jserver.L2DatabaseFactory;
import com.l2jserver.gameserver.model.L2Territory;
import com.l2jserver.gameserver.model.Location;
/**
* @author Balancer, Mr
*/
public class TerritoryTable
{
private static final Logger LOGGER = Logger.getLogger(TerritoryTable.class.getName());
private static final Map<Integer, L2Territory> _territory = new HashMap<>();
/**
* Instantiates a new territory.
*/
protected TerritoryTable()
{
load();
}
/**
* Gets the random point.
* @param terr the territory Id?
* @return the random point
*/
public Location getRandomPoint(int terr)
{
return _territory.get(terr).getRandomPoint();
}
/**
* Gets the proc max.
* @param terr the territory Id?
* @return the proc max
*/
public int getProcMax(int terr)
{
return _territory.get(terr).getProcMax();
}
/**
* Load the data from database.
*/
public void load()
{
_territory.clear();
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
Statement stmt = con.createStatement();
ResultSet rset = stmt.executeQuery("SELECT * FROM locations WHERE loc_id>0"))
{
while (rset.next())
{
int terrId = rset.getInt("loc_id");
L2Territory terr = _territory.get(terrId);
if (terr == null)
{
terr = new L2Territory(terrId);
_territory.put(terrId, terr);
}
terr.add(rset.getInt("loc_x"), rset.getInt("loc_y"), rset.getInt("loc_zmin"), rset.getInt("loc_zmax"), rset.getInt("proc"));
}
LOGGER.info("TerritoryTable: Loaded " + _territory.size() + " territories from database.");
}
catch (SQLException e)
{
LOGGER.log(Level.SEVERE, "TerritoryTable: Failed to load territories from database!", e);
}
}
/**
* Gets the single instance of Territory.
* @return single instance of Territory
*/
public static TerritoryTable getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final TerritoryTable _instance = new TerritoryTable();
}
}

Some files were not shown because too many files have changed in this diff Show More