first commit
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
|
||||
<id>zip</id>
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
<formats>
|
||||
<format>zip</format>
|
||||
</formats>
|
||||
<files>
|
||||
<file>
|
||||
<source>${project.build.directory}/l2jserver.jar</source>
|
||||
<outputDirectory>.</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>src/main/resources/GameServer_loop.sh</source>
|
||||
<outputDirectory>.</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>src/main/resources/startGameServer.bat</source>
|
||||
<outputDirectory>.</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>src/main/resources/startGameServer.sh</source>
|
||||
<outputDirectory>.</outputDirectory>
|
||||
</file>
|
||||
</files>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>src/main/resources/</directory>
|
||||
<includes>
|
||||
<include>config/*</include>
|
||||
</includes>
|
||||
<outputDirectory>.</outputDirectory>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
<dependencySets>
|
||||
<dependencySet>
|
||||
<outputDirectory>libs</outputDirectory>
|
||||
<excludes>
|
||||
<exclude>${project.groupId}:${project.artifactId}:jar:*</exclude>
|
||||
</excludes>
|
||||
</dependencySet>
|
||||
</dependencySets>
|
||||
</assembly>
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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 static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
|
||||
/**
|
||||
* Custom.
|
||||
* @author Zoey76
|
||||
* @version 2.6.2.0
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
public @interface Custom {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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 static com.l2jserver.gameserver.config.Configuration.fortress;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
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
|
||||
* @since 2008
|
||||
*/
|
||||
public class FortUpdater implements Runnable {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(FortUpdater.class);
|
||||
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() >= fortress().getFeeForCastle()) {
|
||||
_clan.getWarehouse().destroyItemByItemId("FS_fee_for_Castle", Inventory.ADENA_ID, fortress().getFeeForCastle(), null, null);
|
||||
_fort.getContractedCastle().addToTreasuryNoTax(fortress().getFeeForCastle());
|
||||
_fort.raiseSupplyLvL();
|
||||
} else {
|
||||
_fort.setFortState(1, 0);
|
||||
}
|
||||
}
|
||||
_fort.saveFortVariables();
|
||||
}
|
||||
case MAX_OWN_TIME -> {
|
||||
if ((_fort.getOwnerClan() == null) || (_fort.getOwnerClan() != _clan)) {
|
||||
return;
|
||||
}
|
||||
if (_fort.getOwnedTime() > (fortress().getMaxKeepTime() * 3600)) {
|
||||
_fort.removeOwner(true);
|
||||
_fort.setFortState(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("There has been a problem updating forts!", e);
|
||||
}
|
||||
}
|
||||
|
||||
public int getRunCount() {
|
||||
return _runCount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,538 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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 static com.l2jserver.gameserver.config.Configuration.customs;
|
||||
import static com.l2jserver.gameserver.config.Configuration.database;
|
||||
import static com.l2jserver.gameserver.config.Configuration.general;
|
||||
import static com.l2jserver.gameserver.config.Configuration.geodata;
|
||||
import static com.l2jserver.gameserver.config.Configuration.hexId;
|
||||
import static com.l2jserver.gameserver.config.Configuration.mmo;
|
||||
import static com.l2jserver.gameserver.config.Configuration.server;
|
||||
import static com.l2jserver.gameserver.config.Configuration.telnet;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
import java.awt.Toolkit;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Calendar;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.l2jserver.commons.UPnPService;
|
||||
import com.l2jserver.commons.dao.ServerNameDAO;
|
||||
import com.l2jserver.commons.database.ConnectionFactory;
|
||||
import com.l2jserver.commons.util.IPv4Filter;
|
||||
import com.l2jserver.commons.util.Util;
|
||||
import com.l2jserver.gameserver.bbs.service.ForumsBBSManager;
|
||||
import com.l2jserver.gameserver.cache.HtmCache;
|
||||
import com.l2jserver.gameserver.dao.factory.impl.DAOFactory;
|
||||
import com.l2jserver.gameserver.data.json.ExperienceData;
|
||||
import com.l2jserver.gameserver.data.sql.impl.AnnouncementsTable;
|
||||
import com.l2jserver.gameserver.data.sql.impl.CharNameTable;
|
||||
import com.l2jserver.gameserver.data.sql.impl.CharSummonTable;
|
||||
import com.l2jserver.gameserver.data.sql.impl.ClanTable;
|
||||
import com.l2jserver.gameserver.data.sql.impl.CrestTable;
|
||||
import com.l2jserver.gameserver.data.sql.impl.NpcBufferTable;
|
||||
import com.l2jserver.gameserver.data.sql.impl.OfflineTradersTable;
|
||||
import com.l2jserver.gameserver.data.sql.impl.SummonSkillsTable;
|
||||
import com.l2jserver.gameserver.data.xml.impl.AdminData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.ArmorSetsData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.BuyListData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.CategoryData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.ClassListData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.DoorData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.EnchantItemData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.EnchantItemGroupsData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.EnchantItemHPBonusData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.EnchantItemOptionsData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.EnchantSkillGroupsData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.FishData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.FishingMonstersData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.FishingRodsData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.HennaData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.HitConditionBonusData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.InitialEquipmentData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.InitialShortcutData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.KarmaData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.MultisellData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.NpcData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.OptionData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.PetDataTable;
|
||||
import com.l2jserver.gameserver.data.xml.impl.PlayerCreationPointData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.PlayerTemplateData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.PlayerXpPercentLostData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.RecipeData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.SecondaryAuthData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.SiegeScheduleData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.SkillLearnData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.SkillTreesData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.StaticObjectData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.TransformData;
|
||||
import com.l2jserver.gameserver.data.xml.impl.UIData;
|
||||
import com.l2jserver.gameserver.datatables.AugmentationData;
|
||||
import com.l2jserver.gameserver.datatables.BotReportTable;
|
||||
import com.l2jserver.gameserver.datatables.EventDroplist;
|
||||
import com.l2jserver.gameserver.datatables.ItemTable;
|
||||
import com.l2jserver.gameserver.datatables.SkillData;
|
||||
import com.l2jserver.gameserver.datatables.SpawnTable;
|
||||
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.CastleManager;
|
||||
import com.l2jserver.gameserver.instancemanager.CastleManorManager;
|
||||
import com.l2jserver.gameserver.instancemanager.ClanHallManager;
|
||||
import com.l2jserver.gameserver.instancemanager.ClanHallSiegeManager;
|
||||
import com.l2jserver.gameserver.instancemanager.CoupleManager;
|
||||
import com.l2jserver.gameserver.instancemanager.CursedWeaponsManager;
|
||||
import com.l2jserver.gameserver.instancemanager.DayNightSpawnManager;
|
||||
import com.l2jserver.gameserver.instancemanager.DimensionalRiftManager;
|
||||
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.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.TerritoryWarManager;
|
||||
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.network.NpcStringId;
|
||||
import com.l2jserver.gameserver.network.SystemMessageId;
|
||||
import com.l2jserver.gameserver.pathfinding.PathFinding;
|
||||
import com.l2jserver.gameserver.script.faenor.FaenorScriptEngine;
|
||||
import com.l2jserver.gameserver.scripting.ScriptEngineManager;
|
||||
import com.l2jserver.gameserver.status.Status;
|
||||
import com.l2jserver.gameserver.taskmanager.KnownListUpdateTaskManager;
|
||||
import com.l2jserver.gameserver.taskmanager.TaskManager;
|
||||
import com.l2jserver.gameserver.util.DeadLockDetector;
|
||||
import com.l2jserver.mmocore.SelectorConfig;
|
||||
import com.l2jserver.mmocore.SelectorThread;
|
||||
|
||||
public final class GameServer {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(GameServer.class);
|
||||
|
||||
private static final String DATAPACK = "-dp";
|
||||
|
||||
private static final String SCRIPT = "-s";
|
||||
|
||||
private static final String GEODATA = "-gd";
|
||||
|
||||
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 GameServer() throws Exception {
|
||||
|
||||
final var serverLoadStart = startTimedSection("Database");
|
||||
ConnectionFactory.builder() //
|
||||
.withUrl(database().getURL()) //
|
||||
.withUser(database().getUser()) //
|
||||
.withPassword(database().getPassword()) //
|
||||
.withMaxIdleTime(database().getMaxIdleTime()) //
|
||||
.withMaxPoolSize(database().getMaxConnections()) //
|
||||
.build();
|
||||
|
||||
DAOFactory.getInstance();
|
||||
|
||||
if (!IdFactory.getInstance().isInitialized()) {
|
||||
LOG.error("Could not read object IDs from database. Please check your configuration.");
|
||||
throw new Exception("Could not initialize the Id factory!");
|
||||
}
|
||||
endTimedSection("Database", serverLoadStart);
|
||||
|
||||
ThreadPoolManager.getInstance();
|
||||
EventDispatcher.getInstance();
|
||||
ScriptEngineManager.getInstance();
|
||||
|
||||
final var worldStart = startTimedSection("World");
|
||||
GameTimeController.init();
|
||||
InstanceManager.getInstance();
|
||||
L2World.getInstance();
|
||||
MapRegionManager.getInstance();
|
||||
AnnouncementsTable.getInstance();
|
||||
GlobalVariablesManager.getInstance();
|
||||
endTimedSection("World", worldStart);
|
||||
|
||||
final var dataStart = startTimedSection("Data");
|
||||
CategoryData.getInstance();
|
||||
SecondaryAuthData.getInstance();
|
||||
endTimedSection("Data", dataStart);
|
||||
|
||||
final var effectsStart = startTimedSection("Effects");
|
||||
EffectHandler.getInstance().executeScript();
|
||||
endTimedSection("Effects", effectsStart);
|
||||
|
||||
final var esgStart = startTimedSection("Enchant Skill Groups");
|
||||
EnchantSkillGroupsData.getInstance();
|
||||
endTimedSection("Enchant Skill Groups", esgStart);
|
||||
|
||||
final var skillTreesStart = startTimedSection("Skill Trees");
|
||||
SkillTreesData.getInstance();
|
||||
endTimedSection("Skill Trees", skillTreesStart);
|
||||
|
||||
final var skillsStart = startTimedSection("Skills");
|
||||
SkillData.getInstance();
|
||||
SummonSkillsTable.getInstance();
|
||||
endTimedSection("Skills", skillsStart);
|
||||
|
||||
final var itemsStart = startTimedSection("Items");
|
||||
ItemTable.getInstance();
|
||||
EnchantItemGroupsData.getInstance();
|
||||
EnchantItemData.getInstance();
|
||||
EnchantItemOptionsData.getInstance();
|
||||
OptionData.getInstance();
|
||||
EnchantItemHPBonusData.getInstance();
|
||||
MultisellData.getInstance();
|
||||
RecipeData.getInstance();
|
||||
ArmorSetsData.getInstance();
|
||||
FishData.getInstance();
|
||||
FishingMonstersData.getInstance();
|
||||
FishingRodsData.getInstance();
|
||||
HennaData.getInstance();
|
||||
endTimedSection("Items", itemsStart);
|
||||
|
||||
final var charactersStart = startTimedSection("Characters");
|
||||
ClassListData.getInstance();
|
||||
InitialEquipmentData.getInstance();
|
||||
InitialShortcutData.getInstance();
|
||||
ExperienceData.getInstance();
|
||||
PlayerXpPercentLostData.getInstance();
|
||||
KarmaData.getInstance();
|
||||
HitConditionBonusData.getInstance();
|
||||
PlayerTemplateData.getInstance();
|
||||
PlayerCreationPointData.getInstance();
|
||||
CharNameTable.getInstance();
|
||||
AdminData.getInstance();
|
||||
RaidBossPointsManager.getInstance();
|
||||
PetDataTable.getInstance();
|
||||
CharSummonTable.getInstance().init();
|
||||
endTimedSection("Characters", charactersStart);
|
||||
|
||||
final var bbsStart = startTimedSection("BBS");
|
||||
if (general().enableCommunityBoard()) {
|
||||
ForumsBBSManager.getInstance().load();
|
||||
}
|
||||
endTimedSection("BBS", bbsStart);
|
||||
|
||||
final var clansStart = startTimedSection("Clans");
|
||||
ClanTable.getInstance();
|
||||
ClanHallSiegeManager.getInstance();
|
||||
ClanHallManager.getInstance();
|
||||
AuctionManager.getInstance();
|
||||
endTimedSection("Clans", clansStart);
|
||||
|
||||
final var geodataStart = startTimedSection("Geodata");
|
||||
GeoData.getInstance();
|
||||
if (geodata().getPathFinding() > 0) {
|
||||
PathFinding.getInstance();
|
||||
}
|
||||
endTimedSection("Geodata", geodataStart);
|
||||
|
||||
final var npcsStart = startTimedSection("NPCs");
|
||||
SkillLearnData.getInstance();
|
||||
NpcData.getInstance();
|
||||
WalkingManager.getInstance();
|
||||
StaticObjectData.getInstance();
|
||||
ZoneManager.getInstance();
|
||||
DoorData.getInstance();
|
||||
CastleManager.getInstance().loadInstances();
|
||||
NpcBufferTable.getInstance();
|
||||
GrandBossManager.getInstance().initZones();
|
||||
EventDroplist.getInstance();
|
||||
endTimedSection("NPCs", npcsStart);
|
||||
|
||||
final var auctionStart = startTimedSection("Auction Manager");
|
||||
ItemAuctionManager.getInstance();
|
||||
endTimedSection("Auction Manager", auctionStart);
|
||||
|
||||
final var olympiadStart = startTimedSection("Olympiad");
|
||||
Olympiad.getInstance();
|
||||
Hero.getInstance();
|
||||
endTimedSection("Olympiad", olympiadStart);
|
||||
|
||||
final var sevenSignsStart = startTimedSection("Seven Signs");
|
||||
SevenSigns.getInstance();
|
||||
endTimedSection("Seven Signs", sevenSignsStart);
|
||||
|
||||
// Call to load caches
|
||||
final var cacheStart = startTimedSection("Cache");
|
||||
HtmCache.getInstance();
|
||||
CrestTable.getInstance();
|
||||
UIData.getInstance();
|
||||
PartyMatchWaitingList.getInstance();
|
||||
PartyMatchRoomList.getInstance();
|
||||
PetitionManager.getInstance();
|
||||
AugmentationData.getInstance();
|
||||
CursedWeaponsManager.getInstance();
|
||||
TransformData.getInstance();
|
||||
BotReportTable.getInstance();
|
||||
QuestManager.getInstance();
|
||||
BoatManager.getInstance();
|
||||
AirShipManager.getInstance();
|
||||
GraciaSeedsManager.getInstance();
|
||||
endTimedSection("Cache", cacheStart);
|
||||
|
||||
final var handlersStart = startTimedSection("Handlers");
|
||||
ScriptEngineManager.getInstance().executeScript("com/l2jserver/datapack/handlers/MasterHandler.java");
|
||||
endTimedSection("Handlers", handlersStart);
|
||||
|
||||
final var aiStart = startTimedSection("AI");
|
||||
ScriptEngineManager.getInstance().executeScript("com/l2jserver/datapack/ai/AILoader.java");
|
||||
endTimedSection("AI", aiStart);
|
||||
|
||||
final var instancesStart = startTimedSection("Instances");
|
||||
ScriptEngineManager.getInstance().executeScript("com/l2jserver/datapack/instances/InstanceLoader.java");
|
||||
endTimedSection("Instances", instancesStart);
|
||||
|
||||
final var graciaStart = startTimedSection("Gracia");
|
||||
ScriptEngineManager.getInstance().executeScript("com/l2jserver/datapack/gracia/GraciaLoader.java");
|
||||
endTimedSection("Gracia", graciaStart);
|
||||
|
||||
final var hellboundStart = startTimedSection("Hellbound");
|
||||
ScriptEngineManager.getInstance().executeScript("com/l2jserver/datapack/hellbound/HellboundLoader.java");
|
||||
endTimedSection("Hellbound", hellboundStart);
|
||||
|
||||
final var questsStart = startTimedSection("Quests");
|
||||
ScriptEngineManager.getInstance().executeScript("com/l2jserver/datapack/quests/QuestLoader.java");
|
||||
ScriptEngineManager.getInstance().executeScript("com/l2jserver/datapack/quests/TerritoryWarScripts/TerritoryWarSuperClass.java");
|
||||
endTimedSection("Quests", questsStart);
|
||||
|
||||
final var scriptsStart = startTimedSection("Scripts");
|
||||
ScriptEngineManager.getInstance().executeScriptList(new File(server().getDatapackRoot(), "data/scripts.cfg"));
|
||||
endTimedSection("Scripts", scriptsStart);
|
||||
|
||||
final var buyListStart = startTimedSection("BuyLists");
|
||||
BuyListData.getInstance().load();
|
||||
endTimedSection("BuyLists", buyListStart);
|
||||
|
||||
final var spawnsStart = startTimedSection("Spawns");
|
||||
SpawnTable.getInstance().load();
|
||||
DayNightSpawnManager.getInstance().trim().notifyChangeMode();
|
||||
FourSepulchersManager.getInstance().init();
|
||||
DimensionalRiftManager.getInstance();
|
||||
RaidBossSpawnManager.getInstance();
|
||||
endTimedSection("Spawns", spawnsStart);
|
||||
|
||||
final var siegeStart = startTimedSection("Siege");
|
||||
SiegeManager.getInstance().getSieges();
|
||||
CastleManager.getInstance().activateInstances();
|
||||
FortManager.getInstance().loadInstances();
|
||||
FortManager.getInstance().activateInstances();
|
||||
FortSiegeManager.getInstance();
|
||||
SiegeScheduleData.getInstance();
|
||||
TerritoryWarManager.getInstance();
|
||||
CastleManorManager.getInstance();
|
||||
MercTicketManager.getInstance();
|
||||
endTimedSection("Siege", siegeStart);
|
||||
|
||||
final var otherStart = startTimedSection("Other");
|
||||
if (general().saveDroppedItem()) {
|
||||
ItemsOnGroundManager.getInstance();
|
||||
}
|
||||
|
||||
if ((general().getAutoDestroyDroppedItemAfter() > 0) || (general().getAutoDestroyHerbTime() > 0)) {
|
||||
ItemsAutoDestroy.getInstance();
|
||||
}
|
||||
|
||||
MonsterRace.getInstance();
|
||||
SevenSigns.getInstance().spawnSevenSignsNPC();
|
||||
SevenSignsFestival.getInstance();
|
||||
AutoSpawnHandler.getInstance();
|
||||
FaenorScriptEngine.getInstance();
|
||||
|
||||
if (customs().allowWedding()) {
|
||||
CoupleManager.getInstance();
|
||||
}
|
||||
|
||||
TaskManager.getInstance();
|
||||
|
||||
AntiFeedManager.getInstance().registerEvent(AntiFeedManager.GAME_ID);
|
||||
|
||||
if (general().allowMail()) {
|
||||
MailManager.getInstance();
|
||||
}
|
||||
|
||||
PunishmentManager.getInstance();
|
||||
|
||||
Runtime.getRuntime().addShutdownHook(Shutdown.getInstance());
|
||||
|
||||
LOG.info("Free Object Ids remaining {}.", IdFactory.getInstance().size());
|
||||
|
||||
TvTManager.getInstance();
|
||||
KnownListUpdateTaskManager.getInstance();
|
||||
|
||||
if ((customs().offlineTradeEnable() || customs().offlineCraftEnable()) && customs().restoreOffliners()) {
|
||||
OfflineTradersTable.getInstance().restoreOfflineTraders();
|
||||
}
|
||||
|
||||
// Multilingual Support
|
||||
if (customs().multiLangEnable()) {
|
||||
if (customs().multiLangSystemMessageEnable()) {
|
||||
SystemMessageId.reloadLocalisations();
|
||||
}
|
||||
if (customs().multiLangNpcStringEnable()) {
|
||||
NpcStringId.reloadLocalisations();
|
||||
}
|
||||
}
|
||||
|
||||
endTimedSection("Other", otherStart);
|
||||
|
||||
if (general().deadLockDetector()) {
|
||||
_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("Started, free memory {} Mb of {} Mb", freeMem, totalMem);
|
||||
Toolkit.getDefaultToolkit().beep();
|
||||
LoginServerThread.getInstance().start();
|
||||
|
||||
final SelectorConfig sc = new SelectorConfig();
|
||||
sc.MAX_READ_PER_PASS = mmo().getMaxReadPerPass();
|
||||
sc.MAX_SEND_PER_PASS = mmo().getMaxSendPerPass();
|
||||
sc.SLEEP_TIME = mmo().getSleepTime();
|
||||
sc.HELPER_BUFFER_COUNT = mmo().getHelperBufferCount();
|
||||
sc.TCP_NODELAY = mmo().isTcpNoDelay();
|
||||
|
||||
_gamePacketHandler = new L2GamePacketHandler();
|
||||
_selectorThread = new SelectorThread<>(sc, _gamePacketHandler, _gamePacketHandler, _gamePacketHandler, new IPv4Filter());
|
||||
|
||||
InetAddress bindAddress = null;
|
||||
if (!server().getHost().equals("*")) {
|
||||
try {
|
||||
bindAddress = InetAddress.getByName(server().getHost());
|
||||
} catch (UnknownHostException ex) {
|
||||
LOG.warn("Bind address is invalid, using all available IPs!", ex);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
_selectorThread.openServerSocket(bindAddress, server().getPort());
|
||||
_selectorThread.start();
|
||||
LOG.info("Now listening on {}:{}", server().getHost(), server().getPort());
|
||||
} catch (IOException ex) {
|
||||
LOG.error("Failed to open server socket!", ex);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
if (server().enableUPnP()) {
|
||||
startTimedSection("UPnP");
|
||||
UPnPService.getInstance().load(server().getPort(), "L2J Game Server");
|
||||
}
|
||||
|
||||
if (telnet().isEnabled()) {
|
||||
new Status(telnet().getPort(), telnet().getPassword()).start();
|
||||
} else {
|
||||
LOG.info("Telnet server is currently disabled.");
|
||||
}
|
||||
|
||||
LOG.info("Maximum numbers of connected players {}.", server().getMaxOnlineUsers());
|
||||
LOG.info("Server {} loaded in {} seconds.", ServerNameDAO.getServer(hexId().getServerID()), MILLISECONDS.toSeconds(System.currentTimeMillis() - serverLoadStart));
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
final String datapackRoot = Util.parseArg(args, DATAPACK, true);
|
||||
if (datapackRoot != null) {
|
||||
server().setProperty("DatapackRoot", datapackRoot);
|
||||
}
|
||||
|
||||
final String scriptRoot = Util.parseArg(args, SCRIPT, true);
|
||||
if (scriptRoot != null) {
|
||||
server().setProperty("ScriptRoot", scriptRoot);
|
||||
}
|
||||
|
||||
final String geodata = Util.parseArg(args, GEODATA, true);
|
||||
if (geodata != null) {
|
||||
geodata().setProperty("GeoDataPath", geodata);
|
||||
}
|
||||
|
||||
gameServer = new GameServer();
|
||||
}
|
||||
|
||||
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 static long startTimedSection(String sectionName) {
|
||||
final var sBuilder = new StringBuilder("=[ " + sectionName + " ]");
|
||||
while (sBuilder.length() < 61) {
|
||||
sBuilder.insert(0, "-");
|
||||
}
|
||||
LOG.info(sBuilder.toString());
|
||||
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private static void endTimedSection(String sectionName, long startTime) {
|
||||
LOG.info("[ {} loaded in {}ms ]", sectionName, System.currentTimeMillis() - startTime);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
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 = LoggerFactory.getLogger(GameTimeController.class);
|
||||
|
||||
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_PER_IG_HOUR = TICKS_PER_IG_DAY / 24;
|
||||
public static final int TICKS_PER_IG_MINUTE = TICKS_PER_IG_HOUR / 60;
|
||||
public static final int TICKS_SUN_STATE_CHANGE = TICKS_PER_IG_DAY / 4;
|
||||
|
||||
private static GameTimeController _instance;
|
||||
|
||||
private final Set<L2Character> _movingObjects = ConcurrentHashMap.newKeySet();
|
||||
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 void init() {
|
||||
_instance = new GameTimeController();
|
||||
}
|
||||
|
||||
public int getGameTime() {
|
||||
return (getGameTicks() % TICKS_PER_IG_DAY) / TICKS_PER_IG_MINUTE;
|
||||
}
|
||||
|
||||
public int getGameHour() {
|
||||
return getGameTime() / 60;
|
||||
}
|
||||
|
||||
public int getGameMinute() {
|
||||
return getGameTime() % 60;
|
||||
}
|
||||
|
||||
public boolean isNight() {
|
||||
return getGameHour() < 6;
|
||||
}
|
||||
|
||||
/**
|
||||
* The true GameTime tick. Directly taken from current time. This represents the tick of the time.
|
||||
* @return
|
||||
*/
|
||||
public 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 void registerMovingObject(final L2Character cha) {
|
||||
if (cha == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
_movingObjects.add(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 void moveObjects() {
|
||||
_movingObjects.removeIf(L2Character::updatePosition);
|
||||
}
|
||||
|
||||
public void stopTimer() {
|
||||
super.interrupt();
|
||||
LOG.info("Stopping {}", getClass().getSimpleName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
LOG.debug("{}: Started.", getClass().getSimpleName());
|
||||
|
||||
long nextTickTime, sleepTime;
|
||||
boolean isNight = isNight();
|
||||
|
||||
if (isNight) {
|
||||
ThreadPoolManager.getInstance().executeAi(() -> DayNightSpawnManager.getInstance().notifyChangeMode());
|
||||
}
|
||||
|
||||
while (true) {
|
||||
nextTickTime = System.currentTimeMillis() + MILLIS_IN_TICK;
|
||||
|
||||
try {
|
||||
moveObjects();
|
||||
} catch (final Throwable e) {
|
||||
LOG.warn("Unable to move objects!", 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 GameTimeController getInstance() {
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,582 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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 static com.l2jserver.gameserver.config.Configuration.geodata;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.l2jserver.gameserver.data.xml.impl.DoorData;
|
||||
import com.l2jserver.gameserver.model.L2Object;
|
||||
import com.l2jserver.gameserver.model.L2World;
|
||||
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;
|
||||
import com.l2jserver.geodriver.Cell;
|
||||
import com.l2jserver.geodriver.GeoDriver;
|
||||
|
||||
/**
|
||||
* Geodata.
|
||||
* @author -Nemesiss-
|
||||
* @author HorridoJoho
|
||||
*/
|
||||
public class GeoData {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(GeoData.class);
|
||||
|
||||
private static final String FILE_NAME_FORMAT = "%d_%d.l2j";
|
||||
|
||||
private static final int ELEVATED_SEE_OVER_DISTANCE = 2;
|
||||
|
||||
private static final int MAX_SEE_OVER_HEIGHT = 48;
|
||||
|
||||
private static final int SPAWN_Z_DELTA_LIMIT = 100;
|
||||
|
||||
private static final Map<String, Boolean> GEODATA_REGIONS = new HashMap<>();
|
||||
|
||||
private final GeoDriver _driver = new GeoDriver();
|
||||
|
||||
protected GeoData() {
|
||||
loadGeodataRegions();
|
||||
|
||||
int loadedRegions = 0;
|
||||
try {
|
||||
for (int regionX = L2World.TILE_X_MIN; regionX <= L2World.TILE_X_MAX; regionX++) {
|
||||
for (int regionY = L2World.TILE_Y_MIN; regionY <= L2World.TILE_Y_MAX; regionY++) {
|
||||
final Path geoFilePath = geodata().getGeoDataPath().toPath().resolve(String.format(FILE_NAME_FORMAT, regionX, regionY));
|
||||
final Boolean loadFile = GEODATA_REGIONS.get(regionX + "_" + regionY);
|
||||
if (loadFile != null) {
|
||||
if (loadFile) {
|
||||
LOG.info("Loading {}...", geoFilePath.getFileName());
|
||||
_driver.loadRegion(geoFilePath, regionX, regionY);
|
||||
loadedRegions++;
|
||||
}
|
||||
} else if (geodata().tryLoadUnspecifiedRegions() && Files.exists(geoFilePath)) {
|
||||
try {
|
||||
LOG.info("Loading {}...", geoFilePath.getFileName());
|
||||
_driver.loadRegion(geoFilePath, regionX, regionY);
|
||||
loadedRegions++;
|
||||
} catch (Exception ex) {
|
||||
LOG.warn("Failed to load {}!", geoFilePath.getFileName(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LOG.error("Failed to load geodata!", ex);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
LOG.info("Loaded {} regions.", loadedRegions);
|
||||
}
|
||||
|
||||
private static void loadGeodataRegions() {
|
||||
for (int regionX = L2World.TILE_X_MIN; regionX <= L2World.TILE_X_MAX; regionX++) {
|
||||
for (int regionY = L2World.TILE_Y_MIN; regionY <= L2World.TILE_Y_MAX; regionY++) {
|
||||
String key = regionX + "_" + regionY;
|
||||
if (geodata().getIncludedRegions().contains(regionX + "_" + regionY)) {
|
||||
GEODATA_REGIONS.put(key, true);
|
||||
} else if (geodata().getExcludedRegions().contains(regionX + "_" + regionY)) {
|
||||
GEODATA_REGIONS.put(key, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasGeoPos(int geoX, int geoY) {
|
||||
return _driver.hasGeoPos(geoX, geoY);
|
||||
}
|
||||
|
||||
public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) {
|
||||
return _driver.checkNearestNswe(geoX, geoY, worldZ, nswe);
|
||||
}
|
||||
|
||||
public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) {
|
||||
boolean can = true;
|
||||
if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) {
|
||||
// can = canEnterNeighbors(prevX, prevY - 1, prevGeoZ, Direction.EAST) && canEnterNeighbors(prevX + 1, prevY, prevGeoZ, Direction.NORTH);
|
||||
can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH);
|
||||
}
|
||||
|
||||
if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) {
|
||||
// can = canEnterNeighbors(prevX, prevY - 1, prevGeoZ, Direction.WEST) && canEnterNeighbors(prevX - 1, prevY, prevGeoZ, Direction.NORTH);
|
||||
can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_NORTH);
|
||||
}
|
||||
|
||||
if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) {
|
||||
// can = canEnterNeighbors(prevX, prevY + 1, prevGeoZ, Direction.EAST) && canEnterNeighbors(prevX + 1, prevY, prevGeoZ, Direction.SOUTH);
|
||||
can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH);
|
||||
}
|
||||
|
||||
if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) {
|
||||
// can = canEnterNeighbors(prevX, prevY + 1, prevGeoZ, Direction.WEST) && canEnterNeighbors(prevX - 1, prevY, prevGeoZ, Direction.SOUTH);
|
||||
can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH);
|
||||
}
|
||||
|
||||
return can && checkNearestNswe(geoX, geoY, worldZ, nswe);
|
||||
}
|
||||
|
||||
public int getNearestZ(int geoX, int geoY, int worldZ) {
|
||||
return _driver.getNearestZ(geoX, geoY, worldZ);
|
||||
}
|
||||
|
||||
public int getNextLowerZ(int geoX, int geoY, int worldZ) {
|
||||
return _driver.getNextLowerZ(geoX, geoY, worldZ);
|
||||
}
|
||||
|
||||
public int getNextHigherZ(int geoX, int geoY, int worldZ) {
|
||||
return _driver.getNextHigherZ(geoX, geoY, worldZ);
|
||||
}
|
||||
|
||||
public int getGeoX(int worldX) {
|
||||
return _driver.getGeoX(worldX);
|
||||
}
|
||||
|
||||
public int getGeoY(int worldY) {
|
||||
return _driver.getGeoY(worldY);
|
||||
}
|
||||
|
||||
public int getWorldX(int geoX) {
|
||||
return _driver.getWorldX(geoX);
|
||||
}
|
||||
|
||||
public int getWorldY(int geoY) {
|
||||
return _driver.getWorldY(geoY);
|
||||
}
|
||||
|
||||
// ///////////////////
|
||||
// L2J METHODS
|
||||
/**
|
||||
* 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) {
|
||||
final int geoX = getGeoX(x);
|
||||
final int geoY = getGeoY(y);
|
||||
|
||||
if (!hasGeoPos(geoX, geoY)) {
|
||||
return z;
|
||||
}
|
||||
|
||||
int nextLowerZ = getNextLowerZ(geoX, geoY, z + 20);
|
||||
return Math.abs(nextLowerZ - z) <= SPAWN_Z_DELTA_LIMIT ? nextLowerZ : z;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 == null) {
|
||||
return false;
|
||||
}
|
||||
if (target.isDoor()) {
|
||||
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 (DoorData.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, int nswe) {
|
||||
if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) {
|
||||
throw new RuntimeException("Multiple directions!");
|
||||
}
|
||||
|
||||
if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) {
|
||||
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);
|
||||
|
||||
// fast path
|
||||
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;
|
||||
|
||||
// check if the position has geodata
|
||||
if (hasGeoPos(curX, curY)) {
|
||||
int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY);
|
||||
curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe);
|
||||
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) {
|
||||
if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) {
|
||||
int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST);
|
||||
int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH);
|
||||
canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ));
|
||||
} else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) {
|
||||
int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST);
|
||||
int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH);
|
||||
canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ));
|
||||
} else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) {
|
||||
int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST);
|
||||
int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH);
|
||||
canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ));
|
||||
} else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) {
|
||||
int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST);
|
||||
int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH);
|
||||
canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ));
|
||||
} else {
|
||||
canSeeThrough = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!canSeeThrough) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
prevX = curX;
|
||||
prevY = curY;
|
||||
prevGeoZ = curGeoZ;
|
||||
++ptIndex;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies if the is a path between origin's location and destination, if not returns the closest location.
|
||||
* @param origin the origin
|
||||
* @param destination the destination
|
||||
* @return the destination if there is a path or the closes location
|
||||
*/
|
||||
public Location moveCheck(ILocational origin, ILocational destination) {
|
||||
return moveCheck(origin.getX(), origin.getY(), origin.getZ(), destination.getX(), destination.getY(), destination.getZ(), origin.getInstanceId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (DoorData.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)) {
|
||||
int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY);
|
||||
if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) {
|
||||
// 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 (DoorData.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)) {
|
||||
int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY);
|
||||
if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
prevX = curX;
|
||||
prevY = curY;
|
||||
prevZ = curZ;
|
||||
}
|
||||
|
||||
// different floors
|
||||
return !hasGeoPos(prevX, prevY) || (prevZ == toZ);
|
||||
}
|
||||
|
||||
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 static final GeoData INSTANCE = new GeoData();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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();
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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 static com.l2jserver.gameserver.config.Configuration.general;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
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 Map<Integer, L2ItemInstance> _items = new ConcurrentHashMap<>();
|
||||
|
||||
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.put(item.getObjectId(), item);
|
||||
}
|
||||
|
||||
public synchronized void removeItems() {
|
||||
final long curtime = System.currentTimeMillis();
|
||||
for (L2ItemInstance item : _items.values()) {
|
||||
if (item == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((item.getDropTime() == 0) || (item.getItemLocation() != ItemLocation.VOID)) {
|
||||
_items.remove(item.getObjectId());
|
||||
} 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.getObjectId());
|
||||
if (general().saveDroppedItem()) {
|
||||
ItemsOnGroundManager.getInstance().removeObject(item);
|
||||
}
|
||||
}
|
||||
} else if (item.getItem().hasExImmediateEffect()) {
|
||||
if ((curtime - item.getDropTime()) > general().getAutoDestroyHerbTime()) {
|
||||
L2World.getInstance().removeVisibleObject(item, item.getWorldRegion());
|
||||
L2World.getInstance().removeObject(item);
|
||||
_items.remove(item.getObjectId());
|
||||
if (general().saveDroppedItem()) {
|
||||
ItemsOnGroundManager.getInstance().removeObject(item);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
final long sleep = ((general().getAutoDestroyDroppedItemAfter() == 0) ? 3600000 : general().getAutoDestroyDroppedItemAfter() * 1000);
|
||||
|
||||
if ((curtime - item.getDropTime()) > sleep) {
|
||||
L2World.getInstance().removeVisibleObject(item, item.getWorldRegion());
|
||||
L2World.getInstance().removeObject(item);
|
||||
_items.remove(item.getObjectId());
|
||||
if (general().saveDroppedItem()) {
|
||||
ItemsOnGroundManager.getInstance().removeObject(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class SingletonHolder {
|
||||
protected static final ItemsAutoDestroy _instance = new ItemsAutoDestroy();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,705 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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 static com.l2jserver.gameserver.config.Configuration.general;
|
||||
import static com.l2jserver.gameserver.config.Configuration.hexId;
|
||||
import static com.l2jserver.gameserver.config.Configuration.ip;
|
||||
import static com.l2jserver.gameserver.config.Configuration.server;
|
||||
import static java.nio.file.StandardOpenOption.CREATE;
|
||||
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
|
||||
|
||||
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.nio.file.Files;
|
||||
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.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.l2jserver.commons.database.ConnectionFactory;
|
||||
import com.l2jserver.commons.network.BaseSendablePacket;
|
||||
import com.l2jserver.commons.security.crypt.NewCrypt;
|
||||
import com.l2jserver.commons.util.Rnd;
|
||||
import com.l2jserver.commons.util.Util;
|
||||
import com.l2jserver.gameserver.config.Configuration;
|
||||
import com.l2jserver.gameserver.config.HexIdConfiguration;
|
||||
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;
|
||||
|
||||
public class LoginServerThread extends Thread {
|
||||
|
||||
protected static final Logger LOG = LoggerFactory.getLogger(LoginServerThread.class);
|
||||
|
||||
protected static final Logger LOG_ACCOUNTING = LoggerFactory.getLogger("accounting");
|
||||
|
||||
private static final int TEENAGER = 15;
|
||||
|
||||
private static final int ADULT = 18;
|
||||
|
||||
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 final byte[] _hexID;
|
||||
private final boolean _acceptAlternate;
|
||||
private final int _requestID;
|
||||
private final boolean _reserveHost;
|
||||
private int _maxPlayer;
|
||||
private final List<WaitingClient> _waitingClients;
|
||||
private final Map<String, L2GameClient> _accountsInGameServer = new ConcurrentHashMap<>();
|
||||
private int _status;
|
||||
private String _serverName;
|
||||
private final List<String> _subnets;
|
||||
private final List<String> _hosts;
|
||||
|
||||
protected LoginServerThread() {
|
||||
super("LoginServerThread");
|
||||
_port = server().getLoginPort();
|
||||
_gamePort = server().getPort();
|
||||
_hostname = server().getLoginHost();
|
||||
if (hexId().getHexID() == null) {
|
||||
_hexID = Util.generateHex(16);
|
||||
_requestID = server().getRequestServerId();
|
||||
hexId().setProperty("ServerID", String.valueOf(_requestID));
|
||||
} else {
|
||||
_hexID = hexId().getHexID().toByteArray();
|
||||
_requestID = hexId().getServerID();
|
||||
}
|
||||
_acceptAlternate = server().acceptAlternateId();
|
||||
_reserveHost = server().reserveHostOnLogin();
|
||||
_subnets = ip().getSubnets();
|
||||
_hosts = ip().getHosts();
|
||||
_waitingClients = new CopyOnWriteArrayList<>();
|
||||
_maxPlayer = server().getMaxOnlineUsers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (!isInterrupted()) {
|
||||
int lengthHi;
|
||||
int lengthLo;
|
||||
int length;
|
||||
boolean checksumOk;
|
||||
try {
|
||||
// Connection
|
||||
LOG.info("Connecting to login server on {}:{}", _hostname, _port);
|
||||
_loginSocket = new Socket(_hostname, _port);
|
||||
InputStream in = _loginSocket.getInputStream();
|
||||
_out = new BufferedOutputStream(_loginSocket.getOutputStream());
|
||||
|
||||
// init Blowfish
|
||||
byte[] blowfishKey = Util.generateHex(40);
|
||||
// Protect the new blowfish key what cannot begin with zero
|
||||
if (blowfishKey[0] == 0) {
|
||||
blowfishKey[0] = (byte) Rnd.get(32, 64);
|
||||
}
|
||||
_blowfish = new NewCrypt("_;v.]05-31!|+-%xT!^[$\00");
|
||||
while (!isInterrupted()) {
|
||||
lengthLo = in.read();
|
||||
lengthHi = in.read();
|
||||
length = (lengthHi * 256) + lengthLo;
|
||||
|
||||
if (lengthHi < 0) {
|
||||
LOG.info("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.warn("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.warn("Incorrect packet checksum, ignoring packet (LS)");
|
||||
break;
|
||||
}
|
||||
|
||||
int packetType = incoming[0] & 0xff;
|
||||
// send the blowfish key through the rsa encryption
|
||||
// now, only accept packet with the new encryption
|
||||
// login will close the connection here
|
||||
switch (packetType) {
|
||||
case 0x00 -> {
|
||||
InitLS init = new InitLS(incoming);
|
||||
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.warn("Trouble while init the public key send by login");
|
||||
break;
|
||||
}
|
||||
sendPacket(new BlowFishKey(blowfishKey, publicKey));
|
||||
_blowfish = new NewCrypt(blowfishKey);
|
||||
sendPacket(new AuthRequest(_requestID, _acceptAlternate, _hexID, _gamePort, _reserveHost, _maxPlayer, _subnets, _hosts));
|
||||
}
|
||||
case 0x01 -> {
|
||||
LoginServerFail lsf = new LoginServerFail(incoming);
|
||||
LOG.info("Damn! Registration Failed: {}", lsf.getReasonString());
|
||||
}
|
||||
case 0x02 -> {
|
||||
AuthResponse aresp = new AuthResponse(incoming);
|
||||
int serverID = aresp.getServerId();
|
||||
_serverName = aresp.getServerName();
|
||||
saveHexid(serverID, hexToString(_hexID));
|
||||
LOG.info("Registered on login as Server {}: {}", serverID, _serverName);
|
||||
ServerStatus st = new ServerStatus();
|
||||
if (general().getServerListBrackets()) {
|
||||
st.addAttribute(ServerStatus.SERVER_LIST_SQUARE_BRACKET, ServerStatus.ON);
|
||||
} else {
|
||||
st.addAttribute(ServerStatus.SERVER_LIST_SQUARE_BRACKET, ServerStatus.OFF);
|
||||
}
|
||||
st.addAttribute(ServerStatus.SERVER_TYPE, general().getServerListType());
|
||||
if (general().serverGMOnly()) {
|
||||
st.addAttribute(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_GM_ONLY);
|
||||
} else {
|
||||
st.addAttribute(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_AUTO);
|
||||
}
|
||||
if (general().getServerListAge() == TEENAGER) {
|
||||
st.addAttribute(ServerStatus.SERVER_AGE, ServerStatus.SERVER_AGE_15);
|
||||
} else if (general().getServerListAge() == ADULT) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
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.warn("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);
|
||||
}
|
||||
}
|
||||
case 0x04 -> {
|
||||
KickPlayer kp = new KickPlayer(incoming);
|
||||
doKickPlayer(kp.getAccount());
|
||||
}
|
||||
case 0x05 -> {
|
||||
RequestCharacters rc = new RequestCharacters(incoming);
|
||||
getCharsOnServer(rc.getAccount());
|
||||
}
|
||||
case 0x06 -> new ChangePasswordResponse(incoming);
|
||||
}
|
||||
}
|
||||
} catch (UnknownHostException e) {
|
||||
LOG.warn("Unknown host!", e);
|
||||
} catch (SocketException e) {
|
||||
LOG.warn("LoginServer not avaible, trying to reconnect...");
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Disconnected from Login, Trying to reconnect!", 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.warn("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.warn("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) {
|
||||
LOG_ACCOUNTING.warn("Kicked by login: {}", client);
|
||||
client.setAdditionalClosePacket(SystemMessage.getSystemMessage(SystemMessageId.ANOTHER_LOGIN_WITH_ACCOUNT));
|
||||
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 (var con = ConnectionFactory.getInstance().getConnection();
|
||||
var ps = con.prepareStatement("SELECT deletetime FROM characters WHERE account_name=?")) {
|
||||
ps.setString(1, account);
|
||||
try (var rs = ps.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
chars++;
|
||||
long delTime = rs.getLong("deletetime");
|
||||
if (delTime != 0) {
|
||||
charToDel.add(delTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOG.warn("Exception: getCharsOnServer!", 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, general().getServerListType());
|
||||
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;
|
||||
}
|
||||
case ServerStatus.STATUS_DOWN -> {
|
||||
sendServerStatus(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_DOWN);
|
||||
_status = status;
|
||||
}
|
||||
case ServerStatus.STATUS_FULL -> {
|
||||
sendServerStatus(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_FULL);
|
||||
_status = status;
|
||||
}
|
||||
case ServerStatus.STATUS_GM_ONLY -> {
|
||||
sendServerStatus(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_GM_ONLY);
|
||||
_status = status;
|
||||
}
|
||||
case ServerStatus.STATUS_GOOD -> {
|
||||
sendServerStatus(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_GOOD);
|
||||
_status = status;
|
||||
}
|
||||
case ServerStatus.STATUS_NORMAL -> {
|
||||
sendServerStatus(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_NORMAL);
|
||||
_status = status;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save hexadecimal ID of the server in the L2Properties file.
|
||||
* @param serverId the ID of the server whose hexId to save
|
||||
* @param hexId the hexadecimal ID to store
|
||||
*/
|
||||
public static void saveHexid(int serverId, String hexId) {
|
||||
final var hexIdFilePath = Configuration.getCustomOrDefaultPath(HexIdConfiguration.FILENAME);
|
||||
hexId().setProperty(HexIdConfiguration.SERVERID_KEY, String.valueOf(serverId));
|
||||
hexId().setProperty(HexIdConfiguration.HEXID_KEY, hexId);
|
||||
|
||||
try {
|
||||
Files.createDirectories(hexIdFilePath.getParent());
|
||||
try (var out = Files.newOutputStream(hexIdFilePath, CREATE, TRUNCATE_EXISTING)) {
|
||||
hexId().store(out, "the hexID to auth into login");
|
||||
LOG.info("Saved {}.", hexIdFilePath);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LOG.warn("Failed to save {}.", hexIdFilePath, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the single instance of LoginServerThread.
|
||||
* @return single instance of LoginServerThread
|
||||
*/
|
||||
public static LoginServerThread getInstance() {
|
||||
return SingletonHolder._instance;
|
||||
}
|
||||
|
||||
private static class SingletonHolder {
|
||||
protected static final LoginServerThread _instance = new LoginServerThread();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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 org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.l2jserver.commons.util.Rnd;
|
||||
import com.l2jserver.gameserver.data.xml.impl.NpcData;
|
||||
import com.l2jserver.gameserver.idfactory.IdFactory;
|
||||
import com.l2jserver.gameserver.model.actor.L2Npc;
|
||||
|
||||
public class MonsterRace {
|
||||
protected static final Logger LOG = LoggerFactory.getLogger(MonsterRace.class);
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
try {
|
||||
final var objectId = IdFactory.getInstance().getNextId();
|
||||
final var template = NpcData.getInstance().getTemplate(id + random);
|
||||
final var constructor = Class.forName("com.l2jserver.gameserver.model.actor.instance." + template.getType() + "Instance").getConstructors()[0];
|
||||
_monsters[i] = (L2Npc) constructor.newInstance(objectId, template);
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Unable to create monster!", e);
|
||||
}
|
||||
}
|
||||
newSpeeds();
|
||||
}
|
||||
|
||||
public void newSpeeds() {
|
||||
_speeds = new int[8][20];
|
||||
int total;
|
||||
_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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,664 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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 static com.l2jserver.gameserver.config.Configuration.character;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.l2jserver.commons.util.Rnd;
|
||||
import com.l2jserver.gameserver.data.xml.impl.RecipeData;
|
||||
import com.l2jserver.gameserver.datatables.ItemTable;
|
||||
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.network.SystemMessageId;
|
||||
import com.l2jserver.gameserver.network.serverpackets.ActionFailed;
|
||||
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;
|
||||
|
||||
public class RecipeController {
|
||||
protected static final Map<Integer, RecipeItemMaker> _activeMakers = new ConcurrentHashMap<>();
|
||||
|
||||
protected RecipeController() {
|
||||
// Prevent external initialization.
|
||||
}
|
||||
|
||||
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.CANT_ALTER_RECIPEBOOK_WHILE_CRAFTING);
|
||||
}
|
||||
|
||||
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.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if manufacturer is under manufacturing store or private store.
|
||||
if (character().alternativeCrafting() && _activeMakers.containsKey(manufacturer.getObjectId())) {
|
||||
player.sendPacket(SystemMessageId.CLOSE_STORE_WINDOW_AND_TRY_AGAIN);
|
||||
return;
|
||||
}
|
||||
|
||||
final RecipeItemMaker maker = new RecipeItemMaker(manufacturer, recipeList, player);
|
||||
if (maker._isValid) {
|
||||
if (character().alternativeCrafting()) {
|
||||
_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.CANT_OPERATE_PRIVATE_STORE_DURING_COMBAT);
|
||||
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.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if player is busy (possible if alt game creation is enabled)
|
||||
if (character().alternativeCrafting() && _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 (character().alternativeCrafting()) {
|
||||
_activeMakers.put(player.getObjectId(), maker);
|
||||
ThreadPoolManager.getInstance().scheduleGeneral(maker, 100);
|
||||
} else {
|
||||
maker.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class RecipeItemMaker implements Runnable {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RecipeItemMaker.class);
|
||||
|
||||
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_NOT_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 (character().alternativeCrafting()) {
|
||||
calculateAltStatChange();
|
||||
}
|
||||
|
||||
updateMakeInfo(true);
|
||||
updateCurMp();
|
||||
updateCurLoad();
|
||||
|
||||
_player.isInCraftMode(false);
|
||||
_isValid = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!character().crafting()) {
|
||||
_target.sendMessage("Item creation is currently disabled.");
|
||||
abort();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_player == null) {
|
||||
LOG.warn("Cannot perform craft, player is null!");
|
||||
abort();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_target == null) {
|
||||
LOG.warn("Cannot perform craft, target is null!");
|
||||
abort();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_player.isOnline()) {
|
||||
LOG.warn("Cannot perform craft, player is not online!");
|
||||
abort();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_target.isOnline()) {
|
||||
LOG.warn("Cannot perform craft, target is not online!");
|
||||
abort();
|
||||
return;
|
||||
}
|
||||
|
||||
if (character().alternativeCrafting() && !_activeMakers.containsKey(_player.getObjectId())) {
|
||||
if (_target != _player) {
|
||||
_target.sendMessage("Manufacture aborted!");
|
||||
_player.sendMessage("Manufacture aborted!");
|
||||
} else {
|
||||
_player.sendMessage("Item creation aborted!");
|
||||
}
|
||||
|
||||
abort();
|
||||
return;
|
||||
}
|
||||
|
||||
if (character().alternativeCrafting() && !_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) (character().getCraftingSpeed() * _player.getMReuseRate(_skill) * GameTimeController.TICKS_PER_SECOND * GameTimeController.MILLIS_IN_TICK);
|
||||
|
||||
// FIXME: please fix this packet to show crafting animation (somebody)
|
||||
_player.broadcastPacket(new MagicSkillUse(_player, _skillId, _skillLevel, _delay, 0));
|
||||
|
||||
_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 (!character().alternativeCrafting()) {
|
||||
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_NOT_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.CREATION_OF_S2_FOR_C1_AT_S3_ADENA_FAILED);
|
||||
msg.addString(_target.getName());
|
||||
msg.addItemName(_recipeList.getItemId());
|
||||
msg.addLong(_price);
|
||||
_player.sendPacket(msg);
|
||||
|
||||
msg = SystemMessage.getSystemMessage(SystemMessageId.C1_FAILED_TO_CREATE_S2_FOR_S3_ADENA);
|
||||
msg.addString(_player.getName());
|
||||
msg.addItemName(_recipeList.getItemId());
|
||||
msg.addLong(_price);
|
||||
_target.sendPacket(msg);
|
||||
} else {
|
||||
_target.sendPacket(SystemMessageId.ITEM_MIXING_FAILED);
|
||||
}
|
||||
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() {
|
||||
StatusUpdate su = new StatusUpdate(_target);
|
||||
su.addAttribute(StatusUpdate.CUR_LOAD, _target.getCurrentLoad());
|
||||
_target.sendPacket(su);
|
||||
}
|
||||
|
||||
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.S1_S2_EQUIPPED); // 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 (character().alternativeCrafting() && 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 (character().alternativeCrafting() && 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.MISSING_S2_S1_TO_CREATE);
|
||||
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_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) || character().craftMasterwork())) {
|
||||
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;
|
||||
if (_target != _player) {
|
||||
// inform manufacturer of earned profit
|
||||
if (itemCount == 1) {
|
||||
sm = SystemMessage.getSystemMessage(SystemMessageId.S2_CREATED_FOR_C1_FOR_S3_ADENA);
|
||||
sm.addString(_target.getName());
|
||||
sm.addItemName(itemId);
|
||||
sm.addLong(_price);
|
||||
_player.sendPacket(sm);
|
||||
|
||||
sm = SystemMessage.getSystemMessage(SystemMessageId.C1_CREATED_S2_FOR_S3_ADENA);
|
||||
sm.addString(_player.getName());
|
||||
sm.addItemName(itemId);
|
||||
sm.addLong(_price);
|
||||
_target.sendPacket(sm);
|
||||
} else {
|
||||
sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S3_S_CREATED_FOR_C1_FOR_S4_ADENA);
|
||||
sm.addString(_target.getName());
|
||||
sm.addInt(itemCount);
|
||||
sm.addItemName(itemId);
|
||||
sm.addLong(_price);
|
||||
_player.sendPacket(sm);
|
||||
|
||||
sm = SystemMessage.getSystemMessage(SystemMessageId.C1_CREATED_S2_S3_S_FOR_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.EARNED_S2_S1_S);
|
||||
sm.addItemName(itemId);
|
||||
sm.addLong(itemCount);
|
||||
_target.sendPacket(sm);
|
||||
} else {
|
||||
sm = SystemMessage.getSystemMessage(SystemMessageId.EARNED_ITEM_S1);
|
||||
sm.addItemName(itemId);
|
||||
_target.sendPacket(sm);
|
||||
}
|
||||
|
||||
if (character().alternativeCrafting()) {
|
||||
int recipeLevel = _recipeList.getLevel();
|
||||
if (_exp < 0) {
|
||||
_exp = template.getReferencePrice() * itemCount;
|
||||
_exp /= recipeLevel;
|
||||
}
|
||||
if (_sp < 0) {
|
||||
_sp = _exp / 10;
|
||||
}
|
||||
if (itemId == rareProdId) {
|
||||
_exp *= character().getCraftingRareXpRate();
|
||||
_sp *= character().getCraftingRareSpRate();
|
||||
}
|
||||
|
||||
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((long) (_exp * character().getCraftingXpRate() * character().getCraftingSpeed()), //
|
||||
(int) (_sp * character().getCraftingSpRate() * character().getCraftingSpeed()));
|
||||
}
|
||||
updateMakeInfo(true); // success
|
||||
}
|
||||
}
|
||||
|
||||
public static RecipeController getInstance() {
|
||||
return SingletonHolder._instance;
|
||||
}
|
||||
|
||||
private static class SingletonHolder {
|
||||
protected static final RecipeController _instance = new RecipeController();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,517 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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 static com.l2jserver.gameserver.config.Configuration.customs;
|
||||
import static com.l2jserver.gameserver.config.Configuration.general;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.l2jserver.commons.UPnPService;
|
||||
import com.l2jserver.commons.database.ConnectionFactory;
|
||||
import com.l2jserver.gameserver.data.sql.impl.ClanTable;
|
||||
import com.l2jserver.gameserver.data.sql.impl.OfflineTradersTable;
|
||||
import com.l2jserver.gameserver.datatables.BotReportTable;
|
||||
import com.l2jserver.gameserver.instancemanager.CastleManorManager;
|
||||
import com.l2jserver.gameserver.instancemanager.ClanHallSiegeManager;
|
||||
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.
|
||||
* @since 2005/03/27 15:29:09
|
||||
*/
|
||||
public class Shutdown extends Thread {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Shutdown.class);
|
||||
|
||||
private static Shutdown _counterInstance = null;
|
||||
|
||||
private int _secondsShut;
|
||||
private int _shutdownMode;
|
||||
private static final int SIGTERM = 0;
|
||||
private static final int GM_SHUTDOWN = 1;
|
||||
private static final int GM_RESTART = 2;
|
||||
private 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_SECONDS);
|
||||
sysm.addInt(seconds);
|
||||
Broadcast.toAllOnlinePlayers(sysm);
|
||||
}
|
||||
|
||||
public void startTelnetShutdown(String IP, int seconds, boolean restart) {
|
||||
LOG.warn("IP: {} issued shutdown command. {} in {} seconds!", IP, MODE_TEXT[_shutdownMode], seconds);
|
||||
|
||||
if (restart) {
|
||||
_shutdownMode = GM_RESTART;
|
||||
} else {
|
||||
_shutdownMode = GM_SHUTDOWN;
|
||||
}
|
||||
|
||||
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.warn("IP: {} issued shutdown ABORT. {} has been stopped!", IP, MODE_TEXT[_shutdownMode]);
|
||||
|
||||
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 ({}ms).", tc.getEstimatedTimeAndRestartCounter());
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Error while removing UPnP port mappings!", e);
|
||||
}
|
||||
|
||||
try {
|
||||
if ((customs().offlineTradeEnable() || customs().offlineCraftEnable()) && customs().restoreOffliners()) {
|
||||
OfflineTradersTable.getInstance().storeOffliners();
|
||||
LOG.info("Offline Traders Table: Offline shops stored({}ms).", tc.getEstimatedTimeAndRestartCounter());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Error saving offline shops!", e);
|
||||
}
|
||||
|
||||
try {
|
||||
disconnectAllCharacters();
|
||||
LOG.info("All players disconnected and saved({}ms).", tc.getEstimatedTimeAndRestartCounter());
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// ensure all services are stopped
|
||||
try {
|
||||
GameTimeController.getInstance().stopTimer();
|
||||
LOG.info("Game Time Controller: Timer stopped({}ms).", tc.getEstimatedTimeAndRestartCounter());
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// stop all thread pools
|
||||
try {
|
||||
ThreadPoolManager.getInstance().shutdown();
|
||||
LOG.info("Thread Pool Manager: Manager has been shut down({}ms).", tc.getEstimatedTimeAndRestartCounter());
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
try {
|
||||
LoginServerThread.getInstance().interrupt();
|
||||
LOG.info("Login Server Thread: Thread interrupted({}ms).", tc.getEstimatedTimeAndRestartCounter());
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// last bye bye, 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({}ms).", tc.getEstimatedTimeAndRestartCounter());
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// commit data, last chance
|
||||
ConnectionFactory.getInstance().close();
|
||||
LOG.info("ConnectionFactory: Database connection has been shutdown({}ms).", tc.getEstimatedTimeAndRestartCounter());
|
||||
|
||||
// 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 {}seconds.", TimeUnit.MILLISECONDS.toSeconds(tc1.getEstimatedTime()));
|
||||
} else {
|
||||
// gm shutdown: send warnings and then call exit to start shutdown sequence
|
||||
countdown();
|
||||
// last point where logging is operational :(
|
||||
LOG.warn("GM shutdown countdown is over. {} NOW!", MODE_TEXT[_shutdownMode]);
|
||||
switch (_shutdownMode) {
|
||||
case GM_SHUTDOWN -> {
|
||||
getInstance().setMode(GM_SHUTDOWN);
|
||||
System.exit(0);
|
||||
}
|
||||
case GM_RESTART -> {
|
||||
getInstance().setMode(GM_RESTART);
|
||||
System.exit(2);
|
||||
}
|
||||
case ABORT -> LoginServerThread.getInstance().setServerStatus(ServerStatus.STATUS_AUTO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.warn("GM: {}({}) issued shutdown command. {} in {} seconds!", activeChar.getName(), activeChar.getObjectId(), MODE_TEXT[_shutdownMode], 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.warn("GM: {}({}) issued shutdown ABORT. {} has been stopped!", activeChar.getName(), activeChar.getObjectId(), MODE_TEXT[_shutdownMode]);
|
||||
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);
|
||||
case 480 -> SendServerQuit(480);
|
||||
case 420 -> SendServerQuit(420);
|
||||
case 360 -> SendServerQuit(360);
|
||||
case 300 -> SendServerQuit(300);
|
||||
case 240 -> SendServerQuit(240);
|
||||
case 180 -> SendServerQuit(180);
|
||||
case 120 -> SendServerQuit(120);
|
||||
case 60 -> {
|
||||
LoginServerThread.getInstance().setServerStatus(ServerStatus.STATUS_DOWN); // avoids new players from logging in
|
||||
SendServerQuit(60);
|
||||
}
|
||||
case 30 -> SendServerQuit(30);
|
||||
case 10 -> SendServerQuit(10);
|
||||
case 5 -> SendServerQuit(5);
|
||||
case 4 -> SendServerQuit(4);
|
||||
case 3 -> SendServerQuit(3);
|
||||
case 2 -> SendServerQuit(2);
|
||||
case 1 -> SendServerQuit(1);
|
||||
}
|
||||
|
||||
_secondsShut--;
|
||||
|
||||
int delay = 1000; // milliseconds
|
||||
Thread.sleep(delay);
|
||||
|
||||
if (_shutdownMode == ABORT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// this will never happen
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This sends a last bye bye, disconnects all players and saves data.
|
||||
*/
|
||||
private void saveData() {
|
||||
switch (_shutdownMode) {
|
||||
case SIGTERM -> LOG.info("SIGTERM received. Shutting down NOW!");
|
||||
case GM_SHUTDOWN -> LOG.info("GM shutdown received. Shutting down NOW!");
|
||||
case GM_RESTART -> LOG.info("GM restart received. Restarting NOW!");
|
||||
}
|
||||
|
||||
TimeCounter tc = new TimeCounter();
|
||||
// Seven Signs data is now saved along with Festival data.
|
||||
if (!SevenSigns.getInstance().isSealValidationPeriod()) {
|
||||
SevenSignsFestival.getInstance().saveFestivalData(false);
|
||||
LOG.info("SevenSignsFestival: Festival data saved({}ms).", tc.getEstimatedTimeAndRestartCounter());
|
||||
}
|
||||
|
||||
// Save Seven Signs data before closing. :)
|
||||
SevenSigns.getInstance().saveSevenSignsData();
|
||||
LOG.info("SevenSigns: Seven Signs data saved({}ms).", tc.getEstimatedTimeAndRestartCounter());
|
||||
SevenSigns.getInstance().saveSevenSignsStatus();
|
||||
LOG.info("SevenSigns: Seven Signs status saved({}ms).", tc.getEstimatedTimeAndRestartCounter());
|
||||
|
||||
// Save all raidboss and GrandBoss status ^_^
|
||||
RaidBossSpawnManager.getInstance().cleanUp();
|
||||
LOG.info("RaidBossSpawnManager: All raidboss info saved({}ms).", tc.getEstimatedTimeAndRestartCounter());
|
||||
GrandBossManager.getInstance().cleanUp();
|
||||
LOG.info("GrandBossManager: All Grand Boss info saved({}ms).", tc.getEstimatedTimeAndRestartCounter());
|
||||
ItemAuctionManager.getInstance().shutdown();
|
||||
LOG.info("Item Auction Manager: All tasks stopped({}ms).", tc.getEstimatedTimeAndRestartCounter());
|
||||
Olympiad.getInstance().saveOlympiadStatus();
|
||||
LOG.info("Olympiad System: Data saved({}ms).", tc.getEstimatedTimeAndRestartCounter());
|
||||
Hero.getInstance().shutdown();
|
||||
LOG.info("Hero System: Data saved({}ms).", tc.getEstimatedTimeAndRestartCounter());
|
||||
ClanTable.getInstance().storeClanScore();
|
||||
LOG.info("Clan System: Data saved({}ms).", tc.getEstimatedTimeAndRestartCounter());
|
||||
|
||||
// Save Cursed Weapons data before closing.
|
||||
CursedWeaponsManager.getInstance().saveData();
|
||||
LOG.info("Cursed Weapons Manager: Data saved({}ms).", tc.getEstimatedTimeAndRestartCounter());
|
||||
|
||||
// Save all manor data
|
||||
if (!general().manorSaveAllActions()) {
|
||||
CastleManorManager.getInstance().storeMe();
|
||||
LOG.info("Castle Manor Manager: Data saved({}ms).", tc.getEstimatedTimeAndRestartCounter());
|
||||
}
|
||||
|
||||
ClanHallSiegeManager.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({}ms).", tc.getEstimatedTimeAndRestartCounter());
|
||||
|
||||
// Save all global variables data
|
||||
GlobalVariablesManager.getInstance().storeMe();
|
||||
LOG.info("Global Variables Manager: Variables saved({}ms).", tc.getEstimatedTimeAndRestartCounter());
|
||||
|
||||
// Save items on ground before closing
|
||||
if (general().saveDroppedItem()) {
|
||||
ItemsOnGroundManager.getInstance().saveInDb();
|
||||
LOG.info("Items On Ground Manager: Data saved({}ms).", tc.getEstimatedTimeAndRestartCounter());
|
||||
ItemsOnGroundManager.getInstance().cleanUp();
|
||||
LOG.info("Items On Ground Manager: Cleaned up({}ms).", tc.getEstimatedTimeAndRestartCounter());
|
||||
}
|
||||
|
||||
// Save bot reports to database
|
||||
if (general().enableBotReportButton()) {
|
||||
BotReportTable.getInstance().saveReportedCharData();
|
||||
LOG.info("Bot Report Table: Successfully 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 (Exception e) {
|
||||
LOG.warn("Failed logout char {}", player, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,630 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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 static com.l2jserver.gameserver.config.Configuration.general;
|
||||
|
||||
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 org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.l2jserver.gameserver.config.Configuration;
|
||||
import com.l2jserver.gameserver.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 periodically.</li>
|
||||
* <li>{@link #scheduleGeneralAtFixedRate(Runnable, long, long, TimeUnit)} and {@link #scheduleGeneralAtFixedRate(Runnable, long, long)} : for scheduled Tasks that needs to be executed periodically.</li>
|
||||
* <li>{@link #scheduleAiAtFixedRate(Runnable, long, long, TimeUnit)} and {@link #scheduleAiAtFixedRate(Runnable, long, long)} : for AI Tasks that needs to be executed periodically</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 = LoggerFactory.getLogger(ThreadPoolManager.class);
|
||||
|
||||
private static final class RunnableWrapper implements Runnable {
|
||||
private final Runnable _r;
|
||||
|
||||
public RunnableWrapper(final Runnable r) {
|
||||
_r = r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final ScheduledThreadPoolExecutor _effectsScheduledThreadPool;
|
||||
private final ScheduledThreadPoolExecutor _generalScheduledThreadPool;
|
||||
private final ScheduledThreadPoolExecutor _aiScheduledThreadPool;
|
||||
private final 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(general().getThreadPoolSizeEffects(), new PriorityThreadFactory("EffectsSTPool", Thread.NORM_PRIORITY));
|
||||
_generalScheduledThreadPool = new ScheduledThreadPoolExecutor(general().getThreadPoolSizeGeneral(), new PriorityThreadFactory("GeneralSTPool", Thread.NORM_PRIORITY));
|
||||
_eventScheduledThreadPool = new ScheduledThreadPoolExecutor(general().getThreadPoolSizeEvents(), new PriorityThreadFactory("EventSTPool", Thread.NORM_PRIORITY));
|
||||
_ioPacketsThreadPool = new ThreadPoolExecutor(general().getUrgentPacketThreadCoreSize(), Integer.MAX_VALUE, 5L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new PriorityThreadFactory("I/O Packet Pool", Thread.NORM_PRIORITY + 1));
|
||||
_generalPacketsThreadPool = new ThreadPoolExecutor(general().getGeneralPacketThreadCoreSize(), general().getGeneralPacketThreadCoreSize() + 2, 15L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new PriorityThreadFactory("Normal Packet Pool", Thread.NORM_PRIORITY + 1));
|
||||
_generalThreadPool = new ThreadPoolExecutor(general().getGeneralThreadCoreSize(), general().getGeneralThreadCoreSize() + 2, 5L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new PriorityThreadFactory("General Pool", Thread.NORM_PRIORITY));
|
||||
_aiScheduledThreadPool = new ScheduledThreadPoolExecutor(general().getAiMaxThread(), new PriorityThreadFactory("AISTPool", Thread.NORM_PRIORITY));
|
||||
_eventThreadPool = new ThreadPoolExecutor(general().getEventsMaxThread(), general().getEventsMaxThread() + 2, 5L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new PriorityThreadFactory("Event Pool", Thread.NORM_PRIORITY));
|
||||
|
||||
scheduleGeneralAtFixedRate(new PurgeTask(_effectsScheduledThreadPool, _generalScheduledThreadPool, _aiScheduledThreadPool, _eventThreadPool), 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(),
|
||||
" | -------"
|
||||
};
|
||||
}
|
||||
|
||||
private static class PriorityThreadFactory implements ThreadFactory {
|
||||
private final int _priority;
|
||||
private final String _name;
|
||||
private final AtomicInteger _threadNumber = new AtomicInteger(1);
|
||||
private final ThreadGroup _group;
|
||||
|
||||
public PriorityThreadFactory(String name, int priority) {
|
||||
_priority = priority;
|
||||
_name = name;
|
||||
_group = new ThreadGroup(_name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread t = new Thread(_group, r, _name + "-" + _threadNumber.getAndIncrement());
|
||||
t.setPriority(_priority);
|
||||
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.warn("There has been a problem shutting down the thread pool manager!", 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 ptf) {
|
||||
int count = ptf.getGroup().activeCount();
|
||||
Thread[] threads = new Thread[count + 2];
|
||||
ptf.getGroup().enumerate(threads);
|
||||
StringUtil.append(sb, "General Packet Thread Pool:" + Configuration.EOL + "Tasks in the queue: ", String.valueOf(_generalPacketsThreadPool.getQueue().size()), Configuration.EOL + "Showing threads stack trace:" + Configuration.EOL + "There should be ", String.valueOf(count), " Threads"
|
||||
+ Configuration.EOL);
|
||||
for (Thread t : threads) {
|
||||
if (t == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
StringUtil.append(sb, t.getName(), Configuration.EOL);
|
||||
for (StackTraceElement ste : t.getStackTrace()) {
|
||||
StringUtil.append(sb, ste.toString(), Configuration.EOL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sb.append("Packet Tp stack traces printed.");
|
||||
sb.append(Configuration.EOL);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String getIOPacketStats() {
|
||||
final StringBuilder sb = new StringBuilder(1000);
|
||||
ThreadFactory tf = _ioPacketsThreadPool.getThreadFactory();
|
||||
|
||||
if (tf instanceof PriorityThreadFactory ptf) {
|
||||
int count = ptf.getGroup().activeCount();
|
||||
Thread[] threads = new Thread[count + 2];
|
||||
ptf.getGroup().enumerate(threads);
|
||||
StringUtil.append(sb, "I/O Packet Thread Pool:" + Configuration.EOL + "Tasks in the queue: ", String.valueOf(_ioPacketsThreadPool.getQueue().size()), Configuration.EOL + "Showing threads stack trace:" + Configuration.EOL + "There should be ", String.valueOf(count), " Threads"
|
||||
+ Configuration.EOL);
|
||||
|
||||
for (Thread t : threads) {
|
||||
if (t == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
StringUtil.append(sb, t.getName(), Configuration.EOL);
|
||||
|
||||
for (StackTraceElement ste : t.getStackTrace()) {
|
||||
StringUtil.append(sb, ste.toString(), Configuration.EOL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sb.append("Packet Tp stack traces printed.");
|
||||
sb.append(Configuration.EOL);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String getGeneralStats() {
|
||||
final StringBuilder sb = new StringBuilder(1000);
|
||||
ThreadFactory tf = _generalThreadPool.getThreadFactory();
|
||||
|
||||
if (tf instanceof PriorityThreadFactory ptf) {
|
||||
int count = ptf.getGroup().activeCount();
|
||||
Thread[] threads = new Thread[count + 2];
|
||||
ptf.getGroup().enumerate(threads);
|
||||
StringUtil.append(sb, "General Thread Pool:" + Configuration.EOL + "Tasks in the queue: ", String.valueOf(_generalThreadPool.getQueue().size()), Configuration.EOL + "Showing threads stack trace:" + Configuration.EOL + "There should be ", String.valueOf(count), " Threads"
|
||||
+ Configuration.EOL);
|
||||
|
||||
for (Thread t : threads) {
|
||||
if (t == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
StringUtil.append(sb, t.getName(), Configuration.EOL);
|
||||
|
||||
for (StackTraceElement ste : t.getStackTrace()) {
|
||||
StringUtil.append(sb, ste.toString(), Configuration.EOL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sb.append("Packet Tp stack traces printed.");
|
||||
sb.append(Configuration.EOL);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
protected static class PurgeTask implements Runnable {
|
||||
private final ScheduledThreadPoolExecutor _effectsScheduled;
|
||||
|
||||
private final ScheduledThreadPoolExecutor _generalScheduled;
|
||||
|
||||
private final ScheduledThreadPoolExecutor _aiScheduled;
|
||||
|
||||
private final ThreadPoolExecutor _eventScheduled;
|
||||
|
||||
PurgeTask(ScheduledThreadPoolExecutor effectsScheduledThreadPool, ScheduledThreadPoolExecutor generalScheduledThreadPool, //
|
||||
ScheduledThreadPoolExecutor aiScheduledThreadPool, ThreadPoolExecutor eventScheduledThreadPool) {
|
||||
_effectsScheduled = effectsScheduledThreadPool;
|
||||
_generalScheduled = generalScheduledThreadPool;
|
||||
_aiScheduled = aiScheduledThreadPool;
|
||||
_eventScheduled = eventScheduledThreadPool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
_effectsScheduled.purge();
|
||||
_generalScheduled.purge();
|
||||
_aiScheduled.purge();
|
||||
_eventScheduled.purge();
|
||||
}
|
||||
}
|
||||
|
||||
private static class SingletonHolder {
|
||||
protected static final ThreadPoolManager _instance = new ThreadPoolManager();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.agathion;
|
||||
|
||||
/**
|
||||
* Agathion.
|
||||
* @author Zoey76
|
||||
* @version 2.6.2.0
|
||||
*/
|
||||
public record Agathion(int npcId, int id, int itemId, int energy, int maxEnergy) {
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.agathion.repository;
|
||||
|
||||
import static com.l2jserver.gameserver.config.Configuration.server;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.l2jserver.gameserver.agathion.Agathion;
|
||||
|
||||
/**
|
||||
* Agathion repository.
|
||||
* @author Zoey76
|
||||
* @version 2.6.3.0
|
||||
*/
|
||||
public class AgathionRepository {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AgathionRepository.class);
|
||||
private static final Gson GSON = new Gson();
|
||||
private static final Type TYPE_LIST_AGATHION = new TypeToken<List<Agathion>>() {
|
||||
}.getType();
|
||||
|
||||
private final Map<Integer, Agathion> agathions = new HashMap<>();
|
||||
private final Map<Integer, Integer> itemIdToNpcId = new HashMap<>();
|
||||
|
||||
private AgathionRepository() {
|
||||
load();
|
||||
}
|
||||
|
||||
private void load() {
|
||||
agathions.clear();
|
||||
itemIdToNpcId.clear();
|
||||
final var file = new File(server().getDatapackRoot(), "data/stats/agathions.json");
|
||||
try (var reader = new JsonReader(new FileReader(file))) {
|
||||
final List<Agathion> loaded = GSON.fromJson(reader, TYPE_LIST_AGATHION);
|
||||
if (loaded != null) {
|
||||
for (var a : loaded) {
|
||||
agathions.put(a.npcId(), a);
|
||||
itemIdToNpcId.put(a.itemId(), a.npcId());
|
||||
}
|
||||
}
|
||||
} catch (FileNotFoundException ex) {
|
||||
LOG.warn("data/stats/agathions.json not found!", ex);
|
||||
} catch (IOException ex) {
|
||||
LOG.warn("Failed to load agathions.json: ", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public Agathion getByNpcId(int npcId) {
|
||||
return agathions.get(npcId);
|
||||
}
|
||||
|
||||
public Agathion getByItemId(int itemId) {
|
||||
final var npcId = itemIdToNpcId.get(itemId);
|
||||
return (npcId != null) ? agathions.get(npcId) : null;
|
||||
}
|
||||
|
||||
public static AgathionRepository getInstance() {
|
||||
return SingletonHolder.INSTANCE;
|
||||
}
|
||||
|
||||
private static class SingletonHolder {
|
||||
static final AgathionRepository INSTANCE = new AgathionRepository();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,717 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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 org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
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>
|
||||
* AbstractAI :<br>
|
||||
* <li>L2CharacterAI</li>
|
||||
*/
|
||||
public abstract class AbstractAI implements Ctrl {
|
||||
protected static final Logger LOG = LoggerFactory.getLogger(AbstractAI.class);
|
||||
|
||||
private NextAction _nextAction;
|
||||
|
||||
public NextAction getNextAction() {
|
||||
return _nextAction;
|
||||
}
|
||||
|
||||
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.warn("{}: There has been a problem running the follow task!", getClass().getSimpleName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** The character that this AI manages */
|
||||
protected final L2Character _actor;
|
||||
|
||||
/** 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 */
|
||||
protected 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 creature the creature
|
||||
*/
|
||||
protected AbstractAI(L2Character creature) {
|
||||
_actor = creature;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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();
|
||||
case AI_INTENTION_ACTIVE -> onIntentionActive();
|
||||
case AI_INTENTION_REST -> onIntentionRest();
|
||||
case AI_INTENTION_ATTACK -> onIntentionAttack((L2Character) arg0);
|
||||
case AI_INTENTION_CAST -> onIntentionCast((Skill) arg0, (L2Object) arg1);
|
||||
case AI_INTENTION_MOVE_TO -> onIntentionMoveTo((Location) arg0);
|
||||
case AI_INTENTION_FOLLOW -> onIntentionFollow((L2Character) arg0);
|
||||
case AI_INTENTION_PICK_UP -> onIntentionPickUp((L2Object) arg0);
|
||||
case AI_INTENTION_INTERACT -> onIntentionInteract((L2Object) arg0);
|
||||
}
|
||||
|
||||
// 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
|
||||
*/
|
||||
@Override
|
||||
public final void notifyEvent(CtrlEvent evt, Object... args) {
|
||||
if ((!_actor.isVisible() && !_actor.isTeleporting()) || !_actor.hasAI()) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (evt) {
|
||||
case EVT_THINK:
|
||||
onEvtThink();
|
||||
break;
|
||||
case EVT_ATTACKED:
|
||||
onEvtAttacked((L2Character) args[0]);
|
||||
break;
|
||||
case EVT_AGGRESSION:
|
||||
onEvtAggression((L2Character) args[0], ((Number) args[1]).intValue());
|
||||
break;
|
||||
case EVT_STUNNED:
|
||||
onEvtStunned((L2Character) args[0]);
|
||||
break;
|
||||
case EVT_SLEEPING:
|
||||
onEvtSleeping((L2Character) args[0]);
|
||||
break;
|
||||
case EVT_ROOTED:
|
||||
onEvtRooted((L2Character) args[0]);
|
||||
break;
|
||||
case EVT_CONFUSED:
|
||||
onEvtConfused((L2Character) args[0]);
|
||||
break;
|
||||
case EVT_MUTED:
|
||||
onEvtMuted((L2Character) args[0]);
|
||||
break;
|
||||
case EVT_EVADED:
|
||||
onEvtEvaded((L2Character) args[0]);
|
||||
break;
|
||||
case EVT_READY_TO_ACT:
|
||||
if (!_actor.isCastingNow() && !_actor.isCastingSimultaneouslyNow()) {
|
||||
onEvtReadyToAct();
|
||||
}
|
||||
break;
|
||||
case EVT_USER_CMD:
|
||||
onEvtUserCmd(args[0], args[1]);
|
||||
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) args[0]);
|
||||
break;
|
||||
case EVT_FORGET_OBJECT:
|
||||
onEvtForgetObject((L2Object) args[0]);
|
||||
break;
|
||||
case EVT_CANCEL:
|
||||
onEvtCancel();
|
||||
break;
|
||||
case EVT_DEAD:
|
||||
onEvtDead();
|
||||
break;
|
||||
case EVT_FAKE_DEATH:
|
||||
onEvtFakeDeath();
|
||||
break;
|
||||
case EVT_FINISH_CASTING:
|
||||
onEvtFinishCasting();
|
||||
break;
|
||||
case EVT_AFRAID: {
|
||||
onEvtAfraid((L2Character) args[0], (Boolean) args[1]);
|
||||
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, long aggro);
|
||||
|
||||
protected abstract void onEvtStunned(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();
|
||||
|
||||
protected abstract void onEvtAfraid(L2Character effector, boolean start);
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate movement data for a move to location action and add the actor to movingObjects of GameTimeController
|
||||
_actor.moveToLocation(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
|
||||
_actor.moveToLocation(x, y, z, 0);
|
||||
|
||||
// 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()) {
|
||||
_actor.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 summon) {
|
||||
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 summon) {
|
||||
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 summon) {
|
||||
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 L2PcInstance 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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 disables 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 args the args
|
||||
*/
|
||||
void notifyEvent(CtrlEvent evt, Object... args);
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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 possible event 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 think 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 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
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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;
|
||||
|
||||
/**
|
||||
* Fear task.
|
||||
* @author Zoey76
|
||||
* @version 2.6.3.0
|
||||
*/
|
||||
public class FearTask implements Runnable {
|
||||
|
||||
protected static final int FEAR_TICKS = 5;
|
||||
|
||||
private final L2AttackableAI ai;
|
||||
|
||||
private final L2Character effector;
|
||||
|
||||
private boolean start;
|
||||
|
||||
public FearTask(L2AttackableAI ai, L2Character effector, boolean start) {
|
||||
this.ai = ai;
|
||||
this.effector = effector;
|
||||
this.start = start;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
ai.setFearTime(ai.getFearTime() - FEAR_TICKS);
|
||||
ai.onEvtAfraid(effector, start);
|
||||
start = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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 creature) {
|
||||
super(creature);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void moveTo(int x, int y, int z) {
|
||||
if (!_actor.isMovementDisabled()) {
|
||||
_clientMoving = true;
|
||||
_actor.moveToLocation(x, y, z, 0);
|
||||
_actor.broadcastPacket(new ExMoveToLocationAirShip(getActor()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void clientStopMoving(Location loc) {
|
||||
if (_actor.isMoving()) {
|
||||
_actor.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
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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 creature) {
|
||||
super(creature);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void moveTo(int x, int y, int z) {
|
||||
if (!_actor.isMovementDisabled()) {
|
||||
if (!_clientMoving) {
|
||||
_actor.broadcastPacket(new VehicleStarted(getActor(), 1));
|
||||
}
|
||||
|
||||
_clientMoving = true;
|
||||
_actor.moveToLocation(x, y, z, 0);
|
||||
_actor.broadcastPacket(new VehicleDeparture(getActor()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void clientStopMoving(Location loc) {
|
||||
if (_actor.isMoving()) {
|
||||
_actor.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
@@ -0,0 +1,437 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.stream.Collectors;
|
||||
|
||||
import com.l2jserver.commons.util.Rnd;
|
||||
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.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;
|
||||
|
||||
/**
|
||||
* 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_FORCE_ATTACK = 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;
|
||||
|
||||
public L2ControllableMobAI(L2ControllableMobInstance creature) {
|
||||
super(creature);
|
||||
setAlternateAI(AI_IDLE);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
_isThinking = 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_FORCE_ATTACK:
|
||||
thinkForceAttack();
|
||||
break;
|
||||
case AI_ATTACK_GROUP:
|
||||
thinkAttackGroup();
|
||||
break;
|
||||
default:
|
||||
if (getIntention() == AI_INTENTION_ACTIVE) {
|
||||
thinkActive();
|
||||
} else if (getIntention() == AI_INTENTION_ATTACK) {
|
||||
thinkAttack();
|
||||
}
|
||||
break;
|
||||
}
|
||||
} finally {
|
||||
_isThinking = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void thinkCast() {
|
||||
final var attackTarget = getAttackTarget();
|
||||
if ((attackTarget == null) || attackTarget.isAlikeDead()) {
|
||||
setAttackTarget(findNextRndTarget());
|
||||
clientStopMoving(null);
|
||||
|
||||
if (attackTarget == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final var actor = getActor();
|
||||
actor.setTarget(attackTarget);
|
||||
|
||||
if (!_actor.isMuted()) {
|
||||
int max_range = 0;
|
||||
// check distant skills
|
||||
|
||||
for (Skill sk : _actor.getAllSkills()) {
|
||||
if (Util.checkIfInRange(sk.getCastRange(), _actor, attackTarget, true) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() > _actor.getStat().getMpConsume2(sk))) {
|
||||
_actor.doCast(sk);
|
||||
return;
|
||||
}
|
||||
|
||||
max_range = Math.max(max_range, sk.getCastRange());
|
||||
}
|
||||
|
||||
if (!isNotMoving()) {
|
||||
moveToPawn(attackTarget, max_range);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 forced attack 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().getMpConsume2(sk))) {
|
||||
_actor.doCast(sk);
|
||||
return;
|
||||
}
|
||||
|
||||
max_range = Math.max(max_range, castRange);
|
||||
}
|
||||
|
||||
if (!isNotMoving()) {
|
||||
moveToPawn(target, range);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
_actor.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().getMpConsume2(sk))) {
|
||||
_actor.doCast(sk);
|
||||
return;
|
||||
}
|
||||
|
||||
max_range = Math.max(max_range, castRange);
|
||||
}
|
||||
|
||||
if (!isNotMoving()) {
|
||||
moveToPawn(getForcedTarget(), _actor.getPhysicalAttackRange()/* range */);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_actor.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 npc)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
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().getMpConsume2(sk))) {
|
||||
_actor.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().getMpConsume2(sk))) {
|
||||
_actor.doCast(sk);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_actor.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) || (target instanceof L2NpcInstance) || (target instanceof L2DoorInstance)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(Zoey76)[#112]: This check must change if summon fall in L2Npc hierarchy.
|
||||
if (target.isNpc()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the target isn't invulnerable
|
||||
if (target.isInvul() || target.isAlikeDead()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Spawn protection (only against mobs)
|
||||
if (target.isPlayer() && ((L2PcInstance) target).isSpawnProtected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final var actor = getActor();
|
||||
if (!_actor.isInsideRadius(target, actor.getAggroRange(), false, false) || (Math.abs(_actor.getZ() - target.getZ()) > 100)) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
return actor.isAggressive();
|
||||
}
|
||||
|
||||
private L2Character findNextRndTarget() {
|
||||
final var potentialTarget = _actor.getKnownList().getKnownCharactersInRadius(getActor().getAggroRange()).stream().filter(this::checkAutoAttackCondition).collect(Collectors.toList());
|
||||
if (potentialTarget.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return potentialTarget.get(Rnd.nextInt(potentialTarget.size()));
|
||||
}
|
||||
|
||||
private L2ControllableMobInstance findNextGroupTarget() {
|
||||
return getGroupTarget().getRandomMob();
|
||||
}
|
||||
|
||||
public int getAlternateAI() {
|
||||
return _alternateAI;
|
||||
}
|
||||
|
||||
public void setAlternateAI(int alternateAI) {
|
||||
_alternateAI = alternateAI;
|
||||
}
|
||||
|
||||
public void forceAttack(L2Character target) {
|
||||
setAlternateAI(AI_FORCE_ATTACK);
|
||||
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 isNotMoving() {
|
||||
return _isNotMoving;
|
||||
}
|
||||
|
||||
public void setNotMoving(boolean isNotMoving) {
|
||||
_isNotMoving = isNotMoving;
|
||||
}
|
||||
|
||||
private L2Character getForcedTarget() {
|
||||
return _forcedTarget;
|
||||
}
|
||||
|
||||
private MobGroup getGroupTarget() {
|
||||
return _targetGroup;
|
||||
}
|
||||
|
||||
private void setForcedTarget(L2Character forcedTarget) {
|
||||
_forcedTarget = forcedTarget;
|
||||
}
|
||||
|
||||
private void setGroupTarget(MobGroup targetGroup) {
|
||||
_targetGroup = targetGroup;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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 creature) {
|
||||
super(creature);
|
||||
}
|
||||
|
||||
@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) {
|
||||
ThreadPoolManager.getInstance().executeGeneral(new onEventAttackedDoorTask((L2DoorInstance) _actor, attacker));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onEvtAggression(L2Character target, long 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,806 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.commons.util.Rnd;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Fort siege guards AI.
|
||||
*/
|
||||
public class L2FortSiegeGuardAI extends L2CharacterAI implements Runnable {
|
||||
private static final int MAX_ATTACK_TIMEOUT = 30 * GameTimeController.TICKS_PER_SECOND; // 30s converted to ticks
|
||||
|
||||
/** 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 creature the creature
|
||||
*/
|
||||
public L2FortSiegeGuardAI(L2DefenderInstance creature) {
|
||||
super(creature);
|
||||
_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.isInvul()) {
|
||||
// However EffectInvincible requires to check GMs specially
|
||||
if (target.isPlayer() && target.isGM()) {
|
||||
return false;
|
||||
}
|
||||
if (target.isSummon() && ((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 playable) {
|
||||
// Check if the target isn't in silent move mode AND too far (>100)
|
||||
if (playable.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) {
|
||||
LOG.debug("{}: changeIntention({}, {}, {})", getClass().getSimpleName(), 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
|
||||
_actor.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) && (_actor.isInActiveRegion())) {
|
||||
_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
|
||||
long 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
|
||||
long 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 defender) {
|
||||
defender.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() {
|
||||
LOG.debug("{}: thinkAttack(); timeout={}", getClass().getSimpleName(), (_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 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 npc)) {
|
||||
if (_selfAnalysis.hasHealOrResurrect && (cha instanceof L2PcInstance) && ((L2Npc) _actor).getFort().getSiege().checkIsDefender(cha.getClan())) {
|
||||
// heal friends
|
||||
if (!_actor.isAttackingDisabled() && (cha.getCurrentHp() < (cha.getMaxHp() * 0.6)) && (_actor.getCurrentHp() > (_actor.getMaxHp() / 2.0)) && (_actor.getCurrentMp() > (_actor.getMaxMp() / 2.0)) && cha.isInCombat()) {
|
||||
for (Skill sk : _selfAnalysis.healSkills) {
|
||||
if (_actor.getCurrentMp() < sk.getMpConsume2()) {
|
||||
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);
|
||||
_actor.doCast(sk);
|
||||
_actor.setTarget(oldTarget);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
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.0)) && (_actor.getCurrentMp() > (_actor.getMaxMp() / 2.0)) && npc.isInCombat()) {
|
||||
for (Skill sk : _selfAnalysis.healSkills) {
|
||||
if (_actor.getCurrentMp() < sk.getMpConsume2()) {
|
||||
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);
|
||||
_actor.doCast(sk);
|
||||
_actor.setTarget(OldTarget);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void attackPrepare() {
|
||||
// Get all information needed to choose between physical or magical attack
|
||||
Collection<Skill> skills;
|
||||
double dist_2;
|
||||
int range;
|
||||
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.warn("{}: Attack target is NULL.", getClass().getSimpleName());
|
||||
_actor.setTarget(null);
|
||||
setIntention(AI_INTENTION_IDLE, null, null);
|
||||
return;
|
||||
}
|
||||
|
||||
// never attack defenders
|
||||
if ((attackTarget instanceof L2PcInstance) && sGuard.getFort().getSiege().checkIsDefender(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().getMpConsume2(sk)) && !sk.isPassive()) {
|
||||
|
||||
L2Object OldTarget = _actor.getTarget();
|
||||
if ((sk.isContinuous() && !sk.isDebuff()) || (sk.hasEffectType(L2EffectType.HP))) {
|
||||
boolean useSkillSelf = true;
|
||||
if ((sk.hasEffectType(L2EffectType.HP)) && (_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);
|
||||
_actor.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Else, if this is close enough to attack
|
||||
else if (dist_2 <= (range * range)) {
|
||||
// Force mobs to attack anybody if confused
|
||||
L2Character hated;
|
||||
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().getMpConsume2(sk)) && !_actor.isSkillDisabled(sk)) {
|
||||
L2Object OldTarget = _actor.getTarget();
|
||||
if ((sk.isContinuous() && !sk.isDebuff()) || (sk.hasEffectType(L2EffectType.HP))) {
|
||||
boolean useSkillSelf = true;
|
||||
if ((sk.hasEffectType(L2EffectType.HP)) && (_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);
|
||||
_actor.doCast(sk);
|
||||
_actor.setTarget(OldTarget);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Finally, do the physical attack itself
|
||||
_actor.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, long 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 (var attacker : me.getAggroList().keySet()) {
|
||||
me.addDamageHate(attacker, 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;
|
||||
}
|
||||
_actor.detachAI();
|
||||
super.stopAITask();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.L2Playable;
|
||||
import com.l2jserver.gameserver.model.skills.Skill;
|
||||
import com.l2jserver.gameserver.model.zone.ZoneId;
|
||||
import com.l2jserver.gameserver.network.SystemMessageId;
|
||||
|
||||
/**
|
||||
* Playable creatures AI.
|
||||
* @author JIV
|
||||
*/
|
||||
public abstract class L2PlayableAI extends L2CharacterAI {
|
||||
|
||||
public L2PlayableAI(L2Playable creature) {
|
||||
super(creature);
|
||||
}
|
||||
|
||||
@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.TARGET_IS_INCORRECT);
|
||||
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.TARGET_IS_INCORRECT);
|
||||
clientActionFailed();
|
||||
return;
|
||||
}
|
||||
|
||||
if (target.getActingPlayer().isCursedWeaponEquipped() && (_actor.getActingPlayer().getLevel() <= 20)) {
|
||||
_actor.getActingPlayer().sendPacket(SystemMessageId.TARGET_IS_INCORRECT);
|
||||
clientActionFailed();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_actor.getActingPlayer().isCursedWeaponEquipped() && (target.getActingPlayer().getLevel() <= 20)) {
|
||||
_actor.getActingPlayer().sendPacket(SystemMessageId.TARGET_IS_INCORRECT);
|
||||
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.TARGET_IS_INCORRECT);
|
||||
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.TARGET_IS_INCORRECT);
|
||||
clientActionFailed();
|
||||
_actor.setIsCastingNow(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (target.getActingPlayer().isCursedWeaponEquipped() && ((_actor.getActingPlayer().getLevel() <= 20) || (target.getActingPlayer().getLevel() <= 20))) {
|
||||
_actor.getActingPlayer().sendPacket(SystemMessageId.TARGET_IS_INCORRECT);
|
||||
clientActionFailed();
|
||||
_actor.setIsCastingNow(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
super.onIntentionCast(skill, target);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.enums.DuelState;
|
||||
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.L2PcInstance;
|
||||
import com.l2jserver.gameserver.model.actor.instance.L2StaticObjectInstance;
|
||||
import com.l2jserver.gameserver.model.skills.Skill;
|
||||
import com.l2jserver.gameserver.model.skills.targets.TargetType;
|
||||
import com.l2jserver.gameserver.network.SystemMessageId;
|
||||
import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
|
||||
|
||||
public class L2PlayerAI extends L2PlayableAI {
|
||||
private boolean _thinking; // to prevent recursive thinking
|
||||
|
||||
private IntentionCommand _nextIntention = null;
|
||||
|
||||
public L2PlayerAI(L2PcInstance creature) {
|
||||
super(creature);
|
||||
}
|
||||
|
||||
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) {
|
||||
// Forget next if it's not cast or it's cast and skill is toggle.
|
||||
if ((intention != AI_INTENTION_CAST) || ((arg0 != null) && !((Skill) arg0).isToggle())) {
|
||||
_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.getActingPlayer().getDuelState() == DuelState.DEAD) {
|
||||
clientActionFailed();
|
||||
_actor.getActingPlayer().sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CANNOT_MOVE_FROZEN));
|
||||
return;
|
||||
}
|
||||
if (_actor.isAllSkillsDisabled() || _actor.isCastingNow() || _actor.isAttackingNow()) {
|
||||
clientActionFailed();
|
||||
saveNextIntention(AI_INTENTION_MOVE_TO, loc, null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast)
|
||||
clientStopAutoAttack();
|
||||
|
||||
// Set the Intention of this AbstractAI to AI_INTENTION_MOVE_TO
|
||||
changeIntention(AI_INTENTION_MOVE_TO, loc, null);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
clientStopMoving(null);
|
||||
_actor.doAttack(target);
|
||||
}
|
||||
|
||||
private void thinkCast() {
|
||||
L2Character target = getCastTarget();
|
||||
if ((_skill.getTargetType() == TargetType.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);
|
||||
}
|
||||
|
||||
_actor.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);
|
||||
_actor.getActingPlayer().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)) {
|
||||
_actor.getActingPlayer().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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,784 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.commons.util.Rnd;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Siege guard AI.
|
||||
*/
|
||||
public class L2SiegeGuardAI extends L2CharacterAI implements Runnable {
|
||||
private static final int MAX_ATTACK_TIMEOUT = 30 * GameTimeController.TICKS_PER_SECOND; // 30s converted to ticks
|
||||
|
||||
/** 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 creature the creature
|
||||
*/
|
||||
public L2SiegeGuardAI(L2DefenderInstance creature) {
|
||||
super(creature);
|
||||
_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.isPlayer() && target.isGM()) {
|
||||
return false;
|
||||
}
|
||||
if (target.isSummon() && ((L2Summon) target).getOwner().isGM()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the owner if the target is a summon
|
||||
if (target instanceof L2Summon summon) {
|
||||
L2PcInstance owner = summon.getOwner();
|
||||
if (_actor.isInsideRadius(owner, 1000, true, false)) {
|
||||
target = owner;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the target is a L2PcInstance
|
||||
if (target instanceof L2Playable playable) {
|
||||
// Check if the target isn't in silent move mode AND too far (>100)
|
||||
if (playable.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) {
|
||||
LOG.debug("{}: changeIntention({}, {}, {})", getClass().getSimpleName(), 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
|
||||
_actor.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) && (_actor.isInActiveRegion())) {
|
||||
_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
|
||||
long 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
|
||||
long 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() {
|
||||
LOG.debug("{}: thinkAttack(); timeout={}", getClass().getSimpleName(), (_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 void factionNotifyAndSupport() {
|
||||
L2Character target = getAttackTarget();
|
||||
final var siegeGuard = (L2Npc) _actor;
|
||||
// Call all L2Object of its Faction inside the Faction Range
|
||||
if ((siegeGuard.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 (var cha : _actor.getKnownList().getKnownCharactersInRadius(1000)) {
|
||||
if (cha == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(cha instanceof L2Npc npc)) {
|
||||
if (_selfAnalysis.hasHealOrResurrect && (cha instanceof L2PcInstance) && (siegeGuard.getCastle().getSiege().checkIsDefender(cha.getClan()))) {
|
||||
// heal friends
|
||||
if (!_actor.isAttackingDisabled() && (cha.getCurrentHp() < (cha.getMaxHp() * 0.6)) && (_actor.getCurrentHp() > (_actor.getMaxHp() / 2.0)) && (_actor.getCurrentMp() > (_actor.getMaxMp() / 2.0)) && cha.isInCombat()) {
|
||||
for (Skill sk : _selfAnalysis.healSkills) {
|
||||
if (_actor.getCurrentMp() < sk.getMpConsume2()) {
|
||||
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);
|
||||
_actor.doCast(sk);
|
||||
_actor.setTarget(OldTarget);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!npc.isInMyClan(siegeGuard)) {
|
||||
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.0)) && (_actor.getCurrentMp() > (_actor.getMaxMp() / 2.0)) && npc.isInCombat()) {
|
||||
for (Skill sk : _selfAnalysis.healSkills) {
|
||||
if (_actor.getCurrentMp() < sk.getMpConsume2()) {
|
||||
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);
|
||||
_actor.doCast(sk);
|
||||
_actor.setTarget(OldTarget);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void attackPrepare() {
|
||||
// Get all information needed to choose between physical or magical attack
|
||||
Collection<Skill> skills;
|
||||
double dist_2;
|
||||
int range;
|
||||
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(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().getMpConsume2(sk)) && !sk.isPassive()) {
|
||||
|
||||
L2Object OldTarget = _actor.getTarget();
|
||||
if ((sk.isContinuous() && !sk.isDebuff()) || (sk.hasEffectType(L2EffectType.HP))) {
|
||||
boolean useSkillSelf = true;
|
||||
if ((sk.hasEffectType(L2EffectType.HP)) && (_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);
|
||||
_actor.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Else, if this is close enough to attack
|
||||
else if (dist_2 <= (range * range)) {
|
||||
// Force mobs to attack anybody if confused
|
||||
L2Character hated;
|
||||
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().getMpConsume2(sk)) && !_actor.isSkillDisabled(sk)) {
|
||||
L2Object OldTarget = _actor.getTarget();
|
||||
if ((sk.isContinuous() && !sk.isDebuff()) || (sk.hasEffectType(L2EffectType.HP))) {
|
||||
boolean useSkillSelf = true;
|
||||
if ((sk.hasEffectType(L2EffectType.HP)) && (_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);
|
||||
_actor.doCast(sk);
|
||||
_actor.setTarget(OldTarget);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Finally, do the physical attack itself
|
||||
if (!_selfAnalysis.isHealer) {
|
||||
_actor.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, long 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;
|
||||
}
|
||||
_actor.detachAI();
|
||||
super.stopAITask();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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 java.util.List;
|
||||
|
||||
import com.l2jserver.gameserver.model.actor.L2Character;
|
||||
import com.l2jserver.gameserver.model.actor.instance.L2DefenderInstance;
|
||||
|
||||
/**
|
||||
* @author BiggBoss
|
||||
*/
|
||||
public final class L2SpecialSiegeGuardAI extends L2SiegeGuardAI {
|
||||
private final List<Integer> _allied = new ArrayList<>();
|
||||
|
||||
public L2SpecialSiegeGuardAI(L2DefenderInstance creature) {
|
||||
super(creature);
|
||||
}
|
||||
|
||||
public List<Integer> getAlly() {
|
||||
return _allied;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean autoAttackCondition(L2Character target) {
|
||||
if (_allied.contains(target.getObjectId())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return super.autoAttackCondition(target);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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 static com.l2jserver.gameserver.config.Configuration.geodata;
|
||||
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import com.l2jserver.commons.util.Rnd;
|
||||
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.L2Summon;
|
||||
import com.l2jserver.gameserver.model.skills.Skill;
|
||||
import com.l2jserver.gameserver.pathfinding.PathFinding;
|
||||
|
||||
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(L2Summon creature) {
|
||||
super(creature);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onIntentionAttack(L2Character target) {
|
||||
if ((geodata().getPathFinding() > 0) && //
|
||||
(PathFinding.getInstance().findPath(_actor.getX(), _actor.getY(), _actor.getZ(), target.getX(), target.getY(), target.getZ(), _actor.getInstanceId(), true) == null)) {
|
||||
return;
|
||||
}
|
||||
super.onIntentionAttack(target);
|
||||
}
|
||||
|
||||
@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, AI_INTENTION_FOLLOW -> startAvoidTask();
|
||||
default -> stopAvoidTask();
|
||||
}
|
||||
super.changeIntention(intention, arg0, arg1);
|
||||
}
|
||||
|
||||
private void thinkAttack() {
|
||||
if (checkTargetLostOrDead(getAttackTarget())) {
|
||||
setAttackTarget(null);
|
||||
return;
|
||||
}
|
||||
if (maybeMoveToPawn(getAttackTarget(), _actor.getPhysicalAttackRange())) {
|
||||
return;
|
||||
}
|
||||
clientStopMoving(null);
|
||||
_actor.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;
|
||||
_actor.doCast(_skill);
|
||||
}
|
||||
|
||||
private void thinkPickUp() {
|
||||
if (checkTargetLost(getTarget())) {
|
||||
return;
|
||||
}
|
||||
if (maybeMoveToPawn(getTarget(), 36)) {
|
||||
return;
|
||||
}
|
||||
setIntention(AI_INTENTION_IDLE);
|
||||
((L2Summon) _actor).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();
|
||||
case AI_INTENTION_CAST -> thinkCast();
|
||||
case AI_INTENTION_PICK_UP -> thinkPickUp();
|
||||
case AI_INTENTION_INTERACT -> thinkInteract();
|
||||
}
|
||||
} 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 (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, AI_INTENTION_FOLLOW, AI_INTENTION_IDLE, AI_INTENTION_MOVE_TO, 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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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 {
|
||||
|
||||
public L2VehicleAI(L2Vehicle creature) {
|
||||
super(creature);
|
||||
}
|
||||
|
||||
@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, long 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() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.List;
|
||||
|
||||
/**
|
||||
* Class for AI action after some event.<br>
|
||||
* Has 2 lists for "work" and "break".
|
||||
* @author Yaroslav
|
||||
* @author Zoey76
|
||||
*/
|
||||
public class NextAction {
|
||||
public interface NextActionCallback {
|
||||
void doWork();
|
||||
}
|
||||
|
||||
private final List<CtrlEvent> _events;
|
||||
private final List<CtrlIntention> _intentions;
|
||||
private final NextActionCallback _callback;
|
||||
|
||||
public NextAction(CtrlEvent event, CtrlIntention intention, NextActionCallback callback) {
|
||||
_events = List.of(event);
|
||||
_intentions = List.of(intention);
|
||||
_callback = callback;
|
||||
}
|
||||
|
||||
public void doAction() {
|
||||
_callback.doWork();
|
||||
}
|
||||
|
||||
public List<CtrlEvent> getEvents() {
|
||||
return _events;
|
||||
}
|
||||
|
||||
public List<CtrlIntention> getIntentions() {
|
||||
return _intentions;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.bbs.model;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Forum.
|
||||
* @author Zoey76
|
||||
* @version 2.6.2.0
|
||||
*/
|
||||
public final class Forum {
|
||||
|
||||
private int id;
|
||||
private String name;
|
||||
private ForumType type;
|
||||
private int post;
|
||||
private ForumVisibility visibility;
|
||||
private final Forum parent;
|
||||
private int ownerId;
|
||||
private final Map<String, Forum> children = new ConcurrentHashMap<>();
|
||||
private final Map<Integer, Topic> topics = new ConcurrentHashMap<>();
|
||||
|
||||
public Forum(int id, String name, Forum parent, ForumType type, ForumVisibility visibility, int ownerId) {
|
||||
this.name = name;
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
post = 0;
|
||||
this.visibility = visibility;
|
||||
this.parent = parent;
|
||||
this.ownerId = ownerId;
|
||||
}
|
||||
|
||||
public int getTopicSize() {
|
||||
return topics.size();
|
||||
}
|
||||
|
||||
public Topic getTopic(int topicId) {
|
||||
return topics.get(topicId);
|
||||
}
|
||||
|
||||
public void addTopic(Topic t) {
|
||||
topics.put(t.getId(), t);
|
||||
}
|
||||
|
||||
public void removeTopic(int topicId) {
|
||||
topics.remove(topicId);
|
||||
}
|
||||
|
||||
public Forum getChildByName(String name) {
|
||||
return children.get(name);
|
||||
}
|
||||
|
||||
public void addChild(Forum child) {
|
||||
children.put(child.getName(), child);
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public ForumType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(ForumType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public int getPost() {
|
||||
return post;
|
||||
}
|
||||
|
||||
public void setPost(int post) {
|
||||
this.post = post;
|
||||
}
|
||||
|
||||
public ForumVisibility getVisibility() {
|
||||
return visibility;
|
||||
}
|
||||
|
||||
public void setVisibility(ForumVisibility visibility) {
|
||||
this.visibility = visibility;
|
||||
}
|
||||
|
||||
public int getOwnerId() {
|
||||
return ownerId;
|
||||
}
|
||||
|
||||
public void setOwnerId(int ownerId) {
|
||||
this.ownerId = ownerId;
|
||||
}
|
||||
|
||||
public Forum getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public Map<String, Forum> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public Map<Integer, Topic> getTopics() {
|
||||
return topics;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.bbs.model;
|
||||
|
||||
/**
|
||||
* Forum Type.
|
||||
* @author Zoey76
|
||||
* @version 2.6.2.0
|
||||
*/
|
||||
public enum ForumType {
|
||||
ROOT,
|
||||
NORMAL,
|
||||
CLAN,
|
||||
MEMO,
|
||||
MAIL
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.bbs.model;
|
||||
|
||||
/**
|
||||
* Forum Visibility.
|
||||
* @author Zoey76
|
||||
* @version 2.6.2.0
|
||||
*/
|
||||
public enum ForumVisibility {
|
||||
INVISIBLE,
|
||||
ALL,
|
||||
CLAN_MEMBER_ONLY,
|
||||
OWNER_ONLY
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.bbs.model;
|
||||
|
||||
/**
|
||||
* Post.
|
||||
* @author Zoey76
|
||||
* @version 2.6.2.0
|
||||
*/
|
||||
public class Post {
|
||||
|
||||
private int id;
|
||||
|
||||
private String ownerName;
|
||||
|
||||
private int ownerId;
|
||||
|
||||
private long date;
|
||||
|
||||
private int topicId;
|
||||
|
||||
private int forumId;
|
||||
|
||||
private String txt;
|
||||
|
||||
public Post(int id, String ownerName, int ownerId, long date, int topicId, int forumId, String txt) {
|
||||
this.id = id;
|
||||
this.ownerName = ownerName;
|
||||
this.ownerId = ownerId;
|
||||
this.date = date;
|
||||
this.topicId = topicId;
|
||||
this.forumId = forumId;
|
||||
this.txt = txt;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getOwnerName() {
|
||||
return ownerName;
|
||||
}
|
||||
|
||||
public void setOwnerName(String ownerName) {
|
||||
this.ownerName = ownerName;
|
||||
}
|
||||
|
||||
public int getOwnerId() {
|
||||
return ownerId;
|
||||
}
|
||||
|
||||
public void setOwnerId(int ownerId) {
|
||||
this.ownerId = ownerId;
|
||||
}
|
||||
|
||||
public long getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public void setDate(long date) {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public int getTopicId() {
|
||||
return topicId;
|
||||
}
|
||||
|
||||
public void setTopicId(int topicId) {
|
||||
this.topicId = topicId;
|
||||
}
|
||||
|
||||
public int getForumId() {
|
||||
return forumId;
|
||||
}
|
||||
|
||||
public void setForumId(int forumId) {
|
||||
this.forumId = forumId;
|
||||
}
|
||||
|
||||
public String getTxt() {
|
||||
return txt;
|
||||
}
|
||||
|
||||
public void setTxt(String txt) {
|
||||
this.txt = txt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.bbs.model;
|
||||
|
||||
/**
|
||||
* Topic.
|
||||
* @author Zoey76
|
||||
* @version 2.6.2.0
|
||||
*/
|
||||
public class Topic {
|
||||
|
||||
private final int id;
|
||||
|
||||
private final int forumId;
|
||||
|
||||
private final String name;
|
||||
|
||||
private final long date;
|
||||
|
||||
private final String ownerName;
|
||||
|
||||
private final int ownerId;
|
||||
|
||||
private final TopicType type;
|
||||
|
||||
private final int reply;
|
||||
|
||||
public Topic(int id, int forumId, String name, long date, String ownerName, int ownerId, TopicType type, int reply) {
|
||||
this.id = id;
|
||||
this.forumId = forumId;
|
||||
this.name = name;
|
||||
this.date = date;
|
||||
this.ownerName = ownerName;
|
||||
this.ownerId = ownerId;
|
||||
this.type = type;
|
||||
this.reply = reply;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getForumId() {
|
||||
return forumId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public long getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public String getOwnerName() {
|
||||
return ownerName;
|
||||
}
|
||||
|
||||
public int getOwnerId() {
|
||||
return ownerId;
|
||||
}
|
||||
|
||||
public TopicType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public int getReply() {
|
||||
return reply;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.bbs.model;
|
||||
|
||||
/**
|
||||
* TopicType.
|
||||
* @author Zoey76
|
||||
* @version 2.6.2.0
|
||||
*/
|
||||
public enum TopicType {
|
||||
NORMAL,
|
||||
MEMO
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.bbs.repository;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.l2jserver.gameserver.bbs.model.Forum;
|
||||
|
||||
/**
|
||||
* Forum repository.
|
||||
* @author Zoey76
|
||||
* @version 2.6.2.0
|
||||
*/
|
||||
public interface ForumRepository {
|
||||
|
||||
Map<String, Forum> getForums();
|
||||
|
||||
void save(Forum forum);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.bbs.repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.l2jserver.gameserver.bbs.model.Post;
|
||||
import com.l2jserver.gameserver.bbs.model.Topic;
|
||||
|
||||
/**
|
||||
* Post repository.
|
||||
* @author Zoey76
|
||||
* @version 2.6.2.0
|
||||
*/
|
||||
public interface PostRepository {
|
||||
|
||||
void delete(Topic topic);
|
||||
|
||||
void update(Post post);
|
||||
|
||||
List<Post> load(Topic topic);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.bbs.repository;
|
||||
|
||||
import com.l2jserver.gameserver.bbs.model.Forum;
|
||||
import com.l2jserver.gameserver.bbs.model.Topic;
|
||||
|
||||
/**
|
||||
* Topic repository.
|
||||
* @author Zoey76
|
||||
* @version 2.6.2.0
|
||||
*/
|
||||
public interface TopicRepository {
|
||||
|
||||
void load(Forum forum);
|
||||
|
||||
void save(Topic topic);
|
||||
|
||||
void delete(Topic topic, Forum forum);
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.bbs.repository.impl;
|
||||
|
||||
import static java.sql.Statement.RETURN_GENERATED_KEYS;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.l2jserver.commons.database.ConnectionFactory;
|
||||
import com.l2jserver.gameserver.bbs.model.Forum;
|
||||
import com.l2jserver.gameserver.bbs.model.ForumType;
|
||||
import com.l2jserver.gameserver.bbs.model.ForumVisibility;
|
||||
import com.l2jserver.gameserver.bbs.repository.ForumRepository;
|
||||
import com.l2jserver.gameserver.bbs.service.ForumsBBSManager;
|
||||
import com.l2jserver.gameserver.dao.factory.impl.DAOFactory;
|
||||
|
||||
/**
|
||||
* Forum repository MySQL implementation.
|
||||
* @author Zoey76
|
||||
* @version 2.6.2.0
|
||||
*/
|
||||
public class ForumRepositoryMySQLImpl implements ForumRepository {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ForumRepositoryMySQLImpl.class);
|
||||
|
||||
private static final String SELECT_FORUMS = "SELECT forum_id, forum_name, forum_post, forum_type, forum_perm, forum_owner_id FROM forums WHERE forum_type = 0";
|
||||
|
||||
private static final String SELECT_FORUM_CHILDREN = "SELECT forum_id, forum_name, forum_post, forum_type, forum_perm, forum_owner_id FROM forums WHERE forum_parent=?";
|
||||
|
||||
private static final String INSERT_FORUM = "INSERT INTO forums (forum_name, forum_parent, forum_post, forum_type, forum_perm, forum_owner_id) VALUES (?,?,?,?,?,?)";
|
||||
|
||||
@Override
|
||||
public Map<String, Forum> getForums() {
|
||||
final var forums = new HashMap<String, Forum>();
|
||||
try (var con = ConnectionFactory.getInstance().getConnection();
|
||||
var s = con.createStatement();
|
||||
var rs = s.executeQuery(SELECT_FORUMS)) {
|
||||
while (rs.next()) {
|
||||
final var forum = new Forum(rs.getInt("forum_id"), //
|
||||
rs.getString("forum_name"), //
|
||||
null, //
|
||||
ForumType.values()[rs.getInt("forum_type")], //
|
||||
ForumVisibility.values()[rs.getInt("forum_perm")], //
|
||||
rs.getInt("forum_owner_id"));
|
||||
forums.put(forum.getName(), forum);
|
||||
|
||||
// Load topic
|
||||
DAOFactory.getInstance().getTopicRepository().load(forum);
|
||||
|
||||
// Load children
|
||||
loadChildren(forum);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LOG.warn("Data error on Forum (root)!", ex);
|
||||
}
|
||||
return forums;
|
||||
}
|
||||
|
||||
private void loadChildren(Forum parent) {
|
||||
try (var con = ConnectionFactory.getInstance().getConnection();
|
||||
var ps = con.prepareStatement(SELECT_FORUM_CHILDREN)) {
|
||||
ps.setInt(1, parent.getId());
|
||||
try (var rs = ps.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
ForumsBBSManager.getInstance().load(rs.getInt("forum_id"), //
|
||||
rs.getString("forum_name"), //
|
||||
parent, //
|
||||
ForumType.values()[rs.getInt("forum_type")], //
|
||||
ForumVisibility.values()[rs.getInt("forum_perm")], //
|
||||
rs.getInt("forum_owner_id"));
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LOG.warn("Error loading child forums for forum Id {}!", parent.getId(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(Forum forum) {
|
||||
try (var con = ConnectionFactory.getInstance().getConnection();
|
||||
var ps = con.prepareStatement(INSERT_FORUM, RETURN_GENERATED_KEYS)) {
|
||||
ps.setString(1, forum.getName());
|
||||
ps.setInt(2, forum.getParent().getId());
|
||||
ps.setInt(3, forum.getPost());
|
||||
ps.setInt(4, forum.getType().ordinal());
|
||||
ps.setInt(5, forum.getVisibility().ordinal());
|
||||
ps.setInt(6, forum.getOwnerId());
|
||||
ps.executeUpdate();
|
||||
|
||||
try (var rs = ps.getGeneratedKeys()) {
|
||||
if (rs.next()) {
|
||||
forum.setId(rs.getInt(1));
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LOG.error("Error saving forum Id {} in database!", forum.getId(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.bbs.repository.impl;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.l2jserver.commons.database.ConnectionFactory;
|
||||
import com.l2jserver.gameserver.bbs.model.Post;
|
||||
import com.l2jserver.gameserver.bbs.model.Topic;
|
||||
import com.l2jserver.gameserver.bbs.repository.PostRepository;
|
||||
|
||||
/**
|
||||
* Post repository MySQL implementation.
|
||||
* @author Zoey76
|
||||
* @version 2.6.2.0
|
||||
*/
|
||||
public class PostRepositoryMySQLImpl implements PostRepository {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(PostRepositoryMySQLImpl.class);
|
||||
|
||||
private static final String INSERT_POST = "INSERT INTO posts (post_id,post_owner_name,post_ownerid,post_date,post_topic_id,post_forum_id,post_txt) values (?,?,?,?,?,?,?)";
|
||||
|
||||
private static final String DELETE_POST = "DELETE FROM posts WHERE post_forum_id=? AND post_topic_id=?";
|
||||
|
||||
private static final String SELECT_POSTS = "SELECT * FROM posts WHERE post_forum_id=? AND post_topic_id=? ORDER BY post_id";
|
||||
|
||||
private static final String UPDATE_POST = "UPDATE posts SET post_txt=? WHERE post_id=? AND post_topic_id=? AND post_forum_id=?";
|
||||
|
||||
public void save(Post cp) {
|
||||
try (var con = ConnectionFactory.getInstance().getConnection();
|
||||
var ps = con.prepareStatement(INSERT_POST)) {
|
||||
ps.setInt(1, cp.getId());
|
||||
ps.setString(2, cp.getOwnerName());
|
||||
ps.setInt(3, cp.getOwnerId());
|
||||
ps.setLong(4, cp.getDate());
|
||||
ps.setInt(5, cp.getTopicId());
|
||||
ps.setInt(6, cp.getForumId());
|
||||
ps.setString(7, cp.getTxt());
|
||||
ps.execute();
|
||||
} catch (Exception ex) {
|
||||
LOG.warn("Could not save post Id {} in database!", cp.getId(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Topic topic) {
|
||||
try (var con = ConnectionFactory.getInstance().getConnection();
|
||||
var ps = con.prepareStatement(DELETE_POST)) {
|
||||
ps.setInt(1, topic.getForumId());
|
||||
ps.setInt(2, topic.getId());
|
||||
ps.execute();
|
||||
} catch (Exception ex) {
|
||||
LOG.warn("Unable to delete post for topic Id {} in forum Id {} from database!", topic.getForumId(), topic.getId(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Post> load(Topic topic) {
|
||||
final var posts = new LinkedList<Post>();
|
||||
try (var con = ConnectionFactory.getInstance().getConnection();
|
||||
var ps = con.prepareStatement(SELECT_POSTS)) {
|
||||
ps.setInt(1, topic.getForumId());
|
||||
ps.setInt(2, topic.getId());
|
||||
try (var rs = ps.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
final Post cp = new Post(rs.getInt("post_id"), rs.getString("post_owner_name"), rs.getInt("post_ownerid"), //
|
||||
rs.getLong("post_date"), rs.getInt("post_topic_id"), rs.getInt("post_forum_id"), rs.getString("post_txt"));
|
||||
posts.add(cp);
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LOG.warn("Unable to get post from topic Id {} in forum Id {} from database!", topic.getForumId(), topic.getId(), ex);
|
||||
}
|
||||
return posts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Post cp) {
|
||||
try (var con = ConnectionFactory.getInstance().getConnection();
|
||||
var ps = con.prepareStatement(UPDATE_POST)) {
|
||||
ps.setString(1, cp.getTxt());
|
||||
ps.setInt(2, cp.getId());
|
||||
ps.setInt(3, cp.getTopicId());
|
||||
ps.setInt(4, cp.getForumId());
|
||||
ps.execute();
|
||||
} catch (Exception ex) {
|
||||
LOG.warn("Unable to store post Id {} in database!", cp.getId(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.bbs.repository.impl;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.l2jserver.commons.database.ConnectionFactory;
|
||||
import com.l2jserver.gameserver.bbs.model.Forum;
|
||||
import com.l2jserver.gameserver.bbs.model.Topic;
|
||||
import com.l2jserver.gameserver.bbs.model.TopicType;
|
||||
import com.l2jserver.gameserver.bbs.repository.TopicRepository;
|
||||
import com.l2jserver.gameserver.bbs.service.TopicBBSManager;
|
||||
|
||||
/**
|
||||
* Topic Repository MySQL implementation.
|
||||
* @author Zoey76
|
||||
* @version 2.6.2.0
|
||||
*/
|
||||
public class TopicRepositoryMySQLImpl implements TopicRepository {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TopicRepositoryMySQLImpl.class);
|
||||
|
||||
private static final String SELECT_TOPICS = "SELECT * FROM topic WHERE topic_forum_id=? ORDER BY topic_id DESC";
|
||||
|
||||
private static final String DELETE_TOPIC = "DELETE FROM topic WHERE topic_id=? AND topic_forum_id=?";
|
||||
|
||||
private static final String INSERT_TOPIC = "INSERT INTO topic (topic_id,topic_forum_id,topic_name,topic_date,topic_ownername,topic_ownerid,topic_type,topic_reply) values (?,?,?,?,?,?,?,?)";
|
||||
|
||||
@Override
|
||||
public void load(Forum forum) {
|
||||
try (var con = ConnectionFactory.getInstance().getConnection();
|
||||
var ps = con.prepareStatement(SELECT_TOPICS)) {
|
||||
ps.setInt(1, forum.getId());
|
||||
try (var rs = ps.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
final var topic = new Topic(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"), TopicType.values()[rs.getInt("topic_type")], //
|
||||
rs.getInt("topic_reply"));
|
||||
|
||||
TopicBBSManager.getInstance().addTopic(topic);
|
||||
|
||||
forum.getTopics().put(topic.getId(), topic);
|
||||
if (topic.getId() > TopicBBSManager.getInstance().getMaxId(forum)) {
|
||||
TopicBBSManager.getInstance().setMaxId(topic.getId(), forum);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LOG.warn("Could not get from database topics for forum Id {}!", forum.getId(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(Topic topic) {
|
||||
try (var con = ConnectionFactory.getInstance().getConnection();
|
||||
var ps = con.prepareStatement(INSERT_TOPIC)) {
|
||||
ps.setInt(1, topic.getId());
|
||||
ps.setInt(2, topic.getForumId());
|
||||
ps.setString(3, topic.getName());
|
||||
ps.setLong(4, topic.getDate());
|
||||
ps.setString(5, topic.getOwnerName());
|
||||
ps.setInt(6, topic.getOwnerId());
|
||||
ps.setInt(7, topic.getType().ordinal());
|
||||
ps.setInt(8, topic.getReply());
|
||||
ps.execute();
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Error while saving new Topic to database!", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Topic topic, Forum forum) {
|
||||
TopicBBSManager.getInstance().delTopic(topic);
|
||||
forum.removeTopic(topic.getId());
|
||||
try (var con = ConnectionFactory.getInstance().getConnection();
|
||||
var ps = con.prepareStatement(DELETE_TOPIC)) {
|
||||
ps.setInt(1, topic.getId());
|
||||
ps.setInt(2, forum.getId());
|
||||
ps.execute();
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Error while deleting topic ID {} from database!", topic.getId(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.bbs.service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
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);
|
||||
|
||||
protected void send1001(String html, L2PcInstance player) {
|
||||
if (html.length() < 8192) {
|
||||
player.sendPacket(new ShowBoard(html, "1001"));
|
||||
}
|
||||
}
|
||||
|
||||
protected void send1002(L2PcInstance player) {
|
||||
send1002(player, " ", " ", "0");
|
||||
}
|
||||
|
||||
protected void send1002(L2PcInstance activeChar, String string, String string2, String string3) {
|
||||
List<String> arg = new ArrayList<>();
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.bbs.service;
|
||||
|
||||
import static com.l2jserver.gameserver.bbs.model.ForumType.CLAN;
|
||||
import static com.l2jserver.gameserver.bbs.model.ForumVisibility.CLAN_MEMBER_ONLY;
|
||||
import static com.l2jserver.gameserver.config.Configuration.general;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.l2jserver.gameserver.bbs.model.Forum;
|
||||
import com.l2jserver.gameserver.bbs.model.ForumType;
|
||||
import com.l2jserver.gameserver.bbs.model.ForumVisibility;
|
||||
import com.l2jserver.gameserver.dao.factory.impl.DAOFactory;
|
||||
import com.l2jserver.gameserver.model.L2Clan;
|
||||
|
||||
/**
|
||||
* Forums BBS Manager.
|
||||
* @author Zoey76
|
||||
* @version 2.6.2.0
|
||||
*/
|
||||
public class ForumsBBSManager {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ForumsBBSManager.class);
|
||||
|
||||
private static final Map<String, Forum> FORUMS_BY_NAME = new ConcurrentHashMap<>();
|
||||
|
||||
private static final Map<Integer, Forum> FORUMS_BY_ID = new ConcurrentHashMap<>();
|
||||
|
||||
protected ForumsBBSManager() {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
public void load() {
|
||||
FORUMS_BY_NAME.putAll(DAOFactory.getInstance().getForumRepository().getForums());
|
||||
FORUMS_BY_ID.putAll(FORUMS_BY_NAME.values().stream().collect(Collectors.toMap(Forum::getId, f -> f)));
|
||||
LOG.info("Loaded {} forums.", FORUMS_BY_NAME.size());
|
||||
}
|
||||
|
||||
public Forum getForumByName(String name) {
|
||||
return FORUMS_BY_NAME.get(name);
|
||||
}
|
||||
|
||||
public Forum getForumById(Integer id) {
|
||||
return FORUMS_BY_ID.get(id);
|
||||
}
|
||||
|
||||
public Forum create(String name, Forum parent, ForumType type, ForumVisibility visibility, int ownerId) {
|
||||
final var forum = new Forum(0, name, parent, type, visibility, ownerId);
|
||||
parent.addChild(forum);
|
||||
|
||||
DAOFactory.getInstance().getForumRepository().save(forum);
|
||||
|
||||
FORUMS_BY_NAME.put(forum.getName(), forum);
|
||||
FORUMS_BY_ID.put(forum.getId(), forum);
|
||||
return forum;
|
||||
}
|
||||
|
||||
public Forum load(int id, String name, Forum parent, ForumType type, ForumVisibility visibility, int ownerId) {
|
||||
final var forum = new Forum(id, name, parent, type, visibility, ownerId);
|
||||
parent.addChild(forum);
|
||||
FORUMS_BY_NAME.put(forum.getName(), forum);
|
||||
FORUMS_BY_ID.put(forum.getId(), forum);
|
||||
return forum;
|
||||
}
|
||||
|
||||
public void onClanLevel(L2Clan clan) {
|
||||
if ((clan.getLevel() >= 2) && general().enableCommunityBoard()) {
|
||||
final var clanRootForum = ForumsBBSManager.getInstance().getForumByName("ClanRoot");
|
||||
if (clanRootForum != null) {
|
||||
var forum = clanRootForum.getChildByName(clan.getName());
|
||||
if (forum == null) {
|
||||
ForumsBBSManager.getInstance().create(clan.getName(), clanRootForum, CLAN, CLAN_MEMBER_ONLY, clan.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ForumsBBSManager getInstance() {
|
||||
return SingletonHolder.INSTANCE;
|
||||
}
|
||||
|
||||
private static class SingletonHolder {
|
||||
protected static final ForumsBBSManager INSTANCE = new ForumsBBSManager();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.bbs.service;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.l2jserver.gameserver.bbs.model.Forum;
|
||||
import com.l2jserver.gameserver.bbs.model.ForumType;
|
||||
import com.l2jserver.gameserver.bbs.model.Post;
|
||||
import com.l2jserver.gameserver.bbs.model.Topic;
|
||||
import com.l2jserver.gameserver.dao.factory.impl.DAOFactory;
|
||||
import com.l2jserver.gameserver.handler.CommunityBoardHandler;
|
||||
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
|
||||
import com.l2jserver.gameserver.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Post BBS Manager.
|
||||
* @author Zoey76
|
||||
* @version 2.6.2.0
|
||||
*/
|
||||
public class PostBBSManager extends BaseBBSManager {
|
||||
|
||||
private final Map<Topic, List<Post>> _postByTopic = new ConcurrentHashMap<>();
|
||||
|
||||
protected PostBBSManager() {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
public List<Post> getGPostByTopic(Topic topic) {
|
||||
var posts = _postByTopic.get(topic);
|
||||
if (posts == null) {
|
||||
posts = DAOFactory.getInstance().getPostRepository().load(topic);
|
||||
_postByTopic.put(topic, posts);
|
||||
}
|
||||
return posts;
|
||||
}
|
||||
|
||||
public void delPostByTopic(Topic t) {
|
||||
_postByTopic.remove(t);
|
||||
}
|
||||
|
||||
public void addPostByTopic(Topic topic, List<Post> posts) {
|
||||
_postByTopic.putIfAbsent(topic, posts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parsecmd(String command, L2PcInstance activeChar) {
|
||||
if (command.startsWith("_bbsposts;read;")) {
|
||||
final var st = new StringTokenizer(command, ";");
|
||||
st.nextToken();
|
||||
st.nextToken();
|
||||
final var forumId = Integer.parseInt(st.nextToken());
|
||||
final var topicId = Integer.parseInt(st.nextToken());
|
||||
final var index = st.hasMoreTokens() ? Integer.parseInt(st.nextToken()) : 1;
|
||||
|
||||
final var forum = ForumsBBSManager.getInstance().getForumById(forumId);
|
||||
final var topic = TopicBBSManager.getInstance().getTopicById(topicId);
|
||||
showPost(topic, forum, activeChar, index);
|
||||
} else if (command.startsWith("_bbsposts;edit;")) {
|
||||
final var st = new StringTokenizer(command, ";");
|
||||
st.nextToken();
|
||||
st.nextToken();
|
||||
final var forumId = Integer.parseInt(st.nextToken());
|
||||
final var topicId = Integer.parseInt(st.nextToken());
|
||||
final var postId = Integer.parseInt(st.nextToken());
|
||||
|
||||
final var forum = ForumsBBSManager.getInstance().getForumById(forumId);
|
||||
final var topic = TopicBBSManager.getInstance().getTopicById(topicId);
|
||||
showEditPost(topic, forum, activeChar, postId);
|
||||
} 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) {
|
||||
if (topic == null) {
|
||||
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>Error: This topic does not exist!</center></body></html>", activeChar);
|
||||
} else {
|
||||
final List<Post> p = getGPostByTopic(topic);
|
||||
if ((forum == null) || (p == null)) {
|
||||
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>Error: This forum or post does not exist!</center></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></body></html>", activeChar);
|
||||
} else if (forum.getType() == ForumType.MEMO) {
|
||||
showMemoPost(topic, activeChar, forum);
|
||||
} else {
|
||||
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>The forum: " + forum.getName() + " is not implemented yet!</center></body></html>", activeChar);
|
||||
}
|
||||
}
|
||||
|
||||
private void showHtmlEditPost(Topic topic, L2PcInstance activeChar, Forum forum, List<Post> posts) {
|
||||
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> > <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> </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> </td><td><img src=\"l2ui.mini_logo\" width=5 height=1></td></tr></table></center></body></html>");
|
||||
send1001(html, activeChar);
|
||||
send1002(activeChar, posts.get(0).getTxt(), topic.getName(), DateFormat.getInstance().format(new Date(topic.getDate())));
|
||||
}
|
||||
|
||||
private void showMemoPost(Topic topic, L2PcInstance activeChar, Forum forum) {
|
||||
final var posts = getGPostByTopic(topic);
|
||||
Locale locale = Locale.getDefault();
|
||||
DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.FULL, locale);
|
||||
|
||||
String mes = posts.get(0).getTxt().replace(">", ">");
|
||||
mes = mes.replace("<", "<");
|
||||
// TODO(Zoey76): Replace this with a template.
|
||||
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> > <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; : </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; : </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(posts.get(0)
|
||||
.getDate()), "</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> <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> <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> </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 var posts = getGPostByTopic(t);
|
||||
if (!posts.isEmpty()) {
|
||||
final Post post = posts.get(idp);
|
||||
if (post == null) {
|
||||
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the post: " + idp + " does not exist !</center><br><br></body></html>", activeChar);
|
||||
} else {
|
||||
post.setTxt(ar4);
|
||||
DAOFactory.getInstance().getPostRepository().update(post);
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.bbs.service;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import com.l2jserver.gameserver.bbs.model.Forum;
|
||||
import com.l2jserver.gameserver.bbs.model.ForumType;
|
||||
import com.l2jserver.gameserver.bbs.model.Post;
|
||||
import com.l2jserver.gameserver.bbs.model.Topic;
|
||||
import com.l2jserver.gameserver.bbs.model.TopicType;
|
||||
import com.l2jserver.gameserver.dao.factory.impl.DAOFactory;
|
||||
import com.l2jserver.gameserver.data.sql.impl.ClanTable;
|
||||
import com.l2jserver.gameserver.handler.CommunityBoardHandler;
|
||||
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
|
||||
import com.l2jserver.gameserver.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Topic BBS Manager.
|
||||
* @author Zoey76
|
||||
* @version 2.6.2.0
|
||||
*/
|
||||
public class TopicBBSManager extends BaseBBSManager {
|
||||
|
||||
private final List<Topic> table = new CopyOnWriteArrayList<>();
|
||||
|
||||
private final Map<Forum, Integer> maxId = new HashMap<>();
|
||||
|
||||
protected TopicBBSManager() {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
public void addTopic(Topic topic) {
|
||||
table.add(topic);
|
||||
}
|
||||
|
||||
public void delTopic(Topic topic) {
|
||||
table.remove(topic);
|
||||
}
|
||||
|
||||
public void setMaxId(int id, Forum forum) {
|
||||
maxId.put(forum, id);
|
||||
}
|
||||
|
||||
public int getMaxId(Forum f) {
|
||||
var i = maxId.get(f);
|
||||
if (i == null) {
|
||||
return 0;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
public Topic getTopicById(int id) {
|
||||
for (var topic : table) {
|
||||
if (topic.getId() == id) {
|
||||
return topic;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parsewrite(String ar1, String ar2, String ar3, String ar4, String ar5, L2PcInstance activeChar) {
|
||||
if (ar1.equals("crea")) {
|
||||
final var forum = ForumsBBSManager.getInstance().getForumById(Integer.parseInt(ar2));
|
||||
if (forum == null) {
|
||||
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the forum: " + ar2 + " is not implemented yet</center><br><br></body></html>", activeChar);
|
||||
return;
|
||||
}
|
||||
|
||||
final var id = TopicBBSManager.getInstance().getMaxId(forum) + 1;
|
||||
final var date = Calendar.getInstance().getTimeInMillis();
|
||||
final var topic = new Topic(id, Integer.parseInt(ar2), ar5, date, activeChar.getName(), activeChar.getObjectId(), TopicType.MEMO, 0);
|
||||
|
||||
TopicBBSManager.getInstance().addTopic(topic);
|
||||
|
||||
DAOFactory.getInstance().getTopicRepository().save(topic);
|
||||
|
||||
forum.addTopic(topic);
|
||||
TopicBBSManager.getInstance().setMaxId(topic.getId(), forum);
|
||||
|
||||
final var posts = new LinkedList<Post>();
|
||||
posts.add(new Post(0, activeChar.getName(), activeChar.getObjectId(), Calendar.getInstance().getTimeInMillis(), topic.getId(), forum.getId(), ar4));
|
||||
PostBBSManager.getInstance().addPostByTopic(topic, posts);
|
||||
parsecmd("_bbsmemo", activeChar);
|
||||
} else if (ar1.equals("del")) {
|
||||
final var forum = ForumsBBSManager.getInstance().getForumById(Integer.parseInt(ar2));
|
||||
if (forum == null) {
|
||||
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the forum: " + ar2 + " does not exist !</center><br><br></body></html>", activeChar);
|
||||
return;
|
||||
}
|
||||
|
||||
final var topic = forum.getTopic(Integer.parseInt(ar3));
|
||||
if (topic == null) {
|
||||
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the topic: " + ar3 + " does not exist !</center><br><br></body></html>", activeChar);
|
||||
return;
|
||||
}
|
||||
|
||||
final var posts = PostBBSManager.getInstance().getGPostByTopic(topic);
|
||||
if (!posts.isEmpty()) {
|
||||
PostBBSManager.getInstance().delPostByTopic(topic);
|
||||
DAOFactory.getInstance().getPostRepository().delete(topic);
|
||||
}
|
||||
|
||||
DAOFactory.getInstance().getTopicRepository().delete(topic, forum);
|
||||
|
||||
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;
|
||||
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 {
|
||||
final var p = PostBBSManager.getInstance().getGPostByTopic(t);
|
||||
if (p != null) {
|
||||
PostBBSManager.getInstance().delPostByTopic(t);
|
||||
DAOFactory.getInstance().getPostRepository().delete(t);
|
||||
}
|
||||
|
||||
DAOFactory.getInstance().getTopicRepository().delete(t, 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() == ForumType.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> > <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> </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> </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() == ForumType.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) {
|
||||
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> > <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().getClanCount()) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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 static com.l2jserver.gameserver.config.Configuration.general;
|
||||
import static com.l2jserver.gameserver.config.Configuration.server;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.l2jserver.gameserver.util.file.filter.HTMLFilter;
|
||||
|
||||
/**
|
||||
* HTML Cache.
|
||||
* @author Layane
|
||||
* @author Zoey76
|
||||
*/
|
||||
public class HtmCache {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(HtmCache.class);
|
||||
|
||||
private static final HTMLFilter HTML_FILTER = new HTMLFilter();
|
||||
|
||||
private static final Map<String, String> HTML_CACHE = general().lazyCache() ? new ConcurrentHashMap<>() : new HashMap<>();
|
||||
|
||||
private int _loadedFiles;
|
||||
|
||||
private long _bytesBuffLen;
|
||||
|
||||
protected HtmCache() {
|
||||
reload();
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
reload(server().getDatapackRoot());
|
||||
}
|
||||
|
||||
public void reload(File f) {
|
||||
if (!general().lazyCache()) {
|
||||
LOG.info("Html cache start...");
|
||||
parseDir(f);
|
||||
LOG.info(String.format("%.3f", getMemoryUsage()) + " megabytes on " + getLoadedFiles() + " files loaded.");
|
||||
} else {
|
||||
HTML_CACHE.clear();
|
||||
_loadedFiles = 0;
|
||||
_bytesBuffLen = 0;
|
||||
LOG.info("Running lazy cache.");
|
||||
}
|
||||
}
|
||||
|
||||
public void reloadPath(File f) {
|
||||
parseDir(f);
|
||||
LOG.info("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();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
if (!file.isDirectory()) {
|
||||
loadFile(file);
|
||||
} else {
|
||||
parseDir(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String loadFile(File file) {
|
||||
if (!HTML_FILTER.accept(file)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String content = null;
|
||||
try (var fis = new FileInputStream(file);
|
||||
var 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 = HTML_CACHE.put(file.getCanonicalPath(), content);
|
||||
if (oldContent == null) {
|
||||
_bytesBuffLen += bytes;
|
||||
_loadedFiles++;
|
||||
} else {
|
||||
_bytesBuffLen = (_bytesBuffLen - oldContent.length()) + bytes;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Problem with htm file {}!", file, e);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
public String getHtm(String prefix, String path) {
|
||||
final var newPath = Objects.requireNonNullElse(prefix, "") + path;
|
||||
var content = HTML_CACHE.get(newPath);
|
||||
if (general().lazyCache() && (content == null)) {
|
||||
content = loadFile(new File(server().getDatapackRoot(), newPath));
|
||||
if (content == null) {
|
||||
content = loadFile(new File(server().getScriptRoot(), newPath));
|
||||
}
|
||||
|
||||
// If multilanguage content is not present, try default location.
|
||||
if ((prefix != null) && (content == null)) {
|
||||
content = loadFile(new File(server().getDatapackRoot(), path));
|
||||
if (content == null) {
|
||||
content = loadFile(new File(server().getScriptRoot(), path));
|
||||
}
|
||||
}
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
public boolean contains(String path) {
|
||||
return HTML_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 HTML_FILTER.accept(new File(server().getDatapackRoot(), path));
|
||||
}
|
||||
|
||||
public static HtmCache getInstance() {
|
||||
return SingletonHolder.INSTANCE;
|
||||
}
|
||||
|
||||
private static class SingletonHolder {
|
||||
protected static final HtmCache INSTANCE = new HtmCache();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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 static com.l2jserver.gameserver.config.Configuration.general;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
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 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)) > general().getWarehouseCacheTime()) {
|
||||
pc.clearWarehouse();
|
||||
_cachedWh.remove(pc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static WarehouseCacheManager getInstance() {
|
||||
return SingletonHolder._instance;
|
||||
}
|
||||
|
||||
private static class SingletonHolder {
|
||||
protected static final WarehouseCacheManager _instance = new WarehouseCacheManager();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
import com.l2jserver.gameserver.config.converter.Seconds2MillisecondsConverter;
|
||||
import com.l2jserver.gameserver.model.zone.ZoneId;
|
||||
|
||||
/**
|
||||
* Buffer Service Configuration.
|
||||
* @author HorridoJoho
|
||||
* @version 2.6.2.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/custom/game/config/bufferservice.properties",
|
||||
"file:./config/bufferservice.properties",
|
||||
"classpath:config/bufferservice.properties"
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 20, unit = MINUTES, type = ASYNC)
|
||||
public interface BufferServiceConfiguration extends Reloadable {
|
||||
|
||||
@Key("Enable")
|
||||
boolean enable();
|
||||
|
||||
@Key("HealCooldown")
|
||||
@ConverterClass(Seconds2MillisecondsConverter.class)
|
||||
long getHealCooldown();
|
||||
|
||||
@Key("MaxUniqueLists")
|
||||
int getMaxUniqueLists();
|
||||
|
||||
@Key("Debug")
|
||||
boolean getDebug();
|
||||
|
||||
@Key("ForbidInZones")
|
||||
ZoneId[] getForbidInZones();
|
||||
|
||||
@Key("ForbidInEvents")
|
||||
boolean getForbidInEvents();
|
||||
|
||||
@Key("ForbidInDuel")
|
||||
boolean getForbidInDuel();
|
||||
|
||||
@Key("ForbidInFight")
|
||||
boolean getForbidInFight();
|
||||
|
||||
@Key("ForbidInPvp")
|
||||
boolean getForbidInPvp();
|
||||
|
||||
@Key("ForbidForChaoticPlayers")
|
||||
boolean getForbidForChaoticPlayers();
|
||||
|
||||
@Key("VoicedEnable")
|
||||
boolean getVoicedEnable();
|
||||
|
||||
@Key("VoicedCommand")
|
||||
String getVoicedCommand();
|
||||
|
||||
@Key("VoicedName")
|
||||
String getVoicedName();
|
||||
|
||||
@Key("VoicedRequiredItem")
|
||||
int getVoicedRequiredItem();
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.HOURS;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
/**
|
||||
* Castle Configuration.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/custom/game/config/castle.properties",
|
||||
"file:./config/castle.properties",
|
||||
"classpath:config/castle.properties"
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 1, unit = HOURS, type = ASYNC)
|
||||
public interface CastleConfiguration extends Reloadable {
|
||||
|
||||
@Key("SiegeHourList")
|
||||
List<Integer> getSiegeHourList();
|
||||
|
||||
@Key("FunctionFeeDay7")
|
||||
long getFunctionFeeDay7();
|
||||
|
||||
@Key("TeleportFunctionFeeLvl11")
|
||||
int getTeleportFunctionFeeLvl11();
|
||||
|
||||
@Key("TeleportFunctionFeeLvl12")
|
||||
int getTeleportFunctionFeeLvl12();
|
||||
|
||||
@Key("SupportFeeLvl15")
|
||||
int getSupportFeeLvl15();
|
||||
|
||||
@Key("SupportFeeLvl18")
|
||||
int getSupportFeeLvl18();
|
||||
|
||||
@Key("MpRegenerationFeeLvl18")
|
||||
int getMpRegenerationFeeLvl18();
|
||||
|
||||
@Key("MpRegenerationFeeLvl20")
|
||||
int getMpRegenerationFeeLvl20();
|
||||
|
||||
@Key("HpRegenerationFeeLvl25")
|
||||
int getHpRegenerationFeeLvl25();
|
||||
|
||||
@Key("HpRegenerationFeeLvl30")
|
||||
int getHpRegenerationFeeLvl30();
|
||||
|
||||
@Key("ExpRegenerationFeeLvl19")
|
||||
int getExpRegenerationFeeLvl19();
|
||||
|
||||
@Key("ExpRegenerationFeeLvl20")
|
||||
int getExpRegenerationFeeLvl20();
|
||||
|
||||
@Key("OuterDoorUpgradePriceLvl2")
|
||||
int getOuterDoorUpgradePriceLvl2();
|
||||
|
||||
@Key("OuterDoorUpgradePriceLvl3")
|
||||
int getOuterDoorUpgradePriceLvl3();
|
||||
|
||||
@Key("OuterDoorUpgradePriceLvl5")
|
||||
int getOuterDoorUpgradePriceLvl5();
|
||||
|
||||
@Key("InnerDoorUpgradePriceLvl2")
|
||||
int getInnerDoorUpgradePriceLvl2();
|
||||
|
||||
@Key("InnerDoorUpgradePriceLvl3")
|
||||
int getInnerDoorUpgradePriceLvl3();
|
||||
|
||||
@Key("InnerDoorUpgradePriceLvl5")
|
||||
int getInnerDoorUpgradePriceLvl5();
|
||||
|
||||
@Key("WallUpgradePriceLvl2")
|
||||
int getWallUpgradePriceLvl2();
|
||||
|
||||
@Key("WallUpgradePriceLvl3")
|
||||
int getWallUpgradePriceLvl3();
|
||||
|
||||
@Key("WallUpgradePriceLvl5")
|
||||
int getWallUpgradePriceLvl5();
|
||||
|
||||
@Key("TrapUpgradePriceLvl1")
|
||||
int getTrapUpgradePriceLvl1();
|
||||
|
||||
@Key("TrapUpgradePriceLvl2")
|
||||
int getTrapUpgradePriceLvl2();
|
||||
|
||||
@Key("TrapUpgradePriceLvl3")
|
||||
int getTrapUpgradePriceLvl3();
|
||||
|
||||
@Key("TrapUpgradePriceLvl4")
|
||||
int getTrapUpgradePriceLvl4();
|
||||
|
||||
@Key("AllowRideWyvernAlways")
|
||||
boolean allowRideWyvernAlways();
|
||||
|
||||
@Key("AllowRideWyvernDuringSiege")
|
||||
boolean allowRideWyvernDuringSiege();
|
||||
|
||||
@Key("MpBuffFree")
|
||||
boolean mpBuffFree();
|
||||
}
|
||||
@@ -0,0 +1,663 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
import com.l2jserver.gameserver.config.converter.ClassMasterSetting;
|
||||
import com.l2jserver.gameserver.config.converter.ClassMasterSettingConverter;
|
||||
import com.l2jserver.gameserver.config.converter.Days2MillisecondsConverter;
|
||||
import com.l2jserver.gameserver.config.converter.MapIntegerIntegerConverter;
|
||||
import com.l2jserver.gameserver.config.converter.PatternConverter;
|
||||
import com.l2jserver.gameserver.config.converter.Seconds2MillisecondsConverter;
|
||||
|
||||
/**
|
||||
* Character Configuration.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/custom/game/config/character.properties",
|
||||
"file:./config/character.properties",
|
||||
"classpath:config/character.properties"
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 5, unit = MINUTES, type = ASYNC)
|
||||
public interface CharacterConfiguration extends Reloadable {
|
||||
|
||||
// Statistics
|
||||
@Key("Delevel")
|
||||
boolean delevel();
|
||||
|
||||
@Key("DecreaseSkillOnDelevel")
|
||||
boolean decreaseSkillOnDelevel();
|
||||
|
||||
@Key("WeightLimit")
|
||||
int getWeightLimit();
|
||||
|
||||
@Key("RunSpeedBoost")
|
||||
int getRunSpeedBoost();
|
||||
|
||||
@Key("DeathPenaltyChance")
|
||||
int getDeathPenaltyChance();
|
||||
|
||||
@Key("RespawnRestoreCP")
|
||||
double getRespawnRestoreCP();
|
||||
|
||||
@Key("RespawnRestoreHP")
|
||||
double getRespawnRestoreHP();
|
||||
|
||||
@Key("RespawnRestoreMP")
|
||||
double getRespawnRestoreMP();
|
||||
|
||||
@Key("HpRegenMultiplier")
|
||||
double getHpRegenMultiplier();
|
||||
|
||||
@Key("MpRegenMultiplier")
|
||||
double getMpRegenMultiplier();
|
||||
|
||||
@Key("CpRegenMultiplier")
|
||||
double getCpRegenMultiplier();
|
||||
|
||||
// Skills & Effects
|
||||
|
||||
@Key("ModifySkillDuration")
|
||||
boolean modifySkillDuration();
|
||||
|
||||
@Key("SkillDuration")
|
||||
@ConverterClass(MapIntegerIntegerConverter.class)
|
||||
Map<Integer, Integer> getSkillDuration();
|
||||
|
||||
@Key("ModifySkillReuse")
|
||||
boolean modifySkillReuse();
|
||||
|
||||
@Key("SkillReuse")
|
||||
@ConverterClass(MapIntegerIntegerConverter.class)
|
||||
Map<Integer, Integer> getSkillReuse();
|
||||
|
||||
@Key("AutoLearnSkills")
|
||||
boolean autoLearnSkills();
|
||||
|
||||
@Key("AutoLearnForgottenScrollSkills")
|
||||
boolean autoLearnForgottenScrollSkills();
|
||||
|
||||
@Key("AutoLootHerbs")
|
||||
boolean autoLootHerbs();
|
||||
|
||||
@Key("MaxBuffAmount")
|
||||
int getMaxBuffAmount();
|
||||
|
||||
@Key("MaxTriggeredBuffAmount")
|
||||
int getMaxTriggeredBuffAmount();
|
||||
|
||||
@Key("MaxDanceAmount")
|
||||
int getMaxDanceAmount();
|
||||
|
||||
@Key("DanceCancelBuff")
|
||||
boolean danceCancelBuff();
|
||||
|
||||
@Key("DanceConsumeAdditionalMP")
|
||||
boolean danceConsumeAdditionalMP();
|
||||
|
||||
@Key("StoreDances")
|
||||
boolean storeDances();
|
||||
|
||||
@Key("AutoLearnDivineInspiration")
|
||||
boolean autoLearnDivineInspiration();
|
||||
|
||||
@Key("CancelByHit")
|
||||
String cancelByHit();
|
||||
|
||||
default boolean cancelBow() {
|
||||
return cancelByHit().equalsIgnoreCase("all") || cancelByHit().equalsIgnoreCase("bow");
|
||||
}
|
||||
|
||||
default boolean cancelCast() {
|
||||
return cancelByHit().equalsIgnoreCase("all") || cancelByHit().equalsIgnoreCase("cast");
|
||||
}
|
||||
|
||||
@Key("MagicFailures")
|
||||
boolean magicFailures();
|
||||
|
||||
@Key("PlayerFakeDeathUpProtection")
|
||||
int getPlayerFakeDeathUpProtection();
|
||||
|
||||
@Key("StoreSkillCooltime")
|
||||
boolean storeSkillCooltime();
|
||||
|
||||
@Key("SubclassStoreSkillCooltime")
|
||||
boolean subclassStoreSkillCooltime();
|
||||
|
||||
@Key("ShieldBlocks")
|
||||
boolean shieldBlocks();
|
||||
|
||||
@Key("PerfectShieldBlockRate")
|
||||
int getPerfectShieldBlockRate();
|
||||
|
||||
@Key("EffectTickRatio")
|
||||
int getEffectTickRatio();
|
||||
|
||||
// Class, Sub-class and skill learning
|
||||
|
||||
@Key("AllowClassMasters")
|
||||
boolean allowClassMasters();
|
||||
|
||||
@Key("ConfigClassMaster")
|
||||
@ConverterClass(ClassMasterSettingConverter.class)
|
||||
ClassMasterSetting getClassMaster();
|
||||
|
||||
@Key("AllowEntireTree")
|
||||
boolean allowEntireTree();
|
||||
|
||||
@Key("AlternateClassMaster")
|
||||
boolean alternateClassMaster();
|
||||
|
||||
@Key("LifeCrystalNeeded")
|
||||
boolean lifeCrystalNeeded();
|
||||
|
||||
@Key("EnchantSkillSpBookNeeded")
|
||||
boolean enchantSkillSpBookNeeded();
|
||||
|
||||
@Key("DivineInspirationSpBookNeeded")
|
||||
boolean divineInspirationSpBookNeeded();
|
||||
|
||||
@Key("SkillLearn")
|
||||
boolean skillLearn();
|
||||
|
||||
@Key("SubclassWithoutQuests")
|
||||
boolean subclassWithoutQuests();
|
||||
|
||||
@Key("SubclassEverywhere")
|
||||
boolean subclassEverywhere();
|
||||
|
||||
@Key("TransformationWithoutQuest")
|
||||
boolean transformationWithoutQuest();
|
||||
|
||||
@Key("FeeDeleteTransferSkills")
|
||||
int getFeeDeleteTransferSkills();
|
||||
|
||||
@Key("FeeDeleteSubClassSkills")
|
||||
int getFeeDeleteSubClassSkills();
|
||||
|
||||
// Summons
|
||||
|
||||
@Key("SummonStoreSkillCooltime")
|
||||
boolean summonStoreSkillCooltime();
|
||||
|
||||
@Key("RestoreServitorOnReconnect")
|
||||
boolean restoreServitorOnReconnect();
|
||||
|
||||
@Key("RestorePetOnReconnect")
|
||||
boolean restorePetOnReconnect();
|
||||
|
||||
// Limits
|
||||
|
||||
@Key("MaxExpBonus")
|
||||
double getMaxExpBonus();
|
||||
|
||||
@Key("MaxSpBonus")
|
||||
double getMaxSpBonus();
|
||||
|
||||
@Key("MaxRunSpeed")
|
||||
int getMaxRunSpeed();
|
||||
|
||||
@Key("MaxPCritRate")
|
||||
int getMaxPCritRate();
|
||||
|
||||
@Key("MaxMCritRate")
|
||||
int getMaxMCritRate();
|
||||
|
||||
@Key("MaxPAtkSpeed")
|
||||
int getMaxPAtkSpeed();
|
||||
|
||||
@Key("MaxMAtkSpeed")
|
||||
int getMaxMAtkSpeed();
|
||||
|
||||
@Key("MaxEvasion")
|
||||
int getMaxEvasion();
|
||||
|
||||
@Key("MinAbnormalStateSuccessRate")
|
||||
int getMinAbnormalStateSuccessRate();
|
||||
|
||||
@Key("MaxAbnormalStateSuccessRate")
|
||||
int getMaxAbnormalStateSuccessRate();
|
||||
|
||||
@Key("MaxPlayerLevel")
|
||||
int getMaxPlayerLevel();
|
||||
|
||||
@Key("MaxPetLevel")
|
||||
int getMaxPetLevel();
|
||||
|
||||
@Key("MaxSubclass")
|
||||
int getMaxSubclass();
|
||||
|
||||
@Key("BaseSubclassLevel")
|
||||
int getBaseSubclassLevel();
|
||||
|
||||
@Key("MaxSubclassLevel")
|
||||
int getMaxSubclassLevel();
|
||||
|
||||
@Key("MaxPvtStoreSellSlotsDwarf")
|
||||
int getMaxPvtStoreSellSlotsDwarf();
|
||||
|
||||
@Key("MaxPvtStoreSellSlotsOther")
|
||||
int getMaxPvtStoreSellSlotsOther();
|
||||
|
||||
@Key("MaxPvtStoreBuySlotsDwarf")
|
||||
int getMaxPvtStoreBuySlotsDwarf();
|
||||
|
||||
@Key("MaxPvtStoreBuySlotsOther")
|
||||
int getMaxPvtStoreBuySlotsOther();
|
||||
|
||||
@Key("MaximumSlotsForNoDwarf")
|
||||
int getMaximumSlotsForNoDwarf();
|
||||
|
||||
@Key("MaximumSlotsForDwarf")
|
||||
int getMaximumSlotsForDwarf();
|
||||
|
||||
@Key("MaximumSlotsForGMPlayer")
|
||||
int getMaximumSlotsForGMPlayer();
|
||||
|
||||
@Key("MaximumSlotsForQuestItems")
|
||||
int getMaximumSlotsForQuestItems();
|
||||
|
||||
@Key("MaximumWarehouseSlotsForDwarf")
|
||||
int getMaximumWarehouseSlotsForDwarf();
|
||||
|
||||
@Key("MaximumWarehouseSlotsForNoDwarf")
|
||||
int getMaximumWarehouseSlotsForNoDwarf();
|
||||
|
||||
@Key("MaximumWarehouseSlotsForClan")
|
||||
int getMaximumWarehouseSlotsForClan();
|
||||
|
||||
@Key("MaximumFreightSlots")
|
||||
int getMaximumFreightSlots();
|
||||
|
||||
@Key("FreightPrice")
|
||||
int getFreightPrice();
|
||||
|
||||
@Key("NpcTalkBlockingTime")
|
||||
int getNpcTalkBlockingTime();
|
||||
|
||||
@Key("FriendListLimit")
|
||||
int getFriendListLimit();
|
||||
|
||||
@Key("BlockListLimit")
|
||||
int getBlockListLimit();
|
||||
|
||||
// Enchanting
|
||||
|
||||
@Key("EnchantChanceElementStone")
|
||||
int getEnchantChanceElementStone();
|
||||
|
||||
@Key("EnchantChanceElementCrystal")
|
||||
int getEnchantChanceElementCrystal();
|
||||
|
||||
@Key("EnchantChanceElementJewel")
|
||||
int getEnchantChanceElementJewel();
|
||||
|
||||
@Key("EnchantChanceElementEnergy")
|
||||
int getEnchantChanceElementEnergy();
|
||||
|
||||
@Key("EnchantBlacklist")
|
||||
Set<Integer> getEnchantBlacklist();
|
||||
|
||||
// Augmenting
|
||||
|
||||
@Key("AugmentationNGSkillChance")
|
||||
int getAugmentationNGSkillChance();
|
||||
|
||||
@Key("AugmentationMidSkillChance")
|
||||
int getAugmentationMidSkillChance();
|
||||
|
||||
@Key("AugmentationHighSkillChance")
|
||||
int getAugmentationHighSkillChance();
|
||||
|
||||
@Key("AugmentationTopSkillChance")
|
||||
int getAugmentationTopSkillChance();
|
||||
|
||||
@Key("AugmentationAccSkillChance")
|
||||
int getAugmentationAccSkillChance();
|
||||
|
||||
@Key("AugmentationBaseStatChance")
|
||||
int getAugmentationBaseStatChance();
|
||||
|
||||
@Key("AugmentationNGGlowChance")
|
||||
int getAugmentationNGGlowChance();
|
||||
|
||||
@Key("AugmentationMidGlowChance")
|
||||
int getAugmentationMidGlowChance();
|
||||
|
||||
@Key("AugmentationHighGlowChance")
|
||||
int getAugmentationHighGlowChance();
|
||||
|
||||
@Key("AugmentationTopGlowChance")
|
||||
int getAugmentationTopGlowChance();
|
||||
|
||||
@Key("RetailLikeAugmentation")
|
||||
boolean retailLikeAugmentation();
|
||||
|
||||
@Key("RetailLikeAugmentationNoGradeChance")
|
||||
List<Integer> getRetailLikeAugmentationNoGradeChance();
|
||||
|
||||
@Key("RetailLikeAugmentationMidGradeChance")
|
||||
List<Integer> getRetailLikeAugmentationMidGradeChance();
|
||||
|
||||
@Key("RetailLikeAugmentationHighGradeChance")
|
||||
List<Integer> getRetailLikeAugmentationHighGradeChance();
|
||||
|
||||
@Key("RetailLikeAugmentationTopGradeChance")
|
||||
List<Integer> getRetailLikeAugmentationTopGradeChance();
|
||||
|
||||
@Key("RetailLikeAugmentationAccessory")
|
||||
boolean retailLikeAugmentationAccessory();
|
||||
|
||||
@Key("AugmentationBlacklist")
|
||||
Set<Integer> getAugmentationBlacklist();
|
||||
|
||||
@Key("AllowAugmentPvPItems")
|
||||
boolean allowAugmentPvPItems();
|
||||
|
||||
// Karma
|
||||
|
||||
@Key("KarmaPlayerCanBeKilledInPeaceZone")
|
||||
boolean karmaPlayerCanBeKilledInPeaceZone();
|
||||
|
||||
@Key("KarmaPlayerCanUseGK")
|
||||
boolean karmaPlayerCanUseGK();
|
||||
|
||||
@Key("KarmaPlayerCanTeleport")
|
||||
boolean karmaPlayerCanTeleport();
|
||||
|
||||
@Key("KarmaPlayerCanShop")
|
||||
boolean karmaPlayerCanShop();
|
||||
|
||||
@Key("KarmaPlayerCanTrade")
|
||||
boolean karmaPlayerCanTrade();
|
||||
|
||||
@Key("KarmaPlayerCanUseWareHouse")
|
||||
boolean karmaPlayerCanUseWareHouse();
|
||||
|
||||
// Fame
|
||||
|
||||
@Key("MaxPersonalFamePoints")
|
||||
int getMaxPersonalFamePoints();
|
||||
|
||||
@Key("FortressZoneFameTaskFrequency")
|
||||
@ConverterClass(Seconds2MillisecondsConverter.class)
|
||||
long getFortressZoneFameTaskFrequency();
|
||||
|
||||
@Key("FortressZoneFameAcquirePoints")
|
||||
int getFortressZoneFameAcquirePoints();
|
||||
|
||||
@Key("CastleZoneFameTaskFrequency")
|
||||
@ConverterClass(Seconds2MillisecondsConverter.class)
|
||||
long getCastleZoneFameTaskFrequency();
|
||||
|
||||
@Key("CastleZoneFameAcquirePoints")
|
||||
int getCastleZoneFameAcquirePoints();
|
||||
|
||||
@Key("FameForDeadPlayers")
|
||||
boolean fameForDeadPlayers();
|
||||
|
||||
// Crafting
|
||||
|
||||
@Key("Crafting")
|
||||
boolean crafting();
|
||||
|
||||
@Key("CraftMasterwork")
|
||||
boolean craftMasterwork();
|
||||
|
||||
@Key("DwarfRecipeLimit")
|
||||
int getDwarfRecipeLimit();
|
||||
|
||||
@Key("CommonRecipeLimit")
|
||||
int getCommonRecipeLimit();
|
||||
|
||||
@Key("AlternativeCrafting")
|
||||
boolean alternativeCrafting();
|
||||
|
||||
@Key("CraftingSpeed")
|
||||
double getCraftingSpeed();
|
||||
|
||||
@Key("CraftingXpRate")
|
||||
double getCraftingXpRate();
|
||||
|
||||
@Key("CraftingSpRate")
|
||||
double getCraftingSpRate();
|
||||
|
||||
@Key("CraftingRareXpRate")
|
||||
double getCraftingRareXpRate();
|
||||
|
||||
@Key("CraftingRareSpRate")
|
||||
double getCraftingRareSpRate();
|
||||
|
||||
@Key("BlacksmithUseRecipes")
|
||||
boolean blacksmithUseRecipes();
|
||||
|
||||
@Key("StoreRecipeShopList")
|
||||
boolean storeRecipeShopList();
|
||||
|
||||
// Clan
|
||||
|
||||
@Key("ClanLeaderDateChange")
|
||||
int getClanLeaderDateChange();
|
||||
|
||||
@Key("ClanLeaderHourChange")
|
||||
String getClanLeaderHourChange();
|
||||
|
||||
@Key("ClanLeaderInstantActivation")
|
||||
boolean clanLeaderInstantActivation();
|
||||
|
||||
@Key("DaysBeforeJoinAClan")
|
||||
@ConverterClass(Days2MillisecondsConverter.class)
|
||||
long getDaysBeforeJoinAClan();
|
||||
|
||||
@Key("DaysBeforeCreateAClan")
|
||||
@ConverterClass(Days2MillisecondsConverter.class)
|
||||
long getDaysBeforeCreateAClan();
|
||||
|
||||
@Key("DaysToPassToDissolveAClan")
|
||||
@ConverterClass(Days2MillisecondsConverter.class)
|
||||
long getDaysToPassToDissolveAClan();
|
||||
|
||||
@Key("DaysBeforeJoiningAllianceAfterLeaving")
|
||||
@ConverterClass(Days2MillisecondsConverter.class)
|
||||
long getDaysBeforeJoiningAllianceAfterLeaving();
|
||||
|
||||
@Key("DaysBeforeJoinAllyWhenDismissed")
|
||||
@ConverterClass(Days2MillisecondsConverter.class)
|
||||
long getDaysBeforeJoinAllyWhenDismissed();
|
||||
|
||||
@Key("DaysBeforeAcceptNewClanWhenDismissed")
|
||||
@ConverterClass(Days2MillisecondsConverter.class)
|
||||
long getDaysBeforeAcceptNewClanWhenDismissed();
|
||||
|
||||
@Key("DaysBeforeCreateNewAllyWhenDissolved")
|
||||
@ConverterClass(Days2MillisecondsConverter.class)
|
||||
long getDaysBeforeCreateNewAllyWhenDissolved();
|
||||
|
||||
@Key("MaxNumOfClansInAlly")
|
||||
int getMaxNumOfClansInAlly();
|
||||
|
||||
@Key("MembersCanWithdrawFromClanWH")
|
||||
boolean membersCanWithdrawFromClanWH();
|
||||
|
||||
@Key("RemoveCastleCirclets")
|
||||
boolean removeCastleCirclets();
|
||||
|
||||
@Key("ClanMembersForWar")
|
||||
int getClanMembersForWar();
|
||||
|
||||
// Party
|
||||
|
||||
@Key("PartyRange")
|
||||
int getPartyRange();
|
||||
|
||||
@Key("PartyRange2")
|
||||
int getPartyRange2();
|
||||
|
||||
@Key("PartyEvenlyDistributeAllStackableItems")
|
||||
boolean getPartyEvenlyDistributeAllStackableItems();
|
||||
|
||||
@Key("PartyEvenlyDistributeAllOtherItems")
|
||||
boolean getPartyEvenlyDistributeAllOtherItems();
|
||||
|
||||
@Key("PartyEvenlyDistributeItems")
|
||||
List<Integer> getPartyEvenlyDistributeItems();
|
||||
|
||||
@Key("LeavePartyLeader")
|
||||
boolean leavePartyLeader();
|
||||
|
||||
// Initial
|
||||
|
||||
@Key("InitialEquipmentEvent")
|
||||
boolean initialEquipmentEvent();
|
||||
|
||||
@Key("StartingAdena")
|
||||
int getStartingAdena();
|
||||
|
||||
@Key("StartingLevel")
|
||||
int getStartingLevel();
|
||||
|
||||
@Key("StartingSP")
|
||||
int getStartingSP();
|
||||
|
||||
// Other
|
||||
|
||||
@Key("MaxAdena")
|
||||
long getMaxAdena();
|
||||
|
||||
@Key("AutoLoot")
|
||||
boolean autoLoot();
|
||||
|
||||
@Key("AutoLootRaids")
|
||||
boolean autoLootRaids();
|
||||
|
||||
@Key("RaidLootRightsInterval")
|
||||
@ConverterClass(Seconds2MillisecondsConverter.class)
|
||||
long getRaidLootRightsInterval();
|
||||
|
||||
@Key("RaidLootRightsCCSize")
|
||||
int getRaidLootRightsCCSize();
|
||||
|
||||
@Key("UnstuckInterval")
|
||||
@ConverterClass(Seconds2MillisecondsConverter.class)
|
||||
long getUnstuckInterval();
|
||||
|
||||
@Key("TeleportWatchdogTimeout")
|
||||
@ConverterClass(Seconds2MillisecondsConverter.class)
|
||||
long getTeleportWatchdogTimeout();
|
||||
|
||||
@Key("PlayerSpawnProtection")
|
||||
int getPlayerSpawnProtection();
|
||||
|
||||
@Key("PlayerSpawnProtectionAllowedItems")
|
||||
Set<Integer> getPlayerSpawnProtectionAllowedItems();
|
||||
|
||||
@Key("PlayerTeleportProtection")
|
||||
int getPlayerTeleportProtection();
|
||||
|
||||
@Key("RandomRespawnInTown")
|
||||
boolean randomRespawnInTown();
|
||||
|
||||
@Key("OffsetOnTeleport")
|
||||
boolean offsetOnTeleport();
|
||||
|
||||
@Key("MaxOffsetOnTeleport")
|
||||
int getMaxOffsetOnTeleport();
|
||||
|
||||
@Key("PetitioningAllowed")
|
||||
boolean petitioningAllowed();
|
||||
|
||||
@Key("MaxPetitionsPerPlayer")
|
||||
int getMaxPetitionsPerPlayer();
|
||||
|
||||
@Key("MaxPetitionsPending")
|
||||
int getMaxPetitionsPending();
|
||||
|
||||
@Key("FreeTeleporting")
|
||||
boolean freeTeleporting();
|
||||
|
||||
@Key("DeleteCharAfterDays")
|
||||
int getDeleteCharAfterDays();
|
||||
|
||||
@Key("ExponentXp")
|
||||
int getExponentXp();
|
||||
|
||||
@Key("ExponentSp")
|
||||
int getExponentSp();
|
||||
|
||||
@Key("PartyXpCutoffMethod")
|
||||
String getPartyXpCutoffMethod();
|
||||
|
||||
@Key("PartyXpCutoffPercent")
|
||||
double getPartyXpCutoffPercent();
|
||||
|
||||
@Key("PartyXpCutoffLevel")
|
||||
int getPartyXpCutoffLevel();
|
||||
|
||||
@Key("PartyXpCutoffGaps")
|
||||
@ConverterClass(MapIntegerIntegerConverter.class)
|
||||
Map<Integer, Integer> getPartyXpCutoffGaps();
|
||||
|
||||
@Key("PartyXpCutoffGapPercent")
|
||||
List<Integer> getPartyXpCutoffGapPercent();
|
||||
|
||||
@Key("Tutorial")
|
||||
boolean tutorial();
|
||||
|
||||
@Key("ExpertisePenalty")
|
||||
boolean expertisePenalty();
|
||||
|
||||
@Key("StoreUISettings")
|
||||
boolean storeUISettings();
|
||||
|
||||
@Key("SilenceModeExclude")
|
||||
boolean silenceModeExclude();
|
||||
|
||||
@Key("ValidateTriggerSkills")
|
||||
boolean validateTriggerSkills();
|
||||
|
||||
@Key("PlayerNameTemplate")
|
||||
@ConverterClass(PatternConverter.class)
|
||||
Pattern getPlayerNameTemplate();
|
||||
|
||||
@Key("PetNameTemplate")
|
||||
@ConverterClass(PatternConverter.class)
|
||||
Pattern getPetNameTemplate();
|
||||
|
||||
@Key("ForbiddenNames")
|
||||
Set<String> getForbiddenNames();
|
||||
|
||||
@Key("CharMaxNumber")
|
||||
int getCharMaxNumber();
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
import com.l2jserver.gameserver.config.converter.PatternConverter;
|
||||
|
||||
/**
|
||||
* Clan Configuration.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/custom/game/config/clan.properties",
|
||||
"file:./config/clan.properties",
|
||||
"classpath:config/clan.properties"
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 20, unit = MINUTES, type = ASYNC)
|
||||
public interface ClanConfiguration extends Reloadable {
|
||||
|
||||
@Key("ClanNameTemplate")
|
||||
@ConverterClass(PatternConverter.class)
|
||||
Pattern getClanNameTemplate();
|
||||
|
||||
@Key("TakeFortPoints")
|
||||
int getTakeFortPoints();
|
||||
|
||||
@Key("TakeCastlePoints")
|
||||
int getTakeCastlePoints();
|
||||
|
||||
@Key("CastleDefendedPoints")
|
||||
int getCastleDefendedPoints();
|
||||
|
||||
@Key("FestivalOfDarknessWin")
|
||||
int getFestivalOfDarknessWin();
|
||||
|
||||
@Key("HeroPoints")
|
||||
int getHeroPoints();
|
||||
|
||||
@Key("CompleteAcademyMinPoints")
|
||||
int getCompleteAcademyMinPoints();
|
||||
|
||||
@Key("CompleteAcademyMaxPoints")
|
||||
int getCompleteAcademyMaxPoints();
|
||||
|
||||
@Key("KillBallistaPoints")
|
||||
int getKillBallistaPoints();
|
||||
|
||||
@Key("BloodAlliancePoints")
|
||||
int getBloodAlliancePoints();
|
||||
|
||||
@Key("BloodOathPoints")
|
||||
int getBloodOathPoints();
|
||||
|
||||
@Key("KnightsEpaulettePoints")
|
||||
int getKnightsEpaulettePoints();
|
||||
|
||||
@Key("1stRaidRankingPoints")
|
||||
int get1stRaidRankingPoints();
|
||||
|
||||
@Key("2ndRaidRankingPoints")
|
||||
int get2ndRaidRankingPoints();
|
||||
|
||||
@Key("3rdRaidRankingPoints")
|
||||
int get3rdRaidRankingPoints();
|
||||
|
||||
@Key("4thRaidRankingPoints")
|
||||
int get4thRaidRankingPoints();
|
||||
|
||||
@Key("5thRaidRankingPoints")
|
||||
int get5thRaidRankingPoints();
|
||||
|
||||
@Key("6thRaidRankingPoints")
|
||||
int get6thRaidRankingPoints();
|
||||
|
||||
@Key("7thRaidRankingPoints")
|
||||
int get7thRaidRankingPoints();
|
||||
|
||||
@Key("8thRaidRankingPoints")
|
||||
int get8thRaidRankingPoints();
|
||||
|
||||
@Key("9thRaidRankingPoints")
|
||||
int get9thRaidRankingPoints();
|
||||
|
||||
@Key("10thRaidRankingPoints")
|
||||
int get10thRaidRankingPoints();
|
||||
|
||||
@Key("UpTo50thRaidRankingPoints")
|
||||
int getUpTo50thRaidRankingPoints();
|
||||
|
||||
@Key("UpTo100thRaidRankingPoints")
|
||||
int getUpTo100thRaidRankingPoints();
|
||||
|
||||
@Key("ReputationScorePerKill")
|
||||
int getReputationScorePerKill();
|
||||
|
||||
@Key("LoseFortPoints")
|
||||
int getLoseFortPoints();
|
||||
|
||||
@Key("LoseCastlePoints")
|
||||
int getLoseCastlePoints();
|
||||
|
||||
@Key("CreateRoyalGuardCost")
|
||||
int getCreateRoyalGuardCost();
|
||||
|
||||
@Key("CreateKnightUnitCost")
|
||||
int getCreateKnightUnitCost();
|
||||
|
||||
// TODO(Zoey76): Implement ReinforceKnightUnitCost configuration.
|
||||
@Key("ReinforceKnightUnitCost")
|
||||
int getReinforceKnightUnitCost();
|
||||
|
||||
@Key("ClanLevel6Cost")
|
||||
int getClanLevel6Cost();
|
||||
|
||||
@Key("ClanLevel7Cost")
|
||||
int getClanLevel7Cost();
|
||||
|
||||
@Key("ClanLevel8Cost")
|
||||
int getClanLevel8Cost();
|
||||
|
||||
@Key("ClanLevel9Cost")
|
||||
int getClanLevel9Cost();
|
||||
|
||||
@Key("ClanLevel10Cost")
|
||||
int getClanLevel10Cost();
|
||||
|
||||
@Key("ClanLevel11Cost")
|
||||
int getClanLevel11Cost();
|
||||
|
||||
@Key("ClanLevel6Requirement")
|
||||
int getClanLevel6Requirement();
|
||||
|
||||
@Key("ClanLevel7Requirement")
|
||||
int getClanLevel7Requirement();
|
||||
|
||||
@Key("ClanLevel8Requirement")
|
||||
int getClanLevel8Requirement();
|
||||
|
||||
@Key("ClanLevel9Requirement")
|
||||
int getClanLevel9Requirement();
|
||||
|
||||
@Key("ClanLevel10Requirement")
|
||||
int getClanLevel10Requirement();
|
||||
|
||||
@Key("ClanLevel11Requirement")
|
||||
int getClanLevel11Requirement();
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
/**
|
||||
* Clan Hall Configuration.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/custom/game/config/clanhall.properties",
|
||||
"file:./config/clanhall.properties",
|
||||
"classpath:config/clanhall.properties"
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 20, unit = MINUTES, type = ASYNC)
|
||||
public interface ClanHallConfiguration extends Reloadable {
|
||||
|
||||
@Key("FunctionFeeDay1")
|
||||
long getFunctionFeeDay1();
|
||||
|
||||
@Key("FunctionFeeDay2")
|
||||
long getFunctionFeeDay2();
|
||||
|
||||
@Key("FunctionFeeDay3")
|
||||
long getFunctionFeeDay3();
|
||||
|
||||
@Key("FunctionFeeDay7")
|
||||
long getFunctionFeeDay7();
|
||||
|
||||
@Key("TeleportFunctionFeeLvl1")
|
||||
int getTeleportFunctionFeeLvl1();
|
||||
|
||||
@Key("TeleportFunctionFeeLvl2")
|
||||
int getTeleportFunctionFeeLvl2();
|
||||
|
||||
@Key("TeleportFunctionFeeLvl11")
|
||||
int getTeleportFunctionFeeLvl11();
|
||||
|
||||
@Key("TeleportFunctionFeeLvl12")
|
||||
int getTeleportFunctionFeeLvl12();
|
||||
|
||||
@Key("SupportFeeLvl1")
|
||||
int getSupportFeeLvl1();
|
||||
|
||||
@Key("SupportFeeLvl2")
|
||||
int getSupportFeeLvl2();
|
||||
|
||||
@Key("SupportFeeLvl3")
|
||||
int getSupportFeeLvl3();
|
||||
|
||||
@Key("SupportFeeLvl4")
|
||||
int getSupportFeeLvl4();
|
||||
|
||||
@Key("SupportFeeLvl5")
|
||||
int getSupportFeeLvl5();
|
||||
|
||||
@Key("SupportFeeLvl7")
|
||||
int getSupportFeeLvl7();
|
||||
|
||||
@Key("SupportFeeLvl8")
|
||||
int getSupportFeeLvl8();
|
||||
|
||||
@Key("SupportFeeLvl15")
|
||||
int getSupportFeeLvl15();
|
||||
|
||||
@Key("SupportFeeLvl18")
|
||||
int getSupportFeeLvl18();
|
||||
|
||||
@Key("MpRegenerationFeeLvl1")
|
||||
int getMpRegenerationFeeLvl1();
|
||||
|
||||
@Key("MpRegenerationFeeLvl3")
|
||||
int getMpRegenerationFeeLvl3();
|
||||
|
||||
@Key("MpRegenerationFeeLvl5")
|
||||
int getMpRegenerationFeeLvl5();
|
||||
|
||||
@Key("MpRegenerationFeeLvl6")
|
||||
int getMpRegenerationFeeLvl6();
|
||||
|
||||
@Key("MpRegenerationFeeLvl8")
|
||||
int getMpRegenerationFeeLvl8();
|
||||
|
||||
@Key("MpRegenerationFeeLvl18")
|
||||
int getMpRegenerationFeeLvl18();
|
||||
|
||||
@Key("MpRegenerationFeeLvl20")
|
||||
int getMpRegenerationFeeLvl20();
|
||||
|
||||
@Key("HpRegenerationFeeLvl2")
|
||||
int getHpRegenerationFeeLvl2();
|
||||
|
||||
@Key("HpRegenerationFeeLvl4")
|
||||
int getHpRegenerationFeeLvl4();
|
||||
|
||||
@Key("HpRegenerationFeeLvl5")
|
||||
int getHpRegenerationFeeLvl5();
|
||||
|
||||
@Key("HpRegenerationFeeLvl6")
|
||||
int getHpRegenerationFeeLvl6();
|
||||
|
||||
@Key("HpRegenerationFeeLvl7")
|
||||
int getHpRegenerationFeeLvl7();
|
||||
|
||||
@Key("HpRegenerationFeeLvl8")
|
||||
int getHpRegenerationFeeLvl8();
|
||||
|
||||
@Key("HpRegenerationFeeLvl9")
|
||||
int getHpRegenerationFeeLvl9();
|
||||
|
||||
@Key("HpRegenerationFeeLvl10")
|
||||
int getHpRegenerationFeeLvl10();
|
||||
|
||||
@Key("HpRegenerationFeeLvl12")
|
||||
int getHpRegenerationFeeLvl12();
|
||||
|
||||
@Key("HpRegenerationFeeLvl13")
|
||||
int getHpRegenerationFeeLvl13();
|
||||
|
||||
@Key("HpRegenerationFeeLvl15")
|
||||
int getHpRegenerationFeeLvl15();
|
||||
|
||||
@Key("HpRegenerationFeeLvl25")
|
||||
int getHpRegenerationFeeLvl25();
|
||||
|
||||
@Key("HpRegenerationFeeLvl30")
|
||||
int getHpRegenerationFeeLvl30();
|
||||
|
||||
@Key("ExpRegenerationFeeLvl1")
|
||||
int getExpRegenerationFeeLvl1();
|
||||
|
||||
@Key("ExpRegenerationFeeLvl3")
|
||||
int getExpRegenerationFeeLvl3();
|
||||
|
||||
@Key("ExpRegenerationFeeLvl5")
|
||||
int getExpRegenerationFeeLvl5();
|
||||
|
||||
@Key("ExpRegenerationFeeLvl6")
|
||||
int getExpRegenerationFeeLvl6();
|
||||
|
||||
@Key("ExpRegenerationFeeLvl7")
|
||||
int getExpRegenerationFeeLvl7();
|
||||
|
||||
@Key("ExpRegenerationFeeLvl8")
|
||||
int getExpRegenerationFeeLvl8();
|
||||
|
||||
@Key("ExpRegenerationFeeLvl10")
|
||||
int getExpRegenerationFeeLvl10();
|
||||
|
||||
@Key("ExpRegenerationFeeLvl19")
|
||||
int getExpRegenerationFeeLvl19();
|
||||
|
||||
@Key("ExpRegenerationFeeLvl20")
|
||||
int getExpRegenerationFeeLvl20();
|
||||
|
||||
@Key("ItemCreationFunctionFeeLvl1")
|
||||
int getItemCreationFunctionFeeLvl1();
|
||||
|
||||
@Key("ItemCreationFunctionFeeLvl2")
|
||||
int getItemCreationFunctionFeeLvl2();
|
||||
|
||||
@Key("ItemCreationFunctionFeeLvl3")
|
||||
int getItemCreationFunctionFeeLvl3();
|
||||
|
||||
@Key("ItemCreationFunctionFeeLvl11")
|
||||
int getItemCreationFunctionFeeLvl11();
|
||||
|
||||
@Key("ItemCreationFunctionFeeLvl12")
|
||||
int getItemCreationFunctionFeeLvl12();
|
||||
|
||||
@Key("ItemCreationFunctionFeeLvl13")
|
||||
int getItemCreationFunctionFeeLvl13();
|
||||
|
||||
@Key("CurtainFunctionFeeLvl1")
|
||||
int getCurtainFunctionFeeLvl1();
|
||||
|
||||
@Key("CurtainFunctionFeeLvl2")
|
||||
int getCurtainFunctionFeeLvl2();
|
||||
|
||||
@Key("FrontPlatformFunctionFeeLvl1")
|
||||
int getFrontPlatformFunctionFeeLvl1();
|
||||
|
||||
@Key("FrontPlatformFunctionFeeLvl2")
|
||||
int getFrontPlatformFunctionFeeLvl2();
|
||||
|
||||
@Key("BroadCastFunctionFeeLvl1")
|
||||
int getBroadCastFunctionFeeLvl1();
|
||||
|
||||
@Key("BroadCastFunctionFeeLvl2")
|
||||
int getBroadCastFunctionFeeLvl2();
|
||||
|
||||
@Key("MpBuffFree")
|
||||
boolean mpBuffFree();
|
||||
|
||||
@Key("MinClanLevel")
|
||||
int getMinClanLevel();
|
||||
|
||||
@Key("MaxAttackers")
|
||||
int getMaxAttackers();
|
||||
|
||||
// TODO(Zoey76): Implement MaxFlagsPerClan configuration.
|
||||
@Key("MaxFlagsPerClan")
|
||||
int getMaxFlagsPerClan();
|
||||
|
||||
@Key("EnableFame")
|
||||
boolean enableFame();
|
||||
|
||||
@Key("FameAmount")
|
||||
int getFameAmount();
|
||||
|
||||
@Key("FameFrequency")
|
||||
int getFameFrequency();
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.aeonbits.owner.ConfigFactory;
|
||||
|
||||
/**
|
||||
* Configuration.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
public class Configuration {
|
||||
|
||||
public static final String DEFAULT_PATH = "config/";
|
||||
|
||||
public static final String CUSTOM_SUBPATH = "custom/game/config/";
|
||||
|
||||
public static final String EOL = System.lineSeparator();
|
||||
|
||||
private static final IPConfigData ipConfigData = new IPConfigData();
|
||||
|
||||
private static final ServerConfiguration server = ConfigFactory.create(ServerConfiguration.class);
|
||||
|
||||
private static final HexIdConfiguration hexId = ConfigFactory.create(HexIdConfiguration.class);
|
||||
|
||||
private static final DatabaseConfiguration database = ConfigFactory.create(DatabaseConfiguration.class);
|
||||
|
||||
private static final MMOConfiguration mmo = ConfigFactory.create(MMOConfiguration.class);
|
||||
|
||||
private static final TelnetConfiguration telnet = ConfigFactory.create(TelnetConfiguration.class);
|
||||
|
||||
private static final GeneralConfiguration general = ConfigFactory.create(GeneralConfiguration.class);
|
||||
|
||||
private static final GeodataConfiguration geodata = ConfigFactory.create(GeodataConfiguration.class);
|
||||
|
||||
private static final CharacterConfiguration character = ConfigFactory.create(CharacterConfiguration.class);
|
||||
|
||||
private static final CastleConfiguration castle = ConfigFactory.create(CastleConfiguration.class);
|
||||
|
||||
private static final ClanConfiguration clan = ConfigFactory.create(ClanConfiguration.class);
|
||||
|
||||
private static final ClanHallConfiguration clanhall = ConfigFactory.create(ClanHallConfiguration.class);
|
||||
|
||||
private static final FortressConfiguration fortress = ConfigFactory.create(FortressConfiguration.class);
|
||||
|
||||
private static final SevenSignsConfiguration sevenSigns = ConfigFactory.create(SevenSignsConfiguration.class);
|
||||
|
||||
private static final TvTConfiguration tvt = ConfigFactory.create(TvTConfiguration.class);
|
||||
|
||||
private static final NPCConfiguration npc = ConfigFactory.create(NPCConfiguration.class);
|
||||
|
||||
private static final OlympiadConfiguration olympiad = ConfigFactory.create(OlympiadConfiguration.class);
|
||||
|
||||
private static final SiegeConfiguration siege = ConfigFactory.create(SiegeConfiguration.class);
|
||||
|
||||
private static final TerritoryWarConfiguration territoryWar = ConfigFactory.create(TerritoryWarConfiguration.class);
|
||||
|
||||
private static final FortSiegeConfiguration fortSiege = ConfigFactory.create(FortSiegeConfiguration.class);
|
||||
|
||||
private static final GrandBossConfiguration grandBoss = ConfigFactory.create(GrandBossConfiguration.class);
|
||||
|
||||
private static final GraciaSeedsConfiguration graciaSeeds = ConfigFactory.create(GraciaSeedsConfiguration.class);
|
||||
|
||||
private static final RatesConfiguration rates = ConfigFactory.create(RatesConfiguration.class);
|
||||
|
||||
private static final VitalityConfiguration vitality = ConfigFactory.create(VitalityConfiguration.class);
|
||||
|
||||
private static final PvPConfiguration pvp = ConfigFactory.create(PvPConfiguration.class);
|
||||
|
||||
private static final CustomsConfiguration customs = ConfigFactory.create(CustomsConfiguration.class);
|
||||
|
||||
private static final HuntingSystemConfiguration hunting = ConfigFactory.create(HuntingSystemConfiguration.class);
|
||||
|
||||
private static final FloodProtectorConfiguration floodProtector = ConfigFactory.create(FloodProtectorConfiguration.class);
|
||||
|
||||
private static final BufferServiceConfiguration bufferService = ConfigFactory.create(BufferServiceConfiguration.class);
|
||||
|
||||
private static final TeleporterServiceConfiguration teleporterService = ConfigFactory.create(TeleporterServiceConfiguration.class);
|
||||
|
||||
private static final DiscordConfiguration discord = ConfigFactory.create(DiscordConfiguration.class);
|
||||
|
||||
private Configuration() {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
public static ServerConfiguration server() {
|
||||
return server;
|
||||
}
|
||||
|
||||
public static HexIdConfiguration hexId() {
|
||||
return hexId;
|
||||
}
|
||||
|
||||
public static DatabaseConfiguration database() {
|
||||
return database;
|
||||
}
|
||||
|
||||
public static MMOConfiguration mmo() {
|
||||
return mmo;
|
||||
}
|
||||
|
||||
public static TelnetConfiguration telnet() {
|
||||
return telnet;
|
||||
}
|
||||
|
||||
public static GeneralConfiguration general() {
|
||||
return general;
|
||||
}
|
||||
|
||||
public static GeodataConfiguration geodata() {
|
||||
return geodata;
|
||||
}
|
||||
|
||||
public static CharacterConfiguration character() {
|
||||
return character;
|
||||
}
|
||||
|
||||
public static CastleConfiguration castle() {
|
||||
return castle;
|
||||
}
|
||||
|
||||
public static ClanConfiguration clan() {
|
||||
return clan;
|
||||
}
|
||||
|
||||
public static ClanHallConfiguration clanhall() {
|
||||
return clanhall;
|
||||
}
|
||||
|
||||
public static FortressConfiguration fortress() {
|
||||
return fortress;
|
||||
}
|
||||
|
||||
public static SevenSignsConfiguration sevenSigns() {
|
||||
return sevenSigns;
|
||||
}
|
||||
|
||||
public static TvTConfiguration tvt() {
|
||||
return tvt;
|
||||
}
|
||||
|
||||
public static NPCConfiguration npc() {
|
||||
return npc;
|
||||
}
|
||||
|
||||
public static OlympiadConfiguration olympiad() {
|
||||
return olympiad;
|
||||
}
|
||||
|
||||
public static SiegeConfiguration siege() {
|
||||
return siege;
|
||||
}
|
||||
|
||||
public static TerritoryWarConfiguration territoryWar() {
|
||||
return territoryWar;
|
||||
}
|
||||
|
||||
public static FortSiegeConfiguration fortSiege() {
|
||||
return fortSiege;
|
||||
}
|
||||
|
||||
public static GrandBossConfiguration grandBoss() {
|
||||
return grandBoss;
|
||||
}
|
||||
|
||||
public static GraciaSeedsConfiguration graciaSeeds() {
|
||||
return graciaSeeds;
|
||||
}
|
||||
|
||||
public static RatesConfiguration rates() {
|
||||
return rates;
|
||||
}
|
||||
|
||||
public static VitalityConfiguration vitality() {
|
||||
return vitality;
|
||||
}
|
||||
|
||||
public static PvPConfiguration pvp() {
|
||||
return pvp;
|
||||
}
|
||||
|
||||
public static CustomsConfiguration customs() {
|
||||
return customs;
|
||||
}
|
||||
|
||||
public static HuntingSystemConfiguration hunting() {
|
||||
return hunting;
|
||||
}
|
||||
|
||||
public static FloodProtectorConfiguration floodProtector() {
|
||||
return floodProtector;
|
||||
}
|
||||
|
||||
public static IPConfigData ip() {
|
||||
return ipConfigData;
|
||||
}
|
||||
|
||||
public static BufferServiceConfiguration bufferService() {
|
||||
return bufferService;
|
||||
}
|
||||
|
||||
public static TeleporterServiceConfiguration teleporterService() {
|
||||
return teleporterService;
|
||||
}
|
||||
|
||||
public static DiscordConfiguration discord() {
|
||||
return discord;
|
||||
}
|
||||
|
||||
public static String getDefaultPath(String filename) {
|
||||
return DEFAULT_PATH + filename;
|
||||
}
|
||||
|
||||
public static String getCustomSubpath(String filename) {
|
||||
return CUSTOM_SUBPATH + filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns either the custom or default path of the config file.<br>
|
||||
* When the L2J_HOME variable is defined, the custom path to the config file is returned. If not, the default path to the config file is returned.
|
||||
* @param filename the filename
|
||||
* @return either the custom or default path to the config file
|
||||
*/
|
||||
public static Path getCustomOrDefaultPath(String filename) {
|
||||
var l2jHome = server().getL2jHome();
|
||||
if (l2jHome == null) {
|
||||
return Path.of(getDefaultPath(filename));
|
||||
}
|
||||
return Path.of(l2jHome, getCustomSubpath(filename));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
import com.l2jserver.gameserver.config.converter.ColorConverter;
|
||||
import com.l2jserver.gameserver.config.converter.IPLimitConverter;
|
||||
import com.l2jserver.gameserver.config.converter.Seconds2MillisecondsConverter;
|
||||
|
||||
/**
|
||||
* Customs Configuration.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/custom/game/config/customs.properties",
|
||||
"file:./config/customs.properties",
|
||||
"classpath:config/customs.properties"
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 20, unit = MINUTES, type = ASYNC)
|
||||
public interface CustomsConfiguration extends Reloadable {
|
||||
|
||||
@Key("ChampionEnable")
|
||||
boolean championEnable();
|
||||
|
||||
@Key("ChampionPassive")
|
||||
boolean championPassive();
|
||||
|
||||
@Key("ChampionFrequency")
|
||||
int getChampionFrequency();
|
||||
|
||||
@Key("ChampionTitle")
|
||||
String getChampionTitle();
|
||||
|
||||
@Key("ChampionMinLevel")
|
||||
int getChampionMinLevel();
|
||||
|
||||
@Key("ChampionMaxLevel")
|
||||
int getChampionMaxLevel();
|
||||
|
||||
@Key("ChampionHp")
|
||||
int getChampionHp();
|
||||
|
||||
@Key("ChampionHpRegen")
|
||||
double getChampionHpRegen();
|
||||
|
||||
@Key("ChampionRewardsExpSp")
|
||||
double getChampionRewardsExpSp();
|
||||
|
||||
@Key("ChampionRewardsChance")
|
||||
double getChampionRewardsChance();
|
||||
|
||||
@Key("ChampionRewardsAmount")
|
||||
double getChampionRewardsAmount();
|
||||
|
||||
@Key("ChampionAdenasRewardsChance")
|
||||
double getChampionAdenasRewardsChance();
|
||||
|
||||
@Key("ChampionAdenasRewardsAmount")
|
||||
double getChampionAdenasRewardsAmount();
|
||||
|
||||
@Key("ChampionAtk")
|
||||
float getChampionAtk();
|
||||
|
||||
@Key("ChampionSpdAtk")
|
||||
float getChampionSpdAtk();
|
||||
|
||||
@Key("ChampionRewardItemID")
|
||||
int getChampionRewardItemID();
|
||||
|
||||
@Key("ChampionRewardItemQty")
|
||||
int getChampionRewardItemQty();
|
||||
|
||||
@Key("ChampionRewardLowerLvlItemChance")
|
||||
int getChampionRewardLowerLvlItemChance();
|
||||
|
||||
@Key("ChampionRewardHigherLvlItemChance")
|
||||
int getChampionRewardHigherLvlItemChance();
|
||||
|
||||
@Key("ChampionEnableVitality")
|
||||
boolean championEnableVitality();
|
||||
|
||||
@Key("ChampionEnableInInstances")
|
||||
boolean championEnableInInstances();
|
||||
|
||||
@Key("AllowWedding")
|
||||
boolean allowWedding();
|
||||
|
||||
@Key("WeddingPrice")
|
||||
int getWeddingPrice();
|
||||
|
||||
@Key("WeddingPunishInfidelity")
|
||||
boolean weddingPunishInfidelity();
|
||||
|
||||
// TODO(Zoey76): Implement WeddingTeleport configuration.
|
||||
@Key("WeddingTeleport")
|
||||
boolean weddingTeleport();
|
||||
|
||||
@Key("WeddingTeleportPrice")
|
||||
int getWeddingTeleportPrice();
|
||||
|
||||
@Key("WeddingTeleportDuration")
|
||||
@ConverterClass(Seconds2MillisecondsConverter.class)
|
||||
long getWeddingTeleportDuration();
|
||||
|
||||
@Key("WeddingAllowSameSex")
|
||||
boolean weddingAllowSameSex();
|
||||
|
||||
@Key("WeddingFormalWear")
|
||||
boolean weddingFormalWear();
|
||||
|
||||
@Key("WeddingDivorceCosts")
|
||||
int getWeddingDivorceCosts();
|
||||
|
||||
@Key("BankingEnabled")
|
||||
boolean bankingEnabled();
|
||||
|
||||
@Key("BankingGoldbarCount")
|
||||
int getBankingGoldbarCount();
|
||||
|
||||
@Key("BankingAdenaCount")
|
||||
int getBankingAdenaCount();
|
||||
|
||||
@Key("EnableWarehouseSortingClan")
|
||||
boolean enableWarehouseSortingClan();
|
||||
|
||||
@Key("EnableWarehouseSortingPrivate")
|
||||
boolean enableWarehouseSortingPrivate();
|
||||
|
||||
@Key("OfflineTradeEnable")
|
||||
boolean offlineTradeEnable();
|
||||
|
||||
@Key("OfflineCraftEnable")
|
||||
boolean offlineCraftEnable();
|
||||
|
||||
@Key("OfflineModeInPeaceZone")
|
||||
boolean offlineModeInPeaceZone();
|
||||
|
||||
@Key("OfflineModeNoDamage")
|
||||
boolean offlineModeNoDamage();
|
||||
|
||||
@Key("OfflineSetNameColor")
|
||||
boolean offlineSetNameColor();
|
||||
|
||||
@Key("OfflineNameColor")
|
||||
@ConverterClass(ColorConverter.class)
|
||||
int getOfflineNameColor();
|
||||
|
||||
@Key("OfflineFame")
|
||||
boolean offlineFame();
|
||||
|
||||
@Key("RestoreOffliners")
|
||||
boolean restoreOffliners();
|
||||
|
||||
@Key("OfflineMaxDays")
|
||||
int getOfflineMaxDays();
|
||||
|
||||
@Key("OfflineDisconnectFinished")
|
||||
boolean offlineDisconnectFinished();
|
||||
|
||||
@Key("EnableManaPotionSupport")
|
||||
boolean enableManaPotionSupport();
|
||||
|
||||
@Key("DisplayServerTime")
|
||||
boolean displayServerTime();
|
||||
|
||||
@Key("ScreenWelcomeMessageEnable")
|
||||
boolean screenWelcomeMessageEnable();
|
||||
|
||||
@Key("ScreenWelcomeMessageText")
|
||||
String getScreenWelcomeMessageText();
|
||||
|
||||
@Key("ScreenWelcomeMessageTime")
|
||||
@ConverterClass(Seconds2MillisecondsConverter.class)
|
||||
long getScreenWelcomeMessageTime();
|
||||
|
||||
@Key("AntiFeedEnable")
|
||||
boolean antiFeedEnable();
|
||||
|
||||
@Key("AntiFeedDualbox")
|
||||
boolean antiFeedDualbox();
|
||||
|
||||
@Key("AntiFeedDisconnectedAsDualbox")
|
||||
boolean antiFeedDisconnectedAsDualbox();
|
||||
|
||||
@Key("AntiFeedInterval")
|
||||
int getAntiFeedInterval();
|
||||
|
||||
@Key("AnnouncePkPvP")
|
||||
boolean announcePkPvP();
|
||||
|
||||
@Key("AnnouncePkPvPNormalMessage")
|
||||
boolean announcePkPvPNormalMessage();
|
||||
|
||||
@Key("AnnouncePkMsg")
|
||||
String getAnnouncePkMsg();
|
||||
|
||||
@Key("AnnouncePvpMsg")
|
||||
String getAnnouncePvpMsg();
|
||||
|
||||
@Key("ChatAdmin")
|
||||
boolean chatAdmin();
|
||||
|
||||
@Key("HellboundStatus")
|
||||
boolean hellboundStatus();
|
||||
|
||||
@Key("AutoLootVoiceEnable")
|
||||
boolean autoLootVoiceCommand();
|
||||
|
||||
@Key("AutoLootVoiceRestore")
|
||||
boolean autoLootVoiceRestore();
|
||||
|
||||
@Key("AutoLootItemsVoiceRestore")
|
||||
boolean autoLootItemsVoiceRestore();
|
||||
|
||||
@Key("AutoLootHerbsVoiceRestore")
|
||||
boolean autoLootHerbsVoiceRestore();
|
||||
|
||||
@Key("AutoLootHerbsList")
|
||||
Set<Integer> getAutoLootHerbsList();
|
||||
|
||||
@Key("AutoLootItemsList")
|
||||
Set<Integer> getAutoLootItemsList();
|
||||
|
||||
@Key("MultiLangEnable")
|
||||
boolean multiLangEnable();
|
||||
|
||||
@Key("MultiLangDefault")
|
||||
String getMultiLangDefault();
|
||||
|
||||
@Key("MultiLangAllowed")
|
||||
Set<String> getMultiLangAllowed();
|
||||
|
||||
@Key("MultiLangHandler")
|
||||
boolean multiLangHandler();
|
||||
|
||||
@Key("MultiLangSystemMessageEnable")
|
||||
boolean multiLangSystemMessageEnable();
|
||||
|
||||
@Key("MultiLangSystemMessageAllowed")
|
||||
Set<String> getMultiLangSystemMessageAllowed();
|
||||
|
||||
@Key("MultiLangNpcStringEnable")
|
||||
boolean multiLangNpcStringEnable();
|
||||
|
||||
@Key("MultiLangNpcStringAllowed")
|
||||
Set<String> getMultiLangNpcStringAllowed();
|
||||
|
||||
@Key("L2WalkerProtection")
|
||||
boolean l2WalkerProtection();
|
||||
|
||||
@Key("DebugHandler")
|
||||
boolean debugHandler();
|
||||
|
||||
@Key("DualboxCheckMaxPlayersPerIP")
|
||||
int getDualboxCheckMaxPlayersPerIP();
|
||||
|
||||
@Key("DualboxCheckMaxOlympiadParticipantsPerIP")
|
||||
int getDualboxCheckMaxOlympiadParticipantsPerIP();
|
||||
|
||||
@Key("DualboxCheckMaxL2EventParticipantsPerIP")
|
||||
int getDualboxCheckMaxL2EventParticipantsPerIP();
|
||||
|
||||
@Key("DualboxCheckWhitelist")
|
||||
@ConverterClass(IPLimitConverter.class)
|
||||
Map<Integer, Integer> getDualboxCheckWhitelist();
|
||||
|
||||
@Key("AllowChangePassword")
|
||||
boolean allowChangePassword();
|
||||
|
||||
@Key("AllowXpHandler")
|
||||
boolean allowXpHandler();
|
||||
|
||||
@Key("AllowCastleHandler")
|
||||
boolean allowCastleHandler();
|
||||
|
||||
@Key("AllowClanHandler")
|
||||
boolean allowClanHandler();
|
||||
|
||||
@Key("AllowStatHandler")
|
||||
boolean allowStatHandler();
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
/**
|
||||
* Database Configuration.
|
||||
* @author Zoey76
|
||||
* @version 2.6.3.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/custom/game/config/database.properties",
|
||||
"file:./config/database.properties",
|
||||
"classpath:config/database.properties"
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 20, unit = MINUTES, type = ASYNC)
|
||||
public interface DatabaseConfiguration extends Reloadable {
|
||||
|
||||
@Key("URL")
|
||||
String getURL();
|
||||
|
||||
@Key("User")
|
||||
String getUser();
|
||||
|
||||
@Key("Password")
|
||||
String getPassword();
|
||||
|
||||
@Key("MaxConnections")
|
||||
int getMaxConnections();
|
||||
|
||||
@Key("MaxIdleTime")
|
||||
int getMaxIdleTime();
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Mutable;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
/**
|
||||
* Discord Configuration.
|
||||
* @author Stalitsa
|
||||
* @version 2.6.2.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/custom/game/config/discord.properties",
|
||||
"file:./config/discord.properties",
|
||||
"classpath:config/discord.properties"
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 20, unit = MINUTES, type = ASYNC)
|
||||
public interface DiscordConfiguration extends Mutable, Reloadable {
|
||||
|
||||
@Key("BotEnable")
|
||||
boolean enableBot();
|
||||
|
||||
@Key("BotPrefix")
|
||||
String getPrefix();
|
||||
|
||||
@Key("BotToken")
|
||||
String getBotToken();
|
||||
|
||||
@Key("ServerId")
|
||||
String getServerId();
|
||||
|
||||
@Key("GameMasterId")
|
||||
String getGameMasterId();
|
||||
|
||||
@Key("ConsoleLogChannelId")
|
||||
String getConsoleLogChannelId();
|
||||
|
||||
@Key("GameChatChannelId")
|
||||
String getGameChatChannelId();
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import org.aeonbits.owner.Accessible;
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
/**
|
||||
* Flood Protector Configuration.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/custom/game/config/floodprotector.properties",
|
||||
"file:./config/floodprotector.properties",
|
||||
"classpath:config/floodprotector.properties"
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 20, unit = MINUTES, type = ASYNC)
|
||||
public interface FloodProtectorConfiguration extends Accessible, Reloadable {
|
||||
|
||||
// TODO(Zoey76): Implement all these configurations.
|
||||
|
||||
@Key("UseItemInterval")
|
||||
int getUseItemInterval();
|
||||
|
||||
@Key("UseItemLogFlooding")
|
||||
boolean useItemLogFlooding();
|
||||
|
||||
@Key("UseItemPunishmentLimit")
|
||||
int getUseItemPunishmentLimit();
|
||||
|
||||
@Key("UseItemPunishmentType")
|
||||
String getUseItemPunishmentType();
|
||||
|
||||
@Key("UseItemPunishmentTime")
|
||||
int getUseItemPunishmentTime();
|
||||
|
||||
@Key("RollDiceInterval")
|
||||
int getRollDiceInterval();
|
||||
|
||||
@Key("RollDiceLogFlooding")
|
||||
boolean rollDiceLogFlooding();
|
||||
|
||||
@Key("RollDicePunishmentLimit")
|
||||
int getRollDicePunishmentLimit();
|
||||
|
||||
@Key("RollDicePunishmentType")
|
||||
String getRollDicePunishmentType();
|
||||
|
||||
@Key("RollDicePunishmentTime")
|
||||
int getRollDicePunishmentTime();
|
||||
|
||||
@Key("FireworkInterval")
|
||||
int getFireworkInterval();
|
||||
|
||||
@Key("FireworkLogFlooding")
|
||||
boolean fireworkLogFlooding();
|
||||
|
||||
@Key("FireworkPunishmentLimit")
|
||||
int getFireworkPunishmentLimit();
|
||||
|
||||
@Key("FireworkPunishmentType")
|
||||
String getFireworkPunishmentType();
|
||||
|
||||
@Key("FireworkPunishmentTime")
|
||||
int getFireworkPunishmentTime();
|
||||
|
||||
@Key("ItemPetSummonInterval")
|
||||
int geItemPetSummonInterval();
|
||||
|
||||
@Key("ItemPetSummonLogFlooding")
|
||||
boolean itemPetSummonLogFlooding();
|
||||
|
||||
@Key("ItemPetSummonPunishmentLimit")
|
||||
int getItemPetSummonPunishmentLimit();
|
||||
|
||||
@Key("ItemPetSummonPunishmentType")
|
||||
String getItemPetSummonPunishmentType();
|
||||
|
||||
@Key("ItemPetSummonPunishmentTime")
|
||||
int getItemPetSummonPunishmentTime();
|
||||
|
||||
@Key("HeroVoiceInterval")
|
||||
int getHeroVoiceInterval();
|
||||
|
||||
@Key("HeroVoiceLogFlooding")
|
||||
boolean heroVoiceLogFlooding();
|
||||
|
||||
@Key("HeroVoicePunishmentLimit")
|
||||
int getHeroVoicePunishmentLimit();
|
||||
|
||||
@Key("HeroVoicePunishmentType")
|
||||
String getHeroVoicePunishmentType();
|
||||
|
||||
@Key("HeroVoicePunishmentTime")
|
||||
int getHeroVoicePunishmentTime();
|
||||
|
||||
@Key("GlobalChatInterval")
|
||||
int getGlobalChatInterval();
|
||||
|
||||
@Key("GlobalChatLogFlooding")
|
||||
boolean globalChatLogFlooding();
|
||||
|
||||
@Key("GlobalChatPunishmentLimit")
|
||||
int getGlobalChatPunishmentLimit();
|
||||
|
||||
@Key("GlobalChatPunishmentType")
|
||||
String getGlobalChatPunishmentType();
|
||||
|
||||
@Key("GlobalChatPunishmentTime")
|
||||
int getGlobalChatPunishmentTime();
|
||||
|
||||
@Key("SubclassInterval")
|
||||
int getSubclassInterval();
|
||||
|
||||
@Key("SubclassLogFlooding")
|
||||
boolean subclassLogFlooding();
|
||||
|
||||
@Key("SubclassPunishmentLimit")
|
||||
int getSubclassPunishmentLimit();
|
||||
|
||||
@Key("SubclassPunishmentType")
|
||||
String getSubclassPunishmentType();
|
||||
|
||||
@Key("SubclassPunishmentTime")
|
||||
int getSubclassPunishmentTime();
|
||||
|
||||
@Key("DropItemInterval")
|
||||
int getDropItemInterval();
|
||||
|
||||
@Key("DropItemLogFlooding")
|
||||
boolean dropItemLogFlooding();
|
||||
|
||||
@Key("DropItemPunishmentLimit")
|
||||
int getDropItemPunishmentLimit();
|
||||
|
||||
@Key("DropItemPunishmentType")
|
||||
String getDropItemPunishmentType();
|
||||
|
||||
@Key("DropItemPunishmentTime")
|
||||
int getDropItemPunishmentTime();
|
||||
|
||||
@Key("ServerBypassInterval")
|
||||
int getServerBypassInterval();
|
||||
|
||||
@Key("ServerBypassLogFlooding")
|
||||
boolean serverBypassLogFlooding();
|
||||
|
||||
@Key("ServerBypassPunishmentLimit")
|
||||
int getServerBypassPunishmentLimit();
|
||||
|
||||
@Key("ServerBypassPunishmentType")
|
||||
String getServerBypassPunishmentType();
|
||||
|
||||
@Key("ServerBypassPunishmentTime")
|
||||
int getServerBypassPunishmentTime();
|
||||
|
||||
@Key("MultiSellInterval")
|
||||
int getMultiSellInterval();
|
||||
|
||||
@Key("MultiSellLogFlooding")
|
||||
boolean multiSellLogFlooding();
|
||||
|
||||
@Key("MultiSellPunishmentLimit")
|
||||
int getMultiSellPunishmentLimit();
|
||||
|
||||
@Key("MultiSellPunishmentType")
|
||||
String getMultiSellPunishmentType();
|
||||
|
||||
@Key("MultiSellPunishmentTime")
|
||||
int getMultiSellPunishmentTime();
|
||||
|
||||
@Key("TransactionInterval")
|
||||
int getTransactionInterval();
|
||||
|
||||
@Key("TransactionLogFlooding")
|
||||
boolean transactionLogFlooding();
|
||||
|
||||
@Key("TransactionPunishmentLimit")
|
||||
int getTransactionPunishmentLimit();
|
||||
|
||||
@Key("TransactionPunishmentType")
|
||||
String getTransactionPunishmentType();
|
||||
|
||||
@Key("TransactionPunishmentTime")
|
||||
int getTransactionPunishmentTime();
|
||||
|
||||
@Key("ManufactureInterval")
|
||||
int getManufactureInterval();
|
||||
|
||||
@Key("ManufactureLogFlooding")
|
||||
boolean manufactureLogFlooding();
|
||||
|
||||
@Key("ManufacturePunishmentLimit")
|
||||
int getManufacturePunishmentLimit();
|
||||
|
||||
@Key("ManufacturePunishmentType")
|
||||
String getManufacturePunishmentType();
|
||||
|
||||
@Key("ManufacturePunishmentTime")
|
||||
int getManufacturePunishmentTime();
|
||||
|
||||
@Key("ManorInterval")
|
||||
int getManorInterval();
|
||||
|
||||
@Key("ManorLogFlooding")
|
||||
boolean manorLogFlooding();
|
||||
|
||||
@Key("ManorPunishmentLimit")
|
||||
int getManorPunishmentLimit();
|
||||
|
||||
@Key("ManorPunishmentType")
|
||||
String getManorPunishmentType();
|
||||
|
||||
@Key("ManorPunishmentTime")
|
||||
int getManorPunishmentTime();
|
||||
|
||||
@Key("SendMailInterval")
|
||||
int getSendMailInterval();
|
||||
|
||||
@Key("SendMailLogFlooding")
|
||||
boolean sendMailLogFlooding();
|
||||
|
||||
@Key("SendMailPunishmentLimit")
|
||||
int getSendMailPunishmentLimit();
|
||||
|
||||
@Key("SendMailPunishmentType")
|
||||
String getSendMailPunishmentType();
|
||||
|
||||
@Key("SendMailPunishmentTime")
|
||||
int getSendMailPunishmentTime();
|
||||
|
||||
@Key("CharacterSelectInterval")
|
||||
int getCharacterSelectInterval();
|
||||
|
||||
@Key("CharacterSelectLogFlooding")
|
||||
boolean characterSelectLogFlooding();
|
||||
|
||||
@Key("CharacterSelectPunishmentLimit")
|
||||
int getCharacterSelectPunishmentLimit();
|
||||
|
||||
@Key("CharacterSelectPunishmentType")
|
||||
String getCharacterSelectPunishmentType();
|
||||
|
||||
@Key("CharacterSelectPunishmentTime")
|
||||
int getCharacterSelectPunishmentTime();
|
||||
|
||||
@Key("ItemAuctionInterval")
|
||||
int getItemAuctionInterval();
|
||||
|
||||
@Key("ItemAuctionLogFlooding")
|
||||
boolean itemAuctionLogFlooding();
|
||||
|
||||
@Key("ItemAuctionPunishmentLimit")
|
||||
int getItemAuctionPunishmentLimit();
|
||||
|
||||
@Key("ItemAuctionPunishmentType")
|
||||
String getItemAuctionPunishmentType();
|
||||
|
||||
@Key("ItemAuctionPunishmentTime")
|
||||
int getItemAuctionPunishmentTime();
|
||||
}
|
||||
@@ -0,0 +1,478 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import org.aeonbits.owner.Accessible;
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
import com.l2jserver.gameserver.config.converter.Minutes2MillisecondsConverter;
|
||||
|
||||
/**
|
||||
* Fort Siege Configuration.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/custom/game/config/fortsiege.properties",
|
||||
"file:./config/fortsiege.properties",
|
||||
"classpath:config/fortsiege.properties"
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 20, unit = MINUTES, type = ASYNC)
|
||||
public interface FortSiegeConfiguration extends Accessible, Reloadable {
|
||||
|
||||
@Key("SiegeLength")
|
||||
@ConverterClass(Minutes2MillisecondsConverter.class)
|
||||
long getSiegeLength();
|
||||
|
||||
@Key("SuspiciousMerchantRespawnDelay")
|
||||
int getSuspiciousMerchantRespawnDelay();
|
||||
|
||||
@Key("CountDownLength")
|
||||
int getCountDownLength();
|
||||
|
||||
@Key("MaxFlags")
|
||||
int getMaxFlags();
|
||||
|
||||
@Key("SiegeClanMinLevel")
|
||||
int getSiegeClanMinLevel();
|
||||
|
||||
@Key("AttackerMaxClans")
|
||||
int getAttackerMaxClans();
|
||||
|
||||
@Key("JustToTerritory")
|
||||
boolean justToTerritory();
|
||||
|
||||
// TODO(Zoey76): Most, if not all, of this configurations are not used.
|
||||
|
||||
@Key("ShantyCommander1")
|
||||
String getShantyCommander1();
|
||||
|
||||
@Key("ShantyCommander2")
|
||||
String getShantyCommander2();
|
||||
|
||||
@Key("ShantyCommander3")
|
||||
String getShantyCommander3();
|
||||
|
||||
@Key("ShantyFlag1")
|
||||
String getShantyFlag1();
|
||||
|
||||
@Key("ShantyFlag2")
|
||||
String getShantyFlag2();
|
||||
|
||||
@Key("ShantyFlag3")
|
||||
String getShantyFlag3();
|
||||
|
||||
@Key("SouthernCommander1")
|
||||
String getSouthernCommander1();
|
||||
|
||||
@Key("SouthernCommander2")
|
||||
String getSouthernCommander2();
|
||||
|
||||
@Key("SouthernCommander3")
|
||||
String getSouthernCommander3();
|
||||
|
||||
@Key("SouthernCommander4")
|
||||
String getSouthernCommander4();
|
||||
|
||||
@Key("SouthernFlag1")
|
||||
String getSouthernFlag1();
|
||||
|
||||
@Key("SouthernFlag2")
|
||||
String getSouthernFlag2();
|
||||
|
||||
@Key("SouthernFlag3")
|
||||
String getSouthernFlag3();
|
||||
|
||||
@Key("HiveCommander1")
|
||||
String getHiveCommander1();
|
||||
|
||||
@Key("HiveCommander2")
|
||||
String getHiveCommander2();
|
||||
|
||||
@Key("HiveCommander3")
|
||||
String getHiveCommander3();
|
||||
|
||||
@Key("HiveFlag1")
|
||||
String getHiveFlag1();
|
||||
|
||||
@Key("HiveFlag2")
|
||||
String getHiveFlag2();
|
||||
|
||||
@Key("HiveFlag3")
|
||||
String getHiveFlag3();
|
||||
|
||||
@Key("ValleyCommander1")
|
||||
String getValleyCommander1();
|
||||
|
||||
@Key("ValleyCommander2")
|
||||
String getValleyCommander2();
|
||||
|
||||
@Key("ValleyCommander3")
|
||||
String getValleyCommander3();
|
||||
|
||||
@Key("ValleyCommander4")
|
||||
String getValleyCommander4();
|
||||
|
||||
@Key("ValleyFlag1")
|
||||
String getValleyFlag1();
|
||||
|
||||
@Key("ValleyFlag2")
|
||||
String getValleyFlag2();
|
||||
|
||||
@Key("ValleyFlag3")
|
||||
String getValleyFlag3();
|
||||
|
||||
@Key("IvoryCommander1")
|
||||
String getIvoryCommander1();
|
||||
|
||||
@Key("IvoryCommander2")
|
||||
String getIvoryCommander2();
|
||||
|
||||
@Key("IvoryCommander3")
|
||||
String getIvoryCommander3();
|
||||
|
||||
@Key("IvoryFlag1")
|
||||
String getIvoryFlag1();
|
||||
|
||||
@Key("IvoryFlag2")
|
||||
String getIvoryFlag2();
|
||||
|
||||
@Key("IvoryFlag3")
|
||||
String getIvoryFlag3();
|
||||
|
||||
@Key("NarsellCommander1")
|
||||
String getNarsellCommander1();
|
||||
|
||||
@Key("NarsellCommander2")
|
||||
String getNarsellCommander2();
|
||||
|
||||
@Key("NarsellCommander3")
|
||||
String getNarsellCommander3();
|
||||
|
||||
@Key("NarsellFlag1")
|
||||
String getNarsellFlag1();
|
||||
|
||||
@Key("NarsellFlag2")
|
||||
String getNarsellFlag2();
|
||||
|
||||
@Key("NarsellFlag3")
|
||||
String getNarsellFlag3();
|
||||
|
||||
@Key("BayouCommander1")
|
||||
String getBayouCommander1();
|
||||
|
||||
@Key("BayouCommander2")
|
||||
String getBayouCommander2();
|
||||
|
||||
@Key("BayouCommander3")
|
||||
String getBayouCommander3();
|
||||
|
||||
@Key("BayouCommander4")
|
||||
String getBayouCommander4();
|
||||
|
||||
@Key("BayouFlag1")
|
||||
String getBayouFlag1();
|
||||
|
||||
@Key("BayouFlag2")
|
||||
String getBayouFlag2();
|
||||
|
||||
@Key("BayouFlag3")
|
||||
String getBayouFlag3();
|
||||
|
||||
@Key("WhiteSandsCommander1")
|
||||
String getWhiteSandsCommander1();
|
||||
|
||||
@Key("WhiteSandsCommander2")
|
||||
String getWhiteSandsCommander2();
|
||||
|
||||
@Key("WhiteSandsCommander3")
|
||||
String getWhiteSandsCommander3();
|
||||
|
||||
@Key("WhiteSandsFlag1")
|
||||
String getWhiteSandsFlag1();
|
||||
|
||||
@Key("WhiteSandsFlag2")
|
||||
String getWhiteSandsFlag2();
|
||||
|
||||
@Key("WhiteSandsFlag3")
|
||||
String getWhiteSandsFlag3();
|
||||
|
||||
@Key("BorderlandCommander1")
|
||||
String getBorderlandCommander1();
|
||||
|
||||
@Key("BorderlandCommander2")
|
||||
String getBorderlandCommander2();
|
||||
|
||||
@Key("BorderlandCommander3")
|
||||
String getBorderlandCommander3();
|
||||
|
||||
@Key("BorderlandCommander4")
|
||||
String getBorderlandCommander4();
|
||||
|
||||
@Key("BorderlandFlag1")
|
||||
String getBorderlandFlag1();
|
||||
|
||||
@Key("BorderlandFlag2")
|
||||
String getBorderlandFlag2();
|
||||
|
||||
@Key("BorderlandFlag3")
|
||||
String getBorderlandFlag3();
|
||||
|
||||
@Key("SwampCommander1")
|
||||
String getSwampCommander1();
|
||||
|
||||
@Key("SwampCommander2")
|
||||
String getSwampCommander2();
|
||||
|
||||
@Key("SwampCommander3")
|
||||
String getSwampCommander3();
|
||||
|
||||
@Key("SwampCommander4")
|
||||
String getSwampCommander4();
|
||||
|
||||
@Key("SwampFlag1")
|
||||
String getSwampFlag1();
|
||||
|
||||
@Key("SwampFlag2")
|
||||
String getSwampFlag2();
|
||||
|
||||
@Key("SwampFlag3")
|
||||
String getSwampFlag3();
|
||||
|
||||
@Key("ArchaicCommander1")
|
||||
String getArchaicCommander1();
|
||||
|
||||
@Key("ArchaicCommander2")
|
||||
String getArchaicCommander2();
|
||||
|
||||
@Key("ArchaicCommander3")
|
||||
String getArchaicCommander3();
|
||||
|
||||
@Key("ArchaicFlag1")
|
||||
String getArchaicFlag1();
|
||||
|
||||
@Key("ArchaicFlag2")
|
||||
String getArchaicFlag2();
|
||||
|
||||
@Key("ArchaicFlag3")
|
||||
String getArchaicFlag3();
|
||||
|
||||
@Key("FloranCommander1")
|
||||
String getFloranCommander1();
|
||||
|
||||
@Key("FloranCommander2")
|
||||
String getFloranCommander2();
|
||||
|
||||
@Key("FloranCommander3")
|
||||
String getFloranCommander3();
|
||||
|
||||
@Key("FloranCommander4")
|
||||
String getFloranCommander4();
|
||||
|
||||
@Key("FloranFlag1")
|
||||
String getFloranFlag1();
|
||||
|
||||
@Key("FloranFlag2")
|
||||
String getFloranFlag2();
|
||||
|
||||
@Key("FloranFlag3")
|
||||
String getFloranFlag3();
|
||||
|
||||
@Key("CloudMountainCommander1")
|
||||
String getCloudMountainCommander1();
|
||||
|
||||
@Key("CloudMountainCommander2")
|
||||
String getCloudMountainCommander2();
|
||||
|
||||
@Key("CloudMountainCommander3")
|
||||
String getCloudMountainCommander3();
|
||||
|
||||
@Key("CloudMountainCommander4")
|
||||
String getCloudMountainCommander4();
|
||||
|
||||
@Key("CloudMountainFlag1")
|
||||
String getCloudMountainFlag1();
|
||||
|
||||
@Key("CloudMountainFlag2")
|
||||
String getCloudMountainFlag2();
|
||||
|
||||
@Key("CloudMountainFlag3")
|
||||
String getCloudMountainFlag3();
|
||||
|
||||
@Key("TanorCommander1")
|
||||
String getTanorCommander1();
|
||||
|
||||
@Key("TanorCommander2")
|
||||
String getTanorCommander2();
|
||||
|
||||
@Key("TanorCommander3")
|
||||
String getTanorCommander3();
|
||||
|
||||
@Key("TanorFlag1")
|
||||
String getTanorFlag1();
|
||||
|
||||
@Key("TanorFlag2")
|
||||
String getTanorFlag2();
|
||||
|
||||
@Key("TanorFlag3")
|
||||
String getTanorFlag3();
|
||||
|
||||
@Key("DragonspineCommander1")
|
||||
String getDragonspineCommander1();
|
||||
|
||||
@Key("DragonspineCommander2")
|
||||
String getDragonspineCommander2();
|
||||
|
||||
@Key("DragonspineCommander3")
|
||||
String getDragonspineCommander3();
|
||||
|
||||
@Key("DragonspineFlag1")
|
||||
String getDragonspineFlag1();
|
||||
|
||||
@Key("DragonspineFlag2")
|
||||
String getDragonspineFlag2();
|
||||
|
||||
@Key("DragonspineFlag3")
|
||||
String getDragonspineFlag3();
|
||||
|
||||
@Key("AntharasCommander1")
|
||||
String getAntharasCommander1();
|
||||
|
||||
@Key("AntharasCommander2")
|
||||
String getAntharasCommander2();
|
||||
|
||||
@Key("AntharasCommander3")
|
||||
String getAntharasCommander3();
|
||||
|
||||
@Key("AntharasCommander4")
|
||||
String getAntharasCommander4();
|
||||
|
||||
@Key("AntharasFlag1")
|
||||
String getAntharasFlag1();
|
||||
|
||||
@Key("AntharasFlag2")
|
||||
String getAntharasFlag2();
|
||||
|
||||
@Key("AntharasFlag3")
|
||||
String getAntharasFlag3();
|
||||
|
||||
@Key("WesternCommander1")
|
||||
String getWesternCommander1();
|
||||
|
||||
@Key("WesternCommander2")
|
||||
String getWesternCommander2();
|
||||
|
||||
@Key("WesternCommander3")
|
||||
String getWesternCommander3();
|
||||
|
||||
@Key("WesternCommander4")
|
||||
String getWesternCommander4();
|
||||
|
||||
@Key("WesternFlag1")
|
||||
String getWesternFlag1();
|
||||
|
||||
@Key("WesternFlag2")
|
||||
String getWesternFlag2();
|
||||
|
||||
@Key("WesternFlag3")
|
||||
String getWesternFlag3();
|
||||
|
||||
@Key("HuntersCommander1")
|
||||
String getHuntersCommander1();
|
||||
|
||||
@Key("HuntersCommander2")
|
||||
String getHuntersCommander2();
|
||||
|
||||
@Key("HuntersCommander3")
|
||||
String getHuntersCommander3();
|
||||
|
||||
@Key("HuntersCommander4")
|
||||
String getHuntersCommander4();
|
||||
|
||||
@Key("HuntersFlag1")
|
||||
String getHuntersFlag1();
|
||||
|
||||
@Key("HuntersFlag2")
|
||||
String getHuntersFlag2();
|
||||
|
||||
@Key("HuntersFlag3")
|
||||
String getHuntersFlag3();
|
||||
|
||||
@Key("AaruCommander1")
|
||||
String getAaruCommander1();
|
||||
|
||||
@Key("AaruCommander2")
|
||||
String getAaruCommander2();
|
||||
|
||||
@Key("AaruCommander3")
|
||||
String getAaruCommander3();
|
||||
|
||||
@Key("AaruFlag1")
|
||||
String getAaruFlag1();
|
||||
|
||||
@Key("AaruFlag2")
|
||||
String getAaruFlag2();
|
||||
|
||||
@Key("AaruFlag3")
|
||||
String getAaruFlag3();
|
||||
|
||||
@Key("DemonCommander1")
|
||||
String getDemonCommander1();
|
||||
|
||||
@Key("DemonCommander2")
|
||||
String getDemonCommander2();
|
||||
|
||||
@Key("DemonCommander3")
|
||||
String getDemonCommander3();
|
||||
|
||||
@Key("DemonFlag1")
|
||||
String getDemonFlag1();
|
||||
|
||||
@Key("DemonFlag2")
|
||||
String getDemonFlag2();
|
||||
|
||||
@Key("DemonFlag3")
|
||||
String getDemonFlag3();
|
||||
|
||||
@Key("MonasticCommander1")
|
||||
String getMonasticCommander1();
|
||||
|
||||
@Key("MonasticCommander2")
|
||||
String getMonasticCommander2();
|
||||
|
||||
@Key("MonasticCommander3")
|
||||
String getMonasticCommander3();
|
||||
|
||||
@Key("MonasticFlag1")
|
||||
String getMonasticFlag1();
|
||||
|
||||
@Key("MonasticFlag2")
|
||||
String getMonasticFlag2();
|
||||
|
||||
@Key("MonasticFlag3")
|
||||
String getMonasticFlag3();
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
import com.l2jserver.gameserver.config.converter.Minutes2MillisecondsConverter;
|
||||
|
||||
/**
|
||||
* Fortress Configuration.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/custom/game/config/fortress.properties",
|
||||
"file:./config/fortress.properties",
|
||||
"classpath:config/fortress.properties"
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 20, unit = MINUTES, type = ASYNC)
|
||||
public interface FortressConfiguration extends Reloadable {
|
||||
|
||||
@Key("FunctionFeeDay7")
|
||||
long getFunctionFeeDay7();
|
||||
|
||||
@Key("TeleportFunctionFeeLvl11")
|
||||
int getTeleportFunctionFeeLvl11();
|
||||
|
||||
@Key("TeleportFunctionFeeLvl12")
|
||||
int getTeleportFunctionFeeLvl12();
|
||||
|
||||
@Key("SupportFeeLvl15")
|
||||
int getSupportFeeLvl15();
|
||||
|
||||
@Key("SupportFeeLvl18")
|
||||
int getSupportFeeLvl18();
|
||||
|
||||
@Key("MpRegenerationFeeLvl18")
|
||||
int getMpRegenerationFeeLvl18();
|
||||
|
||||
@Key("MpRegenerationFeeLvl20")
|
||||
int getMpRegenerationFeeLvl20();
|
||||
|
||||
@Key("HpRegenerationFeeLvl25")
|
||||
int getHpRegenerationFeeLvl25();
|
||||
|
||||
@Key("HpRegenerationFeeLvl30")
|
||||
int getHpRegenerationFeeLvl30();
|
||||
|
||||
@Key("ExpRegenerationFeeLvl19")
|
||||
int getExpRegenerationFeeLvl19();
|
||||
|
||||
@Key("ExpRegenerationFeeLvl20")
|
||||
int getExpRegenerationFeeLvl20();
|
||||
|
||||
@Key("PeriodicUpdateFrequency")
|
||||
@ConverterClass(Minutes2MillisecondsConverter.class)
|
||||
long getPeriodicUpdateFrequency();
|
||||
|
||||
@Key("BloodOathCount")
|
||||
int getBloodOathCount();
|
||||
|
||||
@Key("MaxSupplyLevel")
|
||||
int getMaxSupplyLevel();
|
||||
|
||||
@Key("FeeForCastle")
|
||||
int getFeeForCastle();
|
||||
|
||||
@Key("MaxKeepTime")
|
||||
int getMaxKeepTime();
|
||||
|
||||
@Key("MpBuffFree")
|
||||
boolean mpBuffFree();
|
||||
}
|
||||
@@ -0,0 +1,650 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Mutable;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
import com.l2jserver.gameserver.config.converter.Minutes2MillisecondsConverter;
|
||||
import com.l2jserver.gameserver.config.converter.Seconds2MillisecondsConverter;
|
||||
import com.l2jserver.gameserver.config.converter.ServerListTypeConverter;
|
||||
import com.l2jserver.gameserver.enums.IllegalActionPunishmentType;
|
||||
|
||||
/**
|
||||
* General Configuration.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/custom/game/config/general.properties",
|
||||
"file:./config/general.properties",
|
||||
"classpath:config/general.properties"
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 20, unit = MINUTES, type = ASYNC)
|
||||
public interface GeneralConfiguration extends Mutable, Reloadable {
|
||||
|
||||
@Key("EverybodyHasAdminRights")
|
||||
boolean everybodyHasAdminRights();
|
||||
|
||||
@Key("ServerListBrackets")
|
||||
boolean getServerListBrackets();
|
||||
|
||||
@Key("ServerListType")
|
||||
@ConverterClass(ServerListTypeConverter.class)
|
||||
int getServerListType();
|
||||
|
||||
@Key("ServerListAge")
|
||||
int getServerListAge();
|
||||
|
||||
@Key("ServerGMOnly")
|
||||
boolean serverGMOnly();
|
||||
|
||||
@Key("GMHeroAura")
|
||||
boolean gmHeroAura();
|
||||
|
||||
@Key("GMStartupInvulnerable")
|
||||
boolean gmStartupInvulnerable();
|
||||
|
||||
@Key("GMStartupInvisible")
|
||||
boolean gmStartupInvisible();
|
||||
|
||||
@Key("GMStartupSilence")
|
||||
boolean gmStartupSilence();
|
||||
|
||||
@Key("GMStartupAutoList")
|
||||
boolean gmStartupAutoList();
|
||||
|
||||
@Key("GMStartupDietMode")
|
||||
boolean gmStartupDietMode();
|
||||
|
||||
@Key("GMItemRestriction")
|
||||
boolean gmItemRestriction();
|
||||
|
||||
@Key("GMSkillRestriction")
|
||||
boolean gmSkillRestriction();
|
||||
|
||||
@Key("GMTradeRestrictedItems")
|
||||
boolean gmTradeRestrictedItems();
|
||||
|
||||
@Key("GMRestartFighting")
|
||||
boolean gmRestartFighting();
|
||||
|
||||
@Key("GMShowAnnouncerName")
|
||||
boolean gmShowAnnouncerName();
|
||||
|
||||
// TODO(Zoey76): Implement GMShowCritAnnouncerName configuration.
|
||||
@Key("GMShowCritAnnouncerName")
|
||||
boolean gmShowCritAnnouncerName();
|
||||
|
||||
@Key("GMGiveSpecialSkills")
|
||||
boolean gmGiveSpecialSkills();
|
||||
|
||||
@Key("GMGiveSpecialAuraSkills")
|
||||
boolean gmGiveSpecialAuraSkills();
|
||||
|
||||
@Key("GameGuardEnforce")
|
||||
boolean gameGuardEnforce();
|
||||
|
||||
// TODO(Zoey76): Implement GameGuardProhibitAction configuration.
|
||||
@Key("GameGuardProhibitAction")
|
||||
boolean gameGuardProhibitAction();
|
||||
|
||||
@Key("LogChat")
|
||||
boolean logChat();
|
||||
|
||||
// TODO(Zoey76): Implement LogAutoAnnouncements configuration.
|
||||
@Key("LogAutoAnnouncements")
|
||||
boolean logAutoAnnouncements();
|
||||
|
||||
@Key("LogItems")
|
||||
boolean logItems();
|
||||
|
||||
@Key("LogItemsSmallLog")
|
||||
boolean logItemsSmallLog();
|
||||
|
||||
@Key("LogItemEnchants")
|
||||
boolean logItemEnchants();
|
||||
|
||||
@Key("LogSkillEnchants")
|
||||
boolean logSkillEnchants();
|
||||
|
||||
@Key("GMAudit")
|
||||
boolean gmAudit();
|
||||
|
||||
@Key("SkillCheckEnable")
|
||||
boolean skillCheckEnable();
|
||||
|
||||
@Key("SkillCheckRemove")
|
||||
boolean skillCheckRemove();
|
||||
|
||||
@Key("SkillCheckGM")
|
||||
boolean skillCheckGM();
|
||||
|
||||
@Key("ThreadPoolSizeEffects")
|
||||
int getThreadPoolSizeEffects();
|
||||
|
||||
@Key("ThreadPoolSizeGeneral")
|
||||
int getThreadPoolSizeGeneral();
|
||||
|
||||
@Key("ThreadPoolSizeEvents")
|
||||
int getThreadPoolSizeEvents();
|
||||
|
||||
@Key("UrgentPacketThreadCoreSize")
|
||||
int getUrgentPacketThreadCoreSize();
|
||||
|
||||
@Key("GeneralPacketThreadCoreSize")
|
||||
int getGeneralPacketThreadCoreSize();
|
||||
|
||||
@Key("GeneralThreadCoreSize")
|
||||
int getGeneralThreadCoreSize();
|
||||
|
||||
@Key("AiMaxThread")
|
||||
int getAiMaxThread();
|
||||
|
||||
@Key("EventsMaxThread")
|
||||
int getEventsMaxThread();
|
||||
|
||||
@Key("DeadLockDetector")
|
||||
boolean deadLockDetector();
|
||||
|
||||
@Key("DeadLockCheckInterval")
|
||||
@ConverterClass(Seconds2MillisecondsConverter.class)
|
||||
long getDeadLockCheckInterval();
|
||||
|
||||
@Key("RestartOnDeadlock")
|
||||
boolean restartOnDeadlock();
|
||||
|
||||
@Key("ClientPacketQueueSize")
|
||||
int getClientPacketQueueSize();
|
||||
|
||||
@Key("ClientPacketQueueMaxBurstSize")
|
||||
int getClientPacketQueueMaxBurstSize();
|
||||
|
||||
@Key("ClientPacketQueueMaxPacketsPerSecond")
|
||||
int getClientPacketQueueMaxPacketsPerSecond();
|
||||
|
||||
@Key("ClientPacketQueueMeasureInterval")
|
||||
int getClientPacketQueueMeasureInterval();
|
||||
|
||||
@Key("ClientPacketQueueMaxAveragePacketsPerSecond")
|
||||
int getClientPacketQueueMaxAveragePacketsPerSecond();
|
||||
|
||||
@Key("ClientPacketQueueMaxFloodsPerMin")
|
||||
int getClientPacketQueueMaxFloodsPerMin();
|
||||
|
||||
@Key("ClientPacketQueueMaxOverflowsPerMin")
|
||||
int getClientPacketQueueMaxOverflowsPerMin();
|
||||
|
||||
@Key("ClientPacketQueueMaxUnderflowsPerMin")
|
||||
int getClientPacketQueueMaxUnderflowsPerMin();
|
||||
|
||||
@Key("ClientPacketQueueMaxUnknownPerMin")
|
||||
int getClientPacketQueueMaxUnknownPerMin();
|
||||
|
||||
@Key("AllowDiscardItem")
|
||||
boolean allowDiscardItem();
|
||||
|
||||
@Key("AutoDestroyDroppedItemAfter")
|
||||
int getAutoDestroyDroppedItemAfter();
|
||||
|
||||
@Key("AutoDestroyHerbTime")
|
||||
@ConverterClass(Seconds2MillisecondsConverter.class)
|
||||
long getAutoDestroyHerbTime();
|
||||
|
||||
@Key("ProtectedItems")
|
||||
Set<Integer> getProtectedItems();
|
||||
|
||||
@Key("DatabaseCleanUp")
|
||||
boolean databaseCleanUp();
|
||||
|
||||
// TODO(Zoey76): Implement ConnectionCloseTime configuration.
|
||||
@Key("ConnectionCloseTime")
|
||||
long getConnectionCloseTime();
|
||||
|
||||
@Key("CharacterDataStoreInterval")
|
||||
@ConverterClass(Minutes2MillisecondsConverter.class)
|
||||
long getCharacterDataStoreInterval();
|
||||
|
||||
@Key("LazyItemsUpdate")
|
||||
boolean lazyItemsUpdate();
|
||||
|
||||
@Key("UpdateItemsOnCharStore")
|
||||
boolean updateItemsOnCharStore();
|
||||
|
||||
@Key("DestroyPlayerDroppedItem")
|
||||
boolean destroyPlayerDroppedItem();
|
||||
|
||||
@Key("DestroyEquipableItem")
|
||||
boolean destroyEquipableItem();
|
||||
|
||||
@Key("SaveDroppedItem")
|
||||
boolean saveDroppedItem();
|
||||
|
||||
@Key("EmptyDroppedItemTableAfterLoad")
|
||||
boolean emptyDroppedItemTableAfterLoad();
|
||||
|
||||
@Key("SaveDroppedItemInterval")
|
||||
@ConverterClass(Minutes2MillisecondsConverter.class)
|
||||
long getSaveDroppedItemInterval();
|
||||
|
||||
@Key("ClearDroppedItemTable")
|
||||
boolean clearDroppedItemTable();
|
||||
|
||||
@Key("AutoDeleteInvalidQuestData")
|
||||
boolean autoDeleteInvalidQuestData();
|
||||
|
||||
@Key("PreciseDropCalculation")
|
||||
boolean preciseDropCalculation();
|
||||
|
||||
@Key("PreciseDropMultipleGroupRolls")
|
||||
boolean preciseDropMultipleGroupRolls();
|
||||
|
||||
@Key("PreciseDropMultipleRollsAggregateDrops")
|
||||
boolean preciseDropMultipleRollsAggregateDrops();
|
||||
|
||||
@Key("MultipleItemDrop")
|
||||
boolean multipleItemDrop();
|
||||
|
||||
@Key("ForceInventoryUpdate")
|
||||
boolean forceInventoryUpdate();
|
||||
|
||||
@Key("LazyCache")
|
||||
boolean lazyCache();
|
||||
|
||||
@Key("CacheCharNames")
|
||||
boolean cacheCharNames();
|
||||
|
||||
@Key("MinNPCAnimation")
|
||||
int getMinNPCAnimation();
|
||||
|
||||
@Key("MaxNPCAnimation")
|
||||
int getMaxNPCAnimation();
|
||||
|
||||
@Key("MinMonsterAnimation")
|
||||
int getMinMonsterAnimation();
|
||||
|
||||
@Key("MaxMonsterAnimation")
|
||||
int getMaxMonsterAnimation();
|
||||
|
||||
@Key("MoveBasedKnownList")
|
||||
boolean moveBasedKnownList();
|
||||
|
||||
@Key("KnownListUpdateInterval")
|
||||
long getKnownListUpdateInterval();
|
||||
|
||||
@Key("CheckKnownList")
|
||||
boolean checkKnownList();
|
||||
|
||||
@Key("GridsAlwaysOn")
|
||||
boolean gridsAlwaysOn();
|
||||
|
||||
@Key("GridNeighborTurnOnTime")
|
||||
int getGridNeighborTurnOnTime();
|
||||
|
||||
@Key("GridNeighborTurnOffTime")
|
||||
int getGridNeighborTurnOffTime();
|
||||
|
||||
@Key("EnableFallingDamage")
|
||||
boolean enableFallingDamage();
|
||||
|
||||
@Key("PeaceZoneMode")
|
||||
int getPeaceZoneMode();
|
||||
|
||||
@Key("GlobalChat")
|
||||
String getGlobalChat();
|
||||
|
||||
@Key("TradeChat")
|
||||
String getTradeChat();
|
||||
|
||||
@Key("AllowWarehouse")
|
||||
boolean allowWarehouse();
|
||||
|
||||
@Key("WarehouseCache")
|
||||
boolean warehouseCache();
|
||||
|
||||
@Key("WarehouseCacheTime")
|
||||
@ConverterClass(Minutes2MillisecondsConverter.class)
|
||||
long getWarehouseCacheTime();
|
||||
|
||||
@Key("AllowRefund")
|
||||
boolean allowRefund();
|
||||
|
||||
@Key("AllowMail")
|
||||
boolean allowMail();
|
||||
|
||||
@Key("AllowAttachments")
|
||||
boolean allowAttachments();
|
||||
|
||||
@Key("AllowWear")
|
||||
boolean allowWear();
|
||||
|
||||
@Key("WearDelay")
|
||||
@ConverterClass(Seconds2MillisecondsConverter.class)
|
||||
long getWearDelay();
|
||||
|
||||
@Key("WearPrice")
|
||||
int getWearPrice();
|
||||
|
||||
@Key("RestorePlayerInstance")
|
||||
boolean restorePlayerInstance();
|
||||
|
||||
@Key("AllowSummonInInstance")
|
||||
boolean allowSummonInInstance();
|
||||
|
||||
@Key("EjectDeadPlayerTime")
|
||||
@ConverterClass(Seconds2MillisecondsConverter.class)
|
||||
long getEjectDeadPlayerTime();
|
||||
|
||||
@Key("InstanceFinishTime")
|
||||
int getInstanceFinishTime();
|
||||
|
||||
// TODO(Zoey76): Implement AllowRace configuration.
|
||||
@Key("AllowRace")
|
||||
boolean allowRace();
|
||||
|
||||
@Key("AllowWater")
|
||||
boolean allowWater();
|
||||
|
||||
@Key("AllowRentPet")
|
||||
boolean allowRentPet();
|
||||
|
||||
@Key("AllowFishing")
|
||||
boolean allowFishing();
|
||||
|
||||
@Key("AllowBoat")
|
||||
boolean allowBoat();
|
||||
|
||||
@Key("BoatBroadcastRadius")
|
||||
int getBoatBroadcastRadius();
|
||||
|
||||
@Key("AllowCursedWeapons")
|
||||
boolean allowCursedWeapons();
|
||||
|
||||
@Key("AllowPetWalkers")
|
||||
boolean allowPetWalkers();
|
||||
|
||||
@Key("ShowServerNews")
|
||||
boolean showServerNews();
|
||||
|
||||
@Key("EnableCommunityBoard")
|
||||
boolean enableCommunityBoard();
|
||||
|
||||
@Key("BBSDefault")
|
||||
String getBBSDefault();
|
||||
|
||||
@Key("UseChatFilter")
|
||||
boolean useChatFilter();
|
||||
|
||||
@Key("ChatFilterChars")
|
||||
String getChatFilterChars();
|
||||
|
||||
@Key("ChatFilter")
|
||||
Set<String> getChatFilter();
|
||||
|
||||
@Key("BanChatChannels")
|
||||
Set<Integer> getBanChatChannels();
|
||||
|
||||
@Key("AllowManor")
|
||||
boolean allowManor();
|
||||
|
||||
@Key("ManorRefreshTime")
|
||||
int getManorRefreshTime();
|
||||
|
||||
@Key("ManorRefreshMin")
|
||||
int getManorRefreshMin();
|
||||
|
||||
@Key("ManorApproveTime")
|
||||
int getManorApproveTime();
|
||||
|
||||
@Key("ManorApproveMin")
|
||||
int getManorApproveMin();
|
||||
|
||||
@Key("ManorMaintenanceMin")
|
||||
int getManorMaintenanceMin();
|
||||
|
||||
@Key("ManorSaveAllActions")
|
||||
boolean manorSaveAllActions();
|
||||
|
||||
@Key("ManorSavePeriodRate")
|
||||
int getManorSavePeriodRate();
|
||||
|
||||
@Key("AllowLottery")
|
||||
boolean allowLottery();
|
||||
|
||||
@Key("LotteryPrize")
|
||||
long getLotteryPrize();
|
||||
|
||||
@Key("LotteryTicketPrice")
|
||||
long getLotteryTicketPrice();
|
||||
|
||||
@Key("Lottery5NumberRate")
|
||||
float getLottery5NumberRate();
|
||||
|
||||
@Key("Lottery4NumberRate")
|
||||
float getLottery4NumberRate();
|
||||
|
||||
@Key("Lottery3NumberRate")
|
||||
float getLottery3NumberRate();
|
||||
|
||||
@Key("Lottery2and1NumberPrize")
|
||||
long getLottery2and1NumberPrize();
|
||||
|
||||
@Key("ItemAuctionEnabled")
|
||||
boolean itemAuctionEnabled();
|
||||
|
||||
@Key("ItemAuctionExpiredAfter")
|
||||
int getItemAuctionExpiredAfter();
|
||||
|
||||
@Key("ItemAuctionTimeExtendsOnBid")
|
||||
@ConverterClass(Seconds2MillisecondsConverter.class)
|
||||
long getItemAuctionTimeExtendsOnBid();
|
||||
|
||||
// TODO(Zoey76): Implement RiftMinPartySize configuration.
|
||||
@Key("RiftMinPartySize")
|
||||
int getRiftMinPartySize();
|
||||
|
||||
@Key("MaxRiftJumps")
|
||||
int getMaxRiftJumps();
|
||||
|
||||
@Key("RiftSpawnDelay")
|
||||
int getRiftSpawnDelay();
|
||||
|
||||
@Key("AutoJumpsDelayMin")
|
||||
@ConverterClass(Seconds2MillisecondsConverter.class)
|
||||
long getAutoJumpsDelayMin();
|
||||
|
||||
@Key("AutoJumpsDelayMax")
|
||||
@ConverterClass(Seconds2MillisecondsConverter.class)
|
||||
long getAutoJumpsDelayMax();
|
||||
|
||||
@Key("BossRoomTimeMultiply")
|
||||
float getBossRoomTimeMultiply();
|
||||
|
||||
// TODO(Zoey76): Implement RecruitCost configuration.
|
||||
@Key("RecruitCost")
|
||||
int getRecruitCost();
|
||||
|
||||
// TODO(Zoey76): Implement SoldierCost configuration.
|
||||
@Key("SoldierCost")
|
||||
int getSoldierCost();
|
||||
|
||||
// TODO(Zoey76): Implement OfficerCost configuration.
|
||||
@Key("OfficerCost")
|
||||
int getOfficerCost();
|
||||
|
||||
// TODO(Zoey76): Implement CaptainCost configuration.
|
||||
@Key("CaptainCost")
|
||||
int getCaptainCost();
|
||||
|
||||
// TODO(Zoey76): Implement CommanderCost configuration.
|
||||
@Key("CommanderCost")
|
||||
int getCommanderCost();
|
||||
|
||||
// TODO(Zoey76): Implement HeroCost configuration.
|
||||
@Key("HeroCost")
|
||||
int getHeroCost();
|
||||
|
||||
@Key("TimeOfAttack")
|
||||
@ConverterClass(Minutes2MillisecondsConverter.class)
|
||||
long getTimeOfAttack();
|
||||
|
||||
// TODO(Zoey76): Implement TimeOfCoolDown configuration.
|
||||
@Key("TimeOfCoolDown")
|
||||
@ConverterClass(Minutes2MillisecondsConverter.class)
|
||||
long getTimeOfCoolDown();
|
||||
|
||||
@Key("TimeOfEntry")
|
||||
@ConverterClass(Minutes2MillisecondsConverter.class)
|
||||
long getTimeOfEntry();
|
||||
|
||||
@Key("TimeOfWarmUp")
|
||||
@ConverterClass(Minutes2MillisecondsConverter.class)
|
||||
long getTimeOfWarmUp();
|
||||
|
||||
// TODO(Zoey76): Move this four sepulchers to own configuration file.
|
||||
@Key("NumberOfNecessaryPartyMembers")
|
||||
int getNumberOfNecessaryPartyMembers();
|
||||
|
||||
@Key("DefaultPunish")
|
||||
IllegalActionPunishmentType getDefaultPunish();
|
||||
|
||||
@Key("DefaultPunishParam")
|
||||
@ConverterClass(Seconds2MillisecondsConverter.class)
|
||||
long getDefaultPunishParam();
|
||||
|
||||
@Key("OnlyGMItemsFree")
|
||||
boolean onlyGMItemsFree();
|
||||
|
||||
@Key("JailIsPvp")
|
||||
boolean jailIsPvp();
|
||||
|
||||
@Key("JailDisableChat")
|
||||
boolean jailDisableChat();
|
||||
|
||||
@Key("JailDisableTransaction")
|
||||
boolean jailDisableTransaction();
|
||||
|
||||
@Key("NormalEnchantCostMultipiler")
|
||||
int getNormalEnchantCostMultipiler();
|
||||
|
||||
@Key("SafeEnchantCostMultipiler")
|
||||
int getSafeEnchantCostMultipiler();
|
||||
|
||||
@Key("CustomSpawnlistTable")
|
||||
boolean customSpawnlistTable();
|
||||
|
||||
@Key("SaveGmSpawnOnCustom")
|
||||
boolean saveGmSpawnOnCustom();
|
||||
|
||||
@Key("CustomNpcData")
|
||||
boolean customNpcData();
|
||||
|
||||
@Key("CustomTeleportTable")
|
||||
boolean customTeleportTable();
|
||||
|
||||
@Key("CustomNpcBufferTables")
|
||||
boolean customNpcBufferTables();
|
||||
|
||||
@Key("CustomSkillsLoad")
|
||||
boolean customSkillsLoad();
|
||||
|
||||
@Key("CustomItemsLoad")
|
||||
boolean customItemsLoad();
|
||||
|
||||
@Key("CustomMultisellLoad")
|
||||
boolean customMultisellLoad();
|
||||
|
||||
@Key("CustomBuyListLoad")
|
||||
boolean customBuyListLoad();
|
||||
|
||||
@Key("BirthdayGift")
|
||||
int getBirthdayGift();
|
||||
|
||||
@Key("BirthdayMailSubject")
|
||||
String getBirthdayMailSubject();
|
||||
|
||||
@Key("BirthdayMailText")
|
||||
String getBirthdayMailText();
|
||||
|
||||
@Key("EnableBlockCheckerEvent")
|
||||
boolean enableBlockCheckerEvent();
|
||||
|
||||
@Key("BlockCheckerMinTeamMembers")
|
||||
int getBlockCheckerMinTeamMembers();
|
||||
|
||||
@Key("HBCEFairPlay")
|
||||
boolean isHBCEFairPlay();
|
||||
|
||||
@Key("HellboundWithoutQuest")
|
||||
boolean hellboundWithoutQuest();
|
||||
|
||||
@Key("EnableBotReportButton")
|
||||
boolean enableBotReportButton();
|
||||
|
||||
@Key("BotReportPointsResetHour")
|
||||
String getBotReportPointsResetHour();
|
||||
|
||||
@Key("BotReportDelay")
|
||||
@ConverterClass(Minutes2MillisecondsConverter.class)
|
||||
long getBotReportDelay();
|
||||
|
||||
@Key("AllowReportsFromSameClanMembers")
|
||||
boolean allowReportsFromSameClanMembers();
|
||||
|
||||
@Key("Debug")
|
||||
boolean debug();
|
||||
|
||||
@Key("InstanceDebug")
|
||||
boolean instanceDebug();
|
||||
|
||||
@Key("HtmlActionCacheDebug")
|
||||
boolean htmlActionCacheDebug();
|
||||
|
||||
@Key("PacketHandlerDebug")
|
||||
boolean packetHandlerDebug();
|
||||
|
||||
@Key("Developer")
|
||||
boolean developer();
|
||||
|
||||
@Key("NoHandlers")
|
||||
boolean noHandlers();
|
||||
|
||||
@Key("NoQuests")
|
||||
boolean noQuests();
|
||||
|
||||
@Key("NoSpawns")
|
||||
boolean noSpawns();
|
||||
|
||||
@Key("ShowQuestsLoadInLogs")
|
||||
boolean showQuestsLoadInLogs();
|
||||
|
||||
// TODO(Zoey76): Implement ShowScriptsLoadInLogs configuration.
|
||||
@Key("ShowScriptsLoadInLogs")
|
||||
boolean showScriptsLoadInLogs();
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Mutable;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
/**
|
||||
* Geodata Configuration.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/custom/game/config/geodata.properties",
|
||||
"file:./config/geodata.properties",
|
||||
"classpath:config/geodata.properties"
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 20, unit = MINUTES, type = ASYNC)
|
||||
public interface GeodataConfiguration extends Mutable, Reloadable {
|
||||
|
||||
/**
|
||||
* Pathfinding options:
|
||||
* <ul>
|
||||
* <li>0 = Disabled</li>
|
||||
* <li>1 = Enabled using path node files</li>
|
||||
* <li>2 = Enabled using geodata cells at runtime</li>
|
||||
* </ul>
|
||||
* @return
|
||||
*/
|
||||
@Key("PathFinding")
|
||||
int getPathFinding();
|
||||
|
||||
@Key("PathnodePath")
|
||||
File getPathnodePath();
|
||||
|
||||
@Key("PathFindBuffers")
|
||||
String getPathFindBuffers();
|
||||
|
||||
@Key("LowWeight")
|
||||
float getLowWeight();
|
||||
|
||||
@Key("MediumWeight")
|
||||
float getMediumWeight();
|
||||
|
||||
@Key("HighWeight")
|
||||
float getHighWeight();
|
||||
|
||||
@Key("AdvancedDiagonalStrategy")
|
||||
boolean advancedDiagonalStrategy();
|
||||
|
||||
@Key("DiagonalWeight")
|
||||
float getDiagonalWeight();
|
||||
|
||||
@Key("MaxPostfilterPasses")
|
||||
int getMaxPostfilterPasses();
|
||||
|
||||
@Key("DebugPath")
|
||||
boolean debugPath();
|
||||
|
||||
@Key("ForceGeoData")
|
||||
boolean forceGeoData();
|
||||
|
||||
@Key("CoordSynchronize")
|
||||
int getCoordSynchronize();
|
||||
|
||||
@Key("GeoDataPath")
|
||||
File getGeoDataPath();
|
||||
|
||||
@Key("TryLoadUnspecifiedRegions")
|
||||
boolean tryLoadUnspecifiedRegions();
|
||||
|
||||
@Separator(";")
|
||||
@Key("IncludedRegions")
|
||||
List<String> getIncludedRegions();
|
||||
|
||||
@Separator(";")
|
||||
@Key("ExcludedRegions")
|
||||
List<String> getExcludedRegions();
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
import com.l2jserver.gameserver.config.converter.Minutes2MillisecondsConverter;
|
||||
|
||||
/**
|
||||
* Gracia Seeds Configuration.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/custom/game/config/graciaseeds.properties",
|
||||
"file:./config/graciaseeds.properties",
|
||||
"classpath:config/graciaseeds.properties"
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 20, unit = MINUTES, type = ASYNC)
|
||||
public interface GraciaSeedsConfiguration extends Reloadable {
|
||||
|
||||
@Key("TiatKillCountForNextState")
|
||||
int getTiatKillCountForNextState();
|
||||
|
||||
@Key("Stage2Length")
|
||||
@ConverterClass(Minutes2MillisecondsConverter.class)
|
||||
long getStage2Length();
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
import com.l2jserver.gameserver.config.converter.Hours2MillisecondsConverter;
|
||||
import com.l2jserver.gameserver.config.converter.Minutes2MillisecondsConverter;
|
||||
|
||||
/**
|
||||
* Grand Boss Configuration.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/custom/game/config/grandboss.properties",
|
||||
"file:./config/grandboss.properties",
|
||||
"classpath:config/grandboss.properties"
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 20, unit = MINUTES, type = ASYNC)
|
||||
public interface GrandBossConfiguration extends Reloadable {
|
||||
|
||||
@Key("AntharasWaitTime")
|
||||
@ConverterClass(Minutes2MillisecondsConverter.class)
|
||||
long getAntharasWaitTime();
|
||||
|
||||
@Key("IntervalOfAntharasSpawn")
|
||||
@ConverterClass(Hours2MillisecondsConverter.class)
|
||||
long getIntervalOfAntharasSpawn();
|
||||
|
||||
@Key("RandomOfAntharasSpawn")
|
||||
@ConverterClass(Hours2MillisecondsConverter.class)
|
||||
long getRandomOfAntharasSpawn();
|
||||
|
||||
@Key("ValakasWaitTime")
|
||||
@ConverterClass(Minutes2MillisecondsConverter.class)
|
||||
long getValakasWaitTime();
|
||||
|
||||
@Key("IntervalOfValakasSpawn")
|
||||
@ConverterClass(Hours2MillisecondsConverter.class)
|
||||
long getIntervalOfValakasSpawn();
|
||||
|
||||
@Key("RandomOfValakasSpawn")
|
||||
@ConverterClass(Hours2MillisecondsConverter.class)
|
||||
long getRandomOfValakasSpawn();
|
||||
|
||||
@Key("IntervalOfBaiumSpawn")
|
||||
@ConverterClass(Hours2MillisecondsConverter.class)
|
||||
long getIntervalOfBaiumSpawn();
|
||||
|
||||
@Key("RandomOfBaiumSpawn")
|
||||
@ConverterClass(Hours2MillisecondsConverter.class)
|
||||
long getRandomOfBaiumSpawn();
|
||||
|
||||
@Key("IntervalOfCoreSpawn")
|
||||
@ConverterClass(Hours2MillisecondsConverter.class)
|
||||
long getIntervalOfCoreSpawn();
|
||||
|
||||
@Key("RandomOfCoreSpawn")
|
||||
@ConverterClass(Hours2MillisecondsConverter.class)
|
||||
long getRandomOfCoreSpawn();
|
||||
|
||||
@Key("IntervalOfOrfenSpawn")
|
||||
@ConverterClass(Hours2MillisecondsConverter.class)
|
||||
long getIntervalOfOrfenSpawn();
|
||||
|
||||
@Key("RandomOfOrfenSpawn")
|
||||
@ConverterClass(Hours2MillisecondsConverter.class)
|
||||
long getRandomOfOrfenSpawn();
|
||||
|
||||
@Key("IntervalOfQueenAntSpawn")
|
||||
@ConverterClass(Hours2MillisecondsConverter.class)
|
||||
long getIntervalOfQueenAntSpawn();
|
||||
|
||||
@Key("RandomOfQueenAntSpawn")
|
||||
@ConverterClass(Hours2MillisecondsConverter.class)
|
||||
long getRandomOfQueenAntSpawn();
|
||||
|
||||
@Key("IntervalOfBelethSpawn")
|
||||
@ConverterClass(Hours2MillisecondsConverter.class)
|
||||
long getIntervalOfBelethSpawn();
|
||||
|
||||
@Key("RandomOfBelethSpawn")
|
||||
@ConverterClass(Hours2MillisecondsConverter.class)
|
||||
long getRandomOfBelethSpawn();
|
||||
|
||||
@Key("BelethMinPlayers")
|
||||
int getBelethMinPlayers();
|
||||
|
||||
// TODO(Zoey76): Implement AllowZakenWithoutParty configuration.
|
||||
@Key("AllowZakenWithoutParty")
|
||||
boolean getAllowZakenWithoutParty();
|
||||
|
||||
// TODO(Zoey76): Implement ZakenMinPlayers configuration.
|
||||
@Key("ZakenMinPlayers")
|
||||
List<Integer> getZakenMinPlayers();
|
||||
|
||||
// TODO(Zoey76): Implement ZakenMaxPlayers configuration.
|
||||
@Key("ZakenMaxPlayers")
|
||||
List<Integer> getZakenMaxPlayers();
|
||||
|
||||
// TODO(Zoey76): Implement ZakenMinPlayerLevel configuration.
|
||||
@Key("ZakenMinPlayerLevel")
|
||||
List<Integer> getZakenMinPlayerLevel();
|
||||
|
||||
// TODO(Zoey76): Implement ZakenCanBeRevealedByAOESpells configuration.
|
||||
@Key("ZakenCanBeRevealedByAOESpells")
|
||||
boolean getZakenCanBeRevealedByAoeSPells();
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import org.aeonbits.owner.Accessible;
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Mutable;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
import com.l2jserver.gameserver.config.converter.HexIdConverter;
|
||||
|
||||
/**
|
||||
* Hex Id Configuration.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/" + Configuration.CUSTOM_SUBPATH + HexIdConfiguration.FILENAME,
|
||||
"file:" + Configuration.DEFAULT_PATH + HexIdConfiguration.FILENAME,
|
||||
"classpath:" + Configuration.DEFAULT_PATH + HexIdConfiguration.FILENAME
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 20, unit = MINUTES, type = ASYNC)
|
||||
public interface HexIdConfiguration extends Mutable, Reloadable, Accessible {
|
||||
String FILENAME = "hexid.txt";
|
||||
|
||||
String SERVERID_KEY = "ServerID";
|
||||
String HEXID_KEY = "HexID";
|
||||
|
||||
@Key(SERVERID_KEY)
|
||||
int getServerID();
|
||||
|
||||
@Key(HEXID_KEY)
|
||||
@ConverterClass(HexIdConverter.class)
|
||||
BigInteger getHexID();
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
/**
|
||||
* Hunting System Configuration.
|
||||
* @author Maneco2
|
||||
* @version 2.6.3.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/custom/game/config/huntingsystem.properties",
|
||||
"file:./config/huntingsystem.properties",
|
||||
"classpath:config/huntingsystem.properties"
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 20, unit = MINUTES, type = ASYNC)
|
||||
public interface HuntingSystemConfiguration extends Reloadable {
|
||||
@Key("NevitEnable")
|
||||
boolean getNevitEnable();
|
||||
|
||||
@Key("NevitBlessingMaxPoints")
|
||||
int getNevitBlessingMaxPoints();
|
||||
|
||||
@Key("NevitBonusMaxTime")
|
||||
int getHuntingBonusMaxTime();
|
||||
|
||||
@Key("NevitRegularPoints")
|
||||
int getNevitRegularPoints();
|
||||
|
||||
@Key("NevitRegularPoints2")
|
||||
int getNevitRegularPoints2();
|
||||
|
||||
@Key("NevitNormalPoints")
|
||||
int getNevitNormalPoints();
|
||||
|
||||
@Key("NevitLevelAcquirePoints")
|
||||
int getNevitLevelAcquirePoints();
|
||||
|
||||
@Key("NevitLevelAcquirePoints2")
|
||||
int getNevitLevelAcquirePoints2();
|
||||
|
||||
@Key("NevitDecreaseVitalityAcquirePoints")
|
||||
int getNevitDecreaseVitalityAcquirePoints();
|
||||
|
||||
@Key("NevitBonusEffectTime")
|
||||
int getNevitBlessingEffetcTime();
|
||||
|
||||
@Key("HuntingSystemLimit")
|
||||
boolean getHuntingBonusLimit();
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static com.l2jserver.gameserver.GameServer.startTimedSection;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InterfaceAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
import com.l2jserver.gameserver.util.IXmlReader;
|
||||
|
||||
/**
|
||||
* IP Config Data.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
public class IPConfigData implements IXmlReader {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(IPConfigData.class);
|
||||
|
||||
private static final String IP_CONFIG_FILE = "./config/ipconfig.xml";
|
||||
|
||||
private final List<String> _subnets = new ArrayList<>(5);
|
||||
|
||||
private final List<String> _hosts = new ArrayList<>(5);
|
||||
|
||||
public IPConfigData() {
|
||||
load();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
startTimedSection("Network Configuration");
|
||||
final File f = new File(IP_CONFIG_FILE);
|
||||
if (f.exists()) {
|
||||
LOG.info("Using existing ipconfig.xml.");
|
||||
parseFile(new File(IP_CONFIG_FILE));
|
||||
} else {
|
||||
LOG.info("Using automatic network configuration.");
|
||||
autoIpConfig();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseDocument(Document doc) {
|
||||
for (var n = doc.getFirstChild(); n != null; n = n.getNextSibling()) {
|
||||
if ("gameserver".equalsIgnoreCase(n.getNodeName())) {
|
||||
for (var d = n.getFirstChild(); d != null; d = d.getNextSibling()) {
|
||||
if ("define".equalsIgnoreCase(d.getNodeName())) {
|
||||
final var attrs = d.getAttributes();
|
||||
_subnets.add(attrs.getNamedItem("subnet").getNodeValue());
|
||||
_hosts.add(attrs.getNamedItem("address").getNodeValue());
|
||||
|
||||
if (_hosts.size() != _subnets.size()) {
|
||||
LOG.warn("Failed to load {} file - subnets does not match server addresses.", IP_CONFIG_FILE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final var att = n.getAttributes().getNamedItem("address");
|
||||
if (att == null) {
|
||||
LOG.warn("Failed to load {} file - default server address is missing.", IP_CONFIG_FILE);
|
||||
_hosts.add("127.0.0.1");
|
||||
} else {
|
||||
_hosts.add(att.getNodeValue());
|
||||
}
|
||||
_subnets.add("0.0.0.0/0");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void autoIpConfig() {
|
||||
String externalIp;
|
||||
try {
|
||||
final var autoIp = URI.create("http://ip1.dynupdate.no-ip.com:8245/").toURL();
|
||||
try (var in = new BufferedReader(new InputStreamReader(autoIp.openStream()))) {
|
||||
externalIp = in.readLine();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
LOG.warn("Failed to connect to ip1.dynupdate.no-ip.com:8245 please check your internet connection using 127.0.0.1!");
|
||||
externalIp = "127.0.0.1";
|
||||
}
|
||||
|
||||
try {
|
||||
final var niList = NetworkInterface.getNetworkInterfaces();
|
||||
while (niList.hasMoreElements()) {
|
||||
final var ni = niList.nextElement();
|
||||
if (!ni.isUp() || ni.isVirtual()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ni.isLoopback() && ((ni.getHardwareAddress() == null) || (ni.getHardwareAddress().length != 6))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (InterfaceAddress ia : ni.getInterfaceAddresses()) {
|
||||
if (ia.getAddress() instanceof Inet6Address) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final String hostAddress = ia.getAddress().getHostAddress();
|
||||
final int subnetPrefixLength = ia.getNetworkPrefixLength();
|
||||
final int subnetMaskInt = subnetPrefixLength == 0 ? 0 : -1 << (32 - subnetPrefixLength);
|
||||
final int hostAddressInt = Arrays.stream(hostAddress.split("\\.")).mapToInt(Integer::parseInt).reduce((r, e) -> (r << 8) + e).orElse(0);
|
||||
final int subnetAddressInt = hostAddressInt & subnetMaskInt;
|
||||
final String subnetAddress = ((subnetAddressInt >> 24) & 0xFF) + "." + ((subnetAddressInt >> 16) & 0xFF) + "." + ((subnetAddressInt >> 8) & 0xFF) + "." + (subnetAddressInt & 0xFF);
|
||||
final String subnet = subnetAddress + '/' + subnetPrefixLength;
|
||||
if (!_subnets.contains(subnet) && !subnet.equals("0.0.0.0/0")) {
|
||||
_subnets.add(subnet);
|
||||
_hosts.add(hostAddress);
|
||||
LOG.info("Adding new subnet: " + subnet + " address: " + hostAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// External host and subnet
|
||||
_hosts.add(externalIp);
|
||||
_subnets.add("0.0.0.0/0");
|
||||
LOG.info("Adding new subnet: 0.0.0.0/0 address: {}", externalIp);
|
||||
} catch (SocketException e) {
|
||||
LOG.error("Configuration failed please manually configure ipconfig.xml", e);
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getSubnets() {
|
||||
if (_subnets.isEmpty()) {
|
||||
return List.of("0.0.0.0/0");
|
||||
}
|
||||
return _subnets;
|
||||
}
|
||||
|
||||
public List<String> getHosts() {
|
||||
if (_hosts.isEmpty()) {
|
||||
return List.of("127.0.0.1");
|
||||
}
|
||||
return _hosts;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
/**
|
||||
* MMO Configuration.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/custom/game/config/mmo.properties",
|
||||
"file:./config/mmo.properties",
|
||||
"classpath:config/mmo.properties"
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 20, unit = MINUTES, type = ASYNC)
|
||||
public interface MMOConfiguration extends Reloadable {
|
||||
|
||||
@Key("SleepTime")
|
||||
int getSleepTime();
|
||||
|
||||
@Key("MaxSendPerPass")
|
||||
int getMaxSendPerPass();
|
||||
|
||||
@Key("MaxReadPerPass")
|
||||
int getMaxReadPerPass();
|
||||
|
||||
@Key("HelperBufferCount")
|
||||
int getHelperBufferCount();
|
||||
|
||||
@Key("TcpNoDelay")
|
||||
boolean isTcpNoDelay();
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
import com.l2jserver.gameserver.config.converter.MapIntegerIntegerConverter;
|
||||
|
||||
/**
|
||||
* NPC Configuration.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/custom/game/config/npc.properties",
|
||||
"file:./config/npc.properties",
|
||||
"classpath:config/npc.properties"
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 20, unit = MINUTES, type = ASYNC)
|
||||
public interface NPCConfiguration extends Reloadable {
|
||||
|
||||
@Key("AnnounceMammonSpawn")
|
||||
boolean announceMammonSpawn();
|
||||
|
||||
@Key("MobAggroInPeaceZone")
|
||||
boolean mobAggroInPeaceZone();
|
||||
|
||||
@Key("AttackableNpcs")
|
||||
boolean attackableNpcs();
|
||||
|
||||
@Key("ViewNpc")
|
||||
boolean viewNpc();
|
||||
|
||||
@Key("MaxDriftRange")
|
||||
int getMaxDriftRange();
|
||||
|
||||
@Key("ShowNpcLevel")
|
||||
boolean showNpcLevel();
|
||||
|
||||
@Key("ShowCrestWithoutQuest")
|
||||
boolean showCrestWithoutQuest();
|
||||
|
||||
@Key("RandomEnchantEffect")
|
||||
boolean randomEnchantEffect();
|
||||
|
||||
@Key("MinNPCLevelForDmgPenalty")
|
||||
int getMinNPCLevelForDmgPenalty();
|
||||
|
||||
@Key("DmgPenaltyForLvLDifferences")
|
||||
List<Double> getDmgPenaltyForLvLDifferences();
|
||||
|
||||
@Key("CritDmgPenaltyForLvLDifferences")
|
||||
List<Double> getCritDmgPenaltyForLvLDifferences();
|
||||
|
||||
@Key("SkillDmgPenaltyForLvLDifferences")
|
||||
List<Double> getSkillDmgPenaltyForLvLDifferences();
|
||||
|
||||
// TODO(Zoey76): Implement MinNPCLevelForMagicPenalty configuration.
|
||||
@Key("MinNPCLevelForMagicPenalty")
|
||||
int getMinNPCLevelForMagicPenalty();
|
||||
|
||||
@Key("SkillChancePenaltyForLvLDifferences")
|
||||
List<Double> getSkillChancePenaltyForLvLDifferences();
|
||||
|
||||
// Monsters
|
||||
|
||||
// TODO(Zoey76): Implement DecayTimeTask configuration.
|
||||
@Key("DecayTimeTask")
|
||||
int getDecayTimeTask();
|
||||
|
||||
@Key("DefaultCorpseTime")
|
||||
int getDefaultCorpseTime();
|
||||
|
||||
@Key("SpoiledCorpseExtendTime")
|
||||
int getSpoiledCorpseExtendTime();
|
||||
|
||||
@Key("CorpseConsumeSkillAllowedTimeBeforeDecay")
|
||||
int getCorpseConsumeSkillAllowedTimeBeforeDecay();
|
||||
|
||||
// Guards
|
||||
|
||||
@Key("GuardAttackAggroMob")
|
||||
boolean guardAttackAggroMob();
|
||||
|
||||
// Pets
|
||||
|
||||
// TODO(Zoey76): Implement AllowWyvernUpgrader configuration.
|
||||
@Key("AllowWyvernUpgrader")
|
||||
int allowWyvernUpgrader();
|
||||
|
||||
@Key("PetRentNPCs")
|
||||
Set<Integer> getPetRentNPCs();
|
||||
|
||||
@Key("MaximumSlotsForPet")
|
||||
int getMaximumSlotsForPet();
|
||||
|
||||
@Key("PetHpRegenMultiplier")
|
||||
double getPetHpRegenMultiplier();
|
||||
|
||||
@Key("PetMpRegenMultiplier")
|
||||
double getPetMpRegenMultiplier();
|
||||
|
||||
// Raid Bosses
|
||||
|
||||
@Key("RaidHpRegenMultiplier")
|
||||
double getRaidHpRegenMultiplier();
|
||||
|
||||
@Key("RaidMpRegenMultiplier")
|
||||
double getRaidMpRegenMultiplier();
|
||||
|
||||
@Key("RaidPDefenceMultiplier")
|
||||
double getRaidPDefenceMultiplier();
|
||||
|
||||
@Key("RaidMDefenceMultiplier")
|
||||
double getRaidMDefenceMultiplier();
|
||||
|
||||
@Key("RaidPAttackMultiplier")
|
||||
double getRaidPAttackMultiplier();
|
||||
|
||||
@Key("RaidMAttackMultiplier")
|
||||
double getRaidMAttackMultiplier();
|
||||
|
||||
@Key("RaidMinRespawnMultiplier")
|
||||
double getRaidMinRespawnMultiplier();
|
||||
|
||||
@Key("RaidMaxRespawnMultiplier")
|
||||
double getRaidMaxRespawnMultiplier();
|
||||
|
||||
@Key("RaidMinionRespawnTime")
|
||||
long getRaidMinionRespawnTime();
|
||||
|
||||
@Key("CustomMinionsRespawnTime")
|
||||
@ConverterClass(MapIntegerIntegerConverter.class)
|
||||
Map<Integer, Integer> getCustomMinionsRespawnTime();
|
||||
|
||||
@Key("RaidCurse")
|
||||
boolean raidCurse();
|
||||
|
||||
@Key("RaidChaosTime")
|
||||
int getRaidChaosTime();
|
||||
|
||||
@Key("GrandChaosTime")
|
||||
int getGrandChaosTime();
|
||||
|
||||
@Key("MinionChaosTime")
|
||||
int getMinionChaosTime();
|
||||
|
||||
// Drops
|
||||
|
||||
@Key("UseDeepBlueDropRules")
|
||||
boolean useDeepBlueDropRules();
|
||||
|
||||
@Key("UseDeepBlueDropRulesRaid")
|
||||
boolean useDeepBlueDropRulesRaid();
|
||||
|
||||
@Key("DropAdenaMinLevelDifference")
|
||||
int getDropAdenaMinLevelDifference();
|
||||
|
||||
@Key("DropAdenaMaxLevelDifference")
|
||||
int getDropAdenaMaxLevelDifference();
|
||||
|
||||
@Key("DropAdenaMinLevelGapChance")
|
||||
int getDropAdenaMinLevelGapChance();
|
||||
|
||||
@Key("DropItemMinLevelDifference")
|
||||
int getDropItemMinLevelDifference();
|
||||
|
||||
@Key("DropItemMaxLevelDifference")
|
||||
int getDropItemMaxLevelDifference();
|
||||
|
||||
@Key("DropItemMinLevelGapChance")
|
||||
int getDropItemMinLevelGapChance();
|
||||
|
||||
@Key("MaxAggroRange")
|
||||
int getMaxAggroRange();
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
import com.l2jserver.gameserver.config.converter.ItemHolderConverter;
|
||||
import com.l2jserver.gameserver.model.holders.ItemHolder;
|
||||
|
||||
/**
|
||||
* Olympiad Configuration.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/custom/game/config/olympiad.properties",
|
||||
"file:./config/olympiad.properties",
|
||||
"classpath:config/olympiad.properties"
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 20, unit = MINUTES, type = ASYNC)
|
||||
public interface OlympiadConfiguration extends Reloadable {
|
||||
|
||||
@Key("StartHour")
|
||||
int getStartHour();
|
||||
|
||||
@Key("StartMinute")
|
||||
int getStartMinute();
|
||||
|
||||
@Key("MaxBuffs")
|
||||
int getMaxBuffs();
|
||||
|
||||
// TODO(Zoey76): Check if this should be in minutes or other time unit.
|
||||
@Key("CompetitionPeriod")
|
||||
int getCompetitionPeriod();
|
||||
|
||||
@Key("BattlePeriod")
|
||||
int getBattlePeriod();
|
||||
|
||||
// TODO(Zoey76): Check if this should be in minutes or other time unit.
|
||||
@Key("WeeklyPeriod")
|
||||
int getWeeklyPeriod();
|
||||
|
||||
// TODO(Zoey76): Check if this should be in minutes or other time unit.
|
||||
@Key("ValidationPeriod")
|
||||
int getValidationPeriod();
|
||||
|
||||
@Key("StartPoints")
|
||||
int getStartPoints();
|
||||
|
||||
@Key("WeeklyPoints")
|
||||
int getWeeklyPoints();
|
||||
|
||||
@Key("ClassedParticipants")
|
||||
int getClassedParticipants();
|
||||
|
||||
@Key("NonClassedParticipants")
|
||||
int getNonClassedParticipants();
|
||||
|
||||
@Key("TeamsParticipants")
|
||||
int getTeamsParticipants();
|
||||
|
||||
@Key("RegistrationDisplayNumber")
|
||||
int getRegistrationDisplayNumber();
|
||||
|
||||
@Separator(";")
|
||||
@Key("ClassedReward")
|
||||
@ConverterClass(ItemHolderConverter.class)
|
||||
List<ItemHolder> getClassedReward();
|
||||
|
||||
@Separator(";")
|
||||
@Key("NonClassedReward")
|
||||
@ConverterClass(ItemHolderConverter.class)
|
||||
List<ItemHolder> getNonClassedReward();
|
||||
|
||||
@Separator(";")
|
||||
@Key("TeamReward")
|
||||
@ConverterClass(ItemHolderConverter.class)
|
||||
List<ItemHolder> getTeamReward();
|
||||
|
||||
@Key("CompetitionRewardItem")
|
||||
int getCompetitionRewardItem();
|
||||
|
||||
@Key("MinMatchesForPoints")
|
||||
int getMinMatchesForPoints();
|
||||
|
||||
@Key("GPPerPoint")
|
||||
int getGPPerPoint();
|
||||
|
||||
@Key("HeroPoints")
|
||||
int getHeroPoints();
|
||||
|
||||
@Key("Rank1Points")
|
||||
int getRank1Points();
|
||||
|
||||
@Key("Rank2Points")
|
||||
int getRank2Points();
|
||||
|
||||
@Key("Rank3Points")
|
||||
int getRank3Points();
|
||||
|
||||
@Key("Rank4Points")
|
||||
int getRank4Points();
|
||||
|
||||
@Key("Rank5Points")
|
||||
int getRank5Points();
|
||||
|
||||
@Key("MaxPoints")
|
||||
int getMaxPoints();
|
||||
|
||||
@Key("ShowMonthlyWinners")
|
||||
boolean showMonthlyWinners();
|
||||
|
||||
@Key("AnnounceGames")
|
||||
boolean announceGames();
|
||||
|
||||
@Key("RestrictedItems")
|
||||
Set<Integer> getRestrictedItems();
|
||||
|
||||
@Key("EnchantLimit")
|
||||
int getEnchantLimit();
|
||||
|
||||
@Key("LogFights")
|
||||
boolean logFights();
|
||||
|
||||
@Key("WaitTime")
|
||||
int getWaitTime();
|
||||
|
||||
@Key("DividerClassed")
|
||||
int getDividerClassed();
|
||||
|
||||
@Key("DividerNonClassed")
|
||||
int getDividerNonClassed();
|
||||
|
||||
@Key("MaxWeeklyMatches")
|
||||
int getMaxWeeklyMatches();
|
||||
|
||||
@Key("MaxWeeklyMatchesNonClassed")
|
||||
int getMaxWeeklyMatchesNonClassed();
|
||||
|
||||
@Key("MaxWeeklyMatchesClassed")
|
||||
int getMaxWeeklyMatchesClassed();
|
||||
|
||||
@Key("MaxWeeklyMatchesTeam")
|
||||
int getMaxWeeklyMatchesTeam();
|
||||
|
||||
// TODO(Zoey76): Change this so the check doesn't depend on a null value.
|
||||
@Key("CurrentCycle")
|
||||
Integer getCurrentCycle();
|
||||
|
||||
@Key("Period")
|
||||
int getPeriod();
|
||||
|
||||
// TODO(Zoey76): Check if this should be in minutes or other time unit.
|
||||
@Key("OlympiadEnd")
|
||||
long getOlympiadEnd();
|
||||
|
||||
@Key("ValidationEnd")
|
||||
long getValidationEnd();
|
||||
|
||||
@Key("NextWeeklyChange")
|
||||
long getNextWeeklyChange();
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
/**
|
||||
* PvP Configuration.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/custom/game/config/pvp.properties",
|
||||
"file:./config/pvp.properties",
|
||||
"classpath:config/pvp.properties"
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 20, unit = MINUTES, type = ASYNC)
|
||||
public interface PvPConfiguration extends Reloadable {
|
||||
|
||||
@Key("CanGMDropEquipment")
|
||||
boolean canGMDropEquipment();
|
||||
|
||||
@Key("PetItems")
|
||||
Set<Integer> getPetItems();
|
||||
|
||||
@Key("NonDroppableItems")
|
||||
Set<Integer> getNonDroppableItems();
|
||||
|
||||
@Key("MinimumPKRequiredToDrop")
|
||||
int getMinimumPKRequiredToDrop();
|
||||
|
||||
@Key("AwardPKKillPVPPoint")
|
||||
boolean awardPKKillPVPPoint();
|
||||
|
||||
@Key("PvPVsNormalTime")
|
||||
int getPvPVsNormalTime();
|
||||
|
||||
@Key("PvPVsPvPTime")
|
||||
int getPvPVsPvPTime();
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
import com.l2jserver.gameserver.config.converter.MapIntegerFloatConverter;
|
||||
|
||||
/**
|
||||
* Rates Configuration.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/custom/game/config/rates.properties",
|
||||
"file:./config/rates.properties",
|
||||
"classpath:config/rates.properties"
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 20, unit = MINUTES, type = ASYNC)
|
||||
public interface RatesConfiguration extends Reloadable {
|
||||
|
||||
@Key("DeathDropAmountMultiplier")
|
||||
double getDeathDropAmountMultiplier();
|
||||
|
||||
@Key("CorpseDropAmountMultiplier")
|
||||
double getCorpseDropAmountMultiplier();
|
||||
|
||||
@Key("HerbDropAmountMultiplier")
|
||||
double getHerbDropAmountMultiplier();
|
||||
|
||||
@Key("RaidDropAmountMultiplier")
|
||||
double getRaidDropAmountMultiplier();
|
||||
|
||||
@Key("DeathDropChanceMultiplier")
|
||||
double getDeathDropChanceMultiplier();
|
||||
|
||||
@Key("CorpseDropChanceMultiplier")
|
||||
double getCorpseDropChanceMultiplier();
|
||||
|
||||
@Key("HerbDropChanceMultiplier")
|
||||
double getHerbDropChanceMultiplier();
|
||||
|
||||
@Key("RaidDropChanceMultiplier")
|
||||
double getRaidDropChanceMultiplier();
|
||||
|
||||
@Key("DropAmountMultiplierByItemId")
|
||||
@ConverterClass(MapIntegerFloatConverter.class)
|
||||
Map<Integer, Float> getDropAmountMultiplierByItemId();
|
||||
|
||||
@Key("DropChanceMultiplierByItemId")
|
||||
@ConverterClass(MapIntegerFloatConverter.class)
|
||||
Map<Integer, Float> getDropChanceMultiplierByItemId();
|
||||
|
||||
@Key("RateXp")
|
||||
float getRateXp();
|
||||
|
||||
@Key("RateSp")
|
||||
float getRateSp();
|
||||
|
||||
@Key("RatePartyXp")
|
||||
float getRatePartyXp();
|
||||
|
||||
@Key("RatePartySp")
|
||||
float getRatePartySp();
|
||||
|
||||
// TODO(Zoey76): Should this be int instead of float?
|
||||
@Key("RateDropManor")
|
||||
int getRateDropManor();
|
||||
|
||||
@Key("RateKarmaLost")
|
||||
double getRateKarmaLost();
|
||||
|
||||
@Key("RateKarmaExpLost")
|
||||
double getRateKarmaExpLost();
|
||||
|
||||
@Key("RateSiegeGuardsPrice")
|
||||
double getRateSiegeGuardsPrice();
|
||||
|
||||
@Key("RateExtractable")
|
||||
float getRateExtractable();
|
||||
|
||||
@Key("RateHellboundTrustIncrease")
|
||||
float getRateHellboundTrustIncrease();
|
||||
|
||||
@Key("RateHellboundTrustDecrease")
|
||||
float getRateHellboundTrustDecrease();
|
||||
|
||||
@Key("QuestDropChanceMultiplier")
|
||||
float getQuestDropChanceMultiplier();
|
||||
|
||||
@Key("QuestDropAmountMultiplier")
|
||||
float getQuestDropAmountMultiplier();
|
||||
|
||||
@Key("RateQuestRewardXP")
|
||||
float getRateQuestRewardXP();
|
||||
|
||||
@Key("RateQuestRewardSP")
|
||||
float getRateQuestRewardSP();
|
||||
|
||||
@Key("RateQuestRewardAdena")
|
||||
float getRateQuestRewardAdena();
|
||||
|
||||
@Key("UseQuestRewardMultipliers")
|
||||
boolean useQuestRewardMultipliers();
|
||||
|
||||
@Key("RateQuestReward")
|
||||
float getRateQuestReward();
|
||||
|
||||
@Key("RateQuestRewardPotion")
|
||||
float getRateQuestRewardPotion();
|
||||
|
||||
@Key("RateQuestRewardScroll")
|
||||
float getRateQuestRewardScroll();
|
||||
|
||||
@Key("RateQuestRewardRecipe")
|
||||
float getRateQuestRewardRecipe();
|
||||
|
||||
@Key("RateQuestRewardMaterial")
|
||||
float getRateQuestRewardMaterial();
|
||||
|
||||
@Key("PlayerDropLimit")
|
||||
int getPlayerDropLimit();
|
||||
|
||||
@Key("PlayerRateDrop")
|
||||
int getPlayerRateDrop();
|
||||
|
||||
@Key("PlayerRateDropItem")
|
||||
int getPlayerRateDropItem();
|
||||
|
||||
@Key("PlayerRateDropEquip")
|
||||
int getPlayerRateDropEquip();
|
||||
|
||||
@Key("PlayerRateDropEquipWeapon")
|
||||
int getPlayerRateDropEquipWeapon();
|
||||
|
||||
@Key("KarmaDropLimit")
|
||||
int getKarmaDropLimit();
|
||||
|
||||
@Key("KarmaRateDrop")
|
||||
int getKarmaRateDrop();
|
||||
|
||||
@Key("KarmaRateDropItem")
|
||||
int getKarmaRateDropItem();
|
||||
|
||||
@Key("KarmaRateDropEquip")
|
||||
int getKarmaRateDropEquip();
|
||||
|
||||
@Key("KarmaRateDropEquipWeapon")
|
||||
int getKarmaRateDropEquipWeapon();
|
||||
|
||||
@Key("PetXpRate")
|
||||
double getPetXpRate();
|
||||
|
||||
// TODO(Zoey76): Should PetFoodRate be float?
|
||||
@Key("PetFoodRate")
|
||||
int getPetFoodRate();
|
||||
|
||||
@Key("SinEaterXpRate")
|
||||
double getSinEaterXpRate();
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Set;
|
||||
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Mutable;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
import com.l2jserver.gameserver.config.converter.FileConverter;
|
||||
import com.l2jserver.gameserver.idfactory.IdFactoryType;
|
||||
|
||||
/**
|
||||
* Server Configuration.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/custom/game/config/server.properties",
|
||||
"file:./config/server.properties",
|
||||
"classpath:config/server.properties",
|
||||
"system:properties",
|
||||
"system:env"
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 20, unit = MINUTES, type = ASYNC)
|
||||
public interface ServerConfiguration extends Mutable, Reloadable {
|
||||
|
||||
@Key("EnableUPnP")
|
||||
boolean enableUPnP();
|
||||
|
||||
@Key("LoginHost")
|
||||
String getLoginHost();
|
||||
|
||||
@Key("LoginPort")
|
||||
int getLoginPort();
|
||||
|
||||
@Key("Host")
|
||||
String getHost();
|
||||
|
||||
@Key("Port")
|
||||
int getPort();
|
||||
|
||||
@Key("RequestServerId")
|
||||
int getRequestServerId();
|
||||
|
||||
@Key("AcceptAlternateId")
|
||||
boolean acceptAlternateId();
|
||||
|
||||
@Key("ReserveHostOnLogin")
|
||||
boolean reserveHostOnLogin();
|
||||
|
||||
@Key("DatapackRoot")
|
||||
@ConverterClass(FileConverter.class)
|
||||
File getDatapackRoot();
|
||||
|
||||
@Key("ScriptRoot")
|
||||
File getScriptRoot();
|
||||
|
||||
@Key("MaxOnlineUsers")
|
||||
int getMaxOnlineUsers();
|
||||
|
||||
@Key("AllowedProtocolRevisions")
|
||||
Set<Integer> getAllowedProtocolRevisions();
|
||||
|
||||
@Key("IdFactory")
|
||||
IdFactoryType getIdFactory();
|
||||
|
||||
@Key("BadIdChecking")
|
||||
boolean badIdChecking();
|
||||
|
||||
@Key("L2J_HOME")
|
||||
String getL2jHome();
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
/**
|
||||
* Seven Signs Configuration.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/custom/game/config/sevensigns.properties",
|
||||
"file:./config/sevensigns.properties",
|
||||
"classpath:config/sevensigns.properties"
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 20, unit = MINUTES, type = ASYNC)
|
||||
public interface SevenSignsConfiguration extends Reloadable {
|
||||
|
||||
// TODO(Zoey76): Implement RequireClanCastle configuration.
|
||||
@Key("RequireClanCastle")
|
||||
boolean requireClanCastle();
|
||||
|
||||
// TODO(Zoey76): Implement CastleForDawn configuration.
|
||||
@Key("CastleForDawn")
|
||||
boolean castleForDawn();
|
||||
|
||||
// TODO(Zoey76): Implement CastleForDusk configuration.
|
||||
@Key("CastleForDusk")
|
||||
boolean castleForDusk();
|
||||
|
||||
@Key("FestivalMinPlayer")
|
||||
int getFestivalMinPlayer();
|
||||
|
||||
@Key("MaxPlayerContrib")
|
||||
int getMaxPlayerContrib();
|
||||
|
||||
// TODO(Zoey76): Convert to milliseconds.
|
||||
@Key("FestivalManagerStart")
|
||||
int getFestivalManagerStart();
|
||||
|
||||
@Key("FestivalLength")
|
||||
int getFestivalLength();
|
||||
|
||||
@Key("FestivalCycleLength")
|
||||
int getFestivalCycleLength();
|
||||
|
||||
@Key("FestivalFirstSpawn")
|
||||
int getFestivalFirstSpawn();
|
||||
|
||||
@Key("FestivalFirstSwarm")
|
||||
int getFestivalFirstSwarm();
|
||||
|
||||
@Key("FestivalSecondSpawn")
|
||||
int getFestivalSecondSpawn();
|
||||
|
||||
@Key("FestivalSecondSwarm")
|
||||
int getFestivalSecondSwarm();
|
||||
|
||||
@Key("FestivalChestSpawn")
|
||||
int getFestivalChestSpawn();
|
||||
|
||||
@Key("DawnGatesPdefMult")
|
||||
double getDawnGatesPdefMult();
|
||||
|
||||
@Key("DuskGatesPdefMult")
|
||||
double getDuskGatesPdefMult();
|
||||
|
||||
@Key("DawnGatesMdefMult")
|
||||
double getDawnGatesMdefMult();
|
||||
|
||||
@Key("DuskGatesMdefMult")
|
||||
double getDuskGatesMdefMult();
|
||||
|
||||
@Key("StrictSevenSigns")
|
||||
boolean strictSevenSigns();
|
||||
|
||||
@Key("SevenSignsLazyUpdate")
|
||||
boolean sevenSignsLazyUpdate();
|
||||
|
||||
@Key("SevenSignsDawnTicketQuantity")
|
||||
int getSevenSignsDawnTicketQuantity();
|
||||
|
||||
@Key("SevenSignsDawnTicketPrice")
|
||||
int getSevenSignsDawnTicketPrice();
|
||||
|
||||
@Key("SevenSignsDawnTicketBundle")
|
||||
int getSevenSignsDawnTicketBundle();
|
||||
|
||||
@Key("SevenSignsManorsAgreementId")
|
||||
int getSevenSignsManorsAgreementId();
|
||||
|
||||
@Key("SevenSignsJoinDawnFee")
|
||||
int getSevenSignsJoinDawnFee();
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
import com.l2jserver.gameserver.config.converter.ControlTowerConverter;
|
||||
import com.l2jserver.gameserver.config.converter.FlameTowerConverter;
|
||||
import com.l2jserver.gameserver.model.TowerSpawn;
|
||||
|
||||
/**
|
||||
* Siege Configuration.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/custom/game/config/siege.properties",
|
||||
"file:./config/siege.properties",
|
||||
"classpath:config/siege.properties"
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 20, unit = MINUTES, type = ASYNC)
|
||||
public interface SiegeConfiguration extends Reloadable {
|
||||
|
||||
@Key("SiegeLength")
|
||||
int getSiegeLength();
|
||||
|
||||
@Key("MaxFlags")
|
||||
int getMaxFlags();
|
||||
|
||||
@Key("ClanMinLevel")
|
||||
int getClanMinLevel();
|
||||
|
||||
@Key("AttackerMaxClans")
|
||||
int getAttackerMaxClans();
|
||||
|
||||
@Key("DefenderMaxClans")
|
||||
int getDefenderMaxClans();
|
||||
|
||||
@Key("AttackerRespawn")
|
||||
int getAttackerRespawn();
|
||||
|
||||
@Key("BloodAllianceReward")
|
||||
int getBloodAllianceReward();
|
||||
|
||||
@Key("GludioFlameTower1")
|
||||
@ConverterClass(FlameTowerConverter.class)
|
||||
TowerSpawn getGludioFlameTower1();
|
||||
|
||||
@Key("GludioFlameTower2")
|
||||
@ConverterClass(FlameTowerConverter.class)
|
||||
TowerSpawn getGludioFlameTower2();
|
||||
|
||||
@Key("GludioControlTower1")
|
||||
@ConverterClass(ControlTowerConverter.class)
|
||||
TowerSpawn getGludioControlTower1();
|
||||
|
||||
@Key("GludioControlTower2")
|
||||
@ConverterClass(ControlTowerConverter.class)
|
||||
TowerSpawn getGludioControlTower2();
|
||||
|
||||
@Key("GludioControlTower3")
|
||||
@ConverterClass(ControlTowerConverter.class)
|
||||
TowerSpawn getGludioControlTower3();
|
||||
|
||||
@Key("GludioMaxMercenaries")
|
||||
int getGludioMaxMercenaries();
|
||||
|
||||
@Key("GiranFlameTower1")
|
||||
@ConverterClass(FlameTowerConverter.class)
|
||||
TowerSpawn getGiranFlameTower1();
|
||||
|
||||
@Key("GiranFlameTower2")
|
||||
@ConverterClass(FlameTowerConverter.class)
|
||||
TowerSpawn getGiranFlameTower2();
|
||||
|
||||
@Key("GiranControlTower1")
|
||||
@ConverterClass(ControlTowerConverter.class)
|
||||
TowerSpawn getGiranControlTower1();
|
||||
|
||||
@Key("GiranControlTower2")
|
||||
@ConverterClass(ControlTowerConverter.class)
|
||||
TowerSpawn getGiranControlTower2();
|
||||
|
||||
@Key("GiranControlTower3")
|
||||
@ConverterClass(ControlTowerConverter.class)
|
||||
TowerSpawn getGiranControlTower3();
|
||||
|
||||
@Key("GiranMaxMercenaries")
|
||||
int getGiranMaxMercenaries();
|
||||
|
||||
@Key("DionFlameTower1")
|
||||
@ConverterClass(FlameTowerConverter.class)
|
||||
TowerSpawn getDionFlameTower1();
|
||||
|
||||
@Key("DionFlameTower2")
|
||||
@ConverterClass(FlameTowerConverter.class)
|
||||
TowerSpawn getDionFlameTower2();
|
||||
|
||||
@Key("DionControlTower1")
|
||||
@ConverterClass(ControlTowerConverter.class)
|
||||
TowerSpawn getDionControlTower1();
|
||||
|
||||
@Key("DionControlTower2")
|
||||
@ConverterClass(ControlTowerConverter.class)
|
||||
TowerSpawn getDionControlTower2();
|
||||
|
||||
@Key("DionControlTower3")
|
||||
@ConverterClass(ControlTowerConverter.class)
|
||||
TowerSpawn getDionControlTower3();
|
||||
|
||||
@Key("DionMaxMercenaries")
|
||||
int getDionMaxMercenaries();
|
||||
|
||||
@Key("OrenFlameTower1")
|
||||
@ConverterClass(FlameTowerConverter.class)
|
||||
TowerSpawn getOrenFlameTower1();
|
||||
|
||||
@Key("OrenFlameTower2")
|
||||
@ConverterClass(FlameTowerConverter.class)
|
||||
TowerSpawn getOrenFlameTower2();
|
||||
|
||||
@Key("OrenControlTower1")
|
||||
@ConverterClass(ControlTowerConverter.class)
|
||||
TowerSpawn getOrenControlTower1();
|
||||
|
||||
@Key("OrenControlTower2")
|
||||
@ConverterClass(ControlTowerConverter.class)
|
||||
TowerSpawn getOrenControlTower2();
|
||||
|
||||
@Key("OrenControlTower3")
|
||||
@ConverterClass(ControlTowerConverter.class)
|
||||
TowerSpawn getOrenControlTower3();
|
||||
|
||||
@Key("OrenMaxMercenaries")
|
||||
int getOrenMaxMercenaries();
|
||||
|
||||
@Key("AdenFlameTower1")
|
||||
@ConverterClass(FlameTowerConverter.class)
|
||||
TowerSpawn getAdenFlameTower1();
|
||||
|
||||
@Key("AdenFlameTower2")
|
||||
@ConverterClass(FlameTowerConverter.class)
|
||||
TowerSpawn AdenFlameTower2();
|
||||
|
||||
@Key("AdenControlTower1")
|
||||
@ConverterClass(ControlTowerConverter.class)
|
||||
TowerSpawn getAdenControlTower1();
|
||||
|
||||
@Key("AdenControlTower2")
|
||||
@ConverterClass(ControlTowerConverter.class)
|
||||
TowerSpawn getAdenControlTower2();
|
||||
|
||||
@Key("AdenControlTower3")
|
||||
@ConverterClass(ControlTowerConverter.class)
|
||||
TowerSpawn getAdenControlTower3();
|
||||
|
||||
@Key("AdenMaxMercenaries")
|
||||
int getAdenMaxMercenaries();
|
||||
|
||||
@Key("InnadrilFlameTower1")
|
||||
@ConverterClass(FlameTowerConverter.class)
|
||||
TowerSpawn getInnadrilFlameTower1();
|
||||
|
||||
@Key("InnadrilFlameTower2")
|
||||
@ConverterClass(FlameTowerConverter.class)
|
||||
TowerSpawn getInnadrilFlameTower2();
|
||||
|
||||
@Key("InnadrilControlTower1")
|
||||
@ConverterClass(ControlTowerConverter.class)
|
||||
TowerSpawn getInnadrilControlTower1();
|
||||
|
||||
@Key("InnadrilControlTower2")
|
||||
@ConverterClass(ControlTowerConverter.class)
|
||||
TowerSpawn getInnadrilControlTower2();
|
||||
|
||||
@Key("InnadrilControlTower3")
|
||||
@ConverterClass(ControlTowerConverter.class)
|
||||
TowerSpawn getInnadrilControlTower3();
|
||||
|
||||
@Key("InnadrilMaxMercenaries")
|
||||
int getInnadrilMaxMercenaries();
|
||||
|
||||
@Key("GoddardFlameTower1")
|
||||
@ConverterClass(FlameTowerConverter.class)
|
||||
TowerSpawn getGoddardFlameTower1();
|
||||
|
||||
@Key("GoddardFlameTower2")
|
||||
@ConverterClass(FlameTowerConverter.class)
|
||||
TowerSpawn getGoddardFlameTower2();
|
||||
|
||||
@Key("GoddardControlTower1")
|
||||
@ConverterClass(ControlTowerConverter.class)
|
||||
TowerSpawn getGoddardControlTower1();
|
||||
|
||||
@Key("GoddardControlTower2")
|
||||
@ConverterClass(ControlTowerConverter.class)
|
||||
TowerSpawn getGoddardControlTower2();
|
||||
|
||||
@Key("GoddardControlTower3")
|
||||
@ConverterClass(ControlTowerConverter.class)
|
||||
TowerSpawn getGoddardControlTower3();
|
||||
|
||||
@Key("GoddardMaxMercenaries")
|
||||
int getGoddardMaxMercenaries();
|
||||
|
||||
@Key("RuneFlameTower1")
|
||||
@ConverterClass(FlameTowerConverter.class)
|
||||
TowerSpawn getRuneFlameTower1();
|
||||
|
||||
@Key("RuneFlameTower2")
|
||||
@ConverterClass(FlameTowerConverter.class)
|
||||
TowerSpawn getRuneFlameTower2();
|
||||
|
||||
@Key("RuneControlTower1")
|
||||
@ConverterClass(ControlTowerConverter.class)
|
||||
TowerSpawn getRuneControlTower1();
|
||||
|
||||
@Key("RuneControlTower2")
|
||||
@ConverterClass(ControlTowerConverter.class)
|
||||
TowerSpawn getRuneControlTower2();
|
||||
|
||||
@Key("RuneControlTower3")
|
||||
@ConverterClass(ControlTowerConverter.class)
|
||||
TowerSpawn getRuneControlTower3();
|
||||
|
||||
@Key("RuneMaxMercenaries")
|
||||
int getRuneMaxMercenaries();
|
||||
|
||||
@Key("SchuttgartFlameTower1")
|
||||
@ConverterClass(FlameTowerConverter.class)
|
||||
TowerSpawn getSchuttgartFlameTower1();
|
||||
|
||||
@Key("SchuttgartFlameTower2")
|
||||
@ConverterClass(FlameTowerConverter.class)
|
||||
TowerSpawn getSchuttgartFlameTower2();
|
||||
|
||||
@Key("SchuttgartControlTower1")
|
||||
@ConverterClass(ControlTowerConverter.class)
|
||||
TowerSpawn getSchuttgartControlTower1();
|
||||
|
||||
@Key("SchuttgartControlTower2")
|
||||
@ConverterClass(ControlTowerConverter.class)
|
||||
TowerSpawn getSchuttgartControlTower2();
|
||||
|
||||
@Key("SchuttgartControlTower3")
|
||||
@ConverterClass(ControlTowerConverter.class)
|
||||
TowerSpawn getSchuttgartControlTower3();
|
||||
|
||||
@Key("SchuttgartMaxMercenaries")
|
||||
int getSchuttgartMaxMercenaries();
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
import com.l2jserver.gameserver.model.zone.ZoneId;
|
||||
|
||||
/**
|
||||
* Teleporter Service Configuration.
|
||||
* @author HorridoJoho
|
||||
* @version 2.6.2.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/custom/game/config/teleporterservice.properties",
|
||||
"file:./config/teleporterservice.properties",
|
||||
"classpath:config/teleporterservice.properties"
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 20, unit = MINUTES, type = ASYNC)
|
||||
public interface TeleporterServiceConfiguration extends Reloadable {
|
||||
|
||||
@Key("Enable")
|
||||
boolean enable();
|
||||
|
||||
@Key("Debug")
|
||||
boolean getDebug();
|
||||
|
||||
@Key("ForbidInZones")
|
||||
ZoneId[] getForbidInZones();
|
||||
|
||||
@Key("ForbidInEvents")
|
||||
boolean getForbidInEvents();
|
||||
|
||||
@Key("ForbidInDuel")
|
||||
boolean getForbidInDuel();
|
||||
|
||||
@Key("ForbidInFight")
|
||||
boolean getForbidInFight();
|
||||
|
||||
@Key("ForbidInPvp")
|
||||
boolean getForbidInPvp();
|
||||
|
||||
@Key("ForbidForChaoticPlayers")
|
||||
boolean getForbidForChaoticPlayers();
|
||||
|
||||
@Key("VoicedEnable")
|
||||
boolean getVoicedEnable();
|
||||
|
||||
@Key("VoicedCommand")
|
||||
String getVoicedCommand();
|
||||
|
||||
@Key("VoicedName")
|
||||
String getVoicedName();
|
||||
|
||||
@Key("VoicedRequiredItem")
|
||||
int getVoicedRequiredItem();
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
/**
|
||||
* Telnet Configuration.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/custom/game/config/telnet.properties",
|
||||
"file:./config/telnet.properties",
|
||||
"classpath:config/telnet.properties"
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 20, unit = MINUTES, type = ASYNC)
|
||||
public interface TelnetConfiguration extends Reloadable {
|
||||
|
||||
@Key("EnableTelnet")
|
||||
boolean isEnabled();
|
||||
|
||||
@Key("Port")
|
||||
int getPort();
|
||||
|
||||
@Key("Password")
|
||||
String getPassword();
|
||||
|
||||
@Key("Hosts")
|
||||
List<String> getHosts();
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
/**
|
||||
* Territory War Configuration.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/custom/game/config/territorywar.properties",
|
||||
"file:./config/territorywar.properties",
|
||||
"classpath:config/territorywar.properties"
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 20, unit = MINUTES, type = ASYNC)
|
||||
public interface TerritoryWarConfiguration extends Reloadable {
|
||||
|
||||
// TODO(Zoey76): Convert this to milliseconds.
|
||||
@Key("WarLength")
|
||||
long getWarLength();
|
||||
|
||||
// TODO(Zoey76): Implement ClanMinLevel configuration.
|
||||
@Key("ClanMinLevel")
|
||||
int getClanMinLevel();
|
||||
|
||||
@Key("PlayerMinLevel")
|
||||
int getPlayerMinLevel();
|
||||
|
||||
// TODO(Zoey76): Implement DefenderMaxClans configuration.
|
||||
@Key("DefenderMaxClans")
|
||||
int getDefenderMaxClans();
|
||||
|
||||
// TODO(Zoey76): Implement DefenderMaxPlayers configuration.
|
||||
@Key("DefenderMaxPlayers")
|
||||
int getDefenderMaxPlayers();
|
||||
|
||||
@Key("PlayerWithWardCanBeKilledInPeaceZone")
|
||||
boolean playerWithWardCanBeKilledInPeaceZone();
|
||||
|
||||
@Key("SpawnWardsWhenTWIsNotInProgress")
|
||||
boolean spawnWardsWhenTWIsNotInProgress();
|
||||
|
||||
@Key("ReturnWardsWhenTWStarts")
|
||||
boolean returnWardsWhenTWStarts();
|
||||
|
||||
@Key("MinTerritoryBadgeForNobless")
|
||||
int getMinTerritoryBadgeForNobless();
|
||||
|
||||
@Key("MinTerritoryBadgeForStriders")
|
||||
int getMinTerritoryBadgeForStriders();
|
||||
|
||||
@Key("MinTerritoryBadgeForBigStrider")
|
||||
int getMinTerritoryBadgeForBigStrider();
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
import com.l2jserver.gameserver.config.converter.ItemHolderConverter;
|
||||
import com.l2jserver.gameserver.config.converter.LocationConverter;
|
||||
import com.l2jserver.gameserver.config.converter.Seconds2MillisecondsConverter;
|
||||
import com.l2jserver.gameserver.config.converter.SkillHolderConverter;
|
||||
import com.l2jserver.gameserver.model.Location;
|
||||
import com.l2jserver.gameserver.model.holders.ItemHolder;
|
||||
import com.l2jserver.gameserver.model.holders.SkillHolder;
|
||||
|
||||
/**
|
||||
* TvT Configuration.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/custom/game/config/tvt.properties",
|
||||
"file:./config/tvt.properties",
|
||||
"classpath:config/tvt.properties"
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 20, unit = MINUTES, type = ASYNC)
|
||||
public interface TvTConfiguration extends Reloadable {
|
||||
|
||||
@Key("Enabled")
|
||||
boolean enabled();
|
||||
|
||||
@Key("Instanced")
|
||||
boolean instanced();
|
||||
|
||||
@Key("InstanceFile")
|
||||
String getInstanceFile();
|
||||
|
||||
@Key("Interval")
|
||||
List<String> getInterval();
|
||||
|
||||
// TODO(Zoey76): Convert to milliseconds.
|
||||
@Key("ParticipationTime")
|
||||
int getParticipationTime();
|
||||
|
||||
// TODO(Zoey76): Convert to milliseconds.
|
||||
@Key("RunningTime")
|
||||
int getRunningTime();
|
||||
|
||||
@Key("ParticipationNpcId")
|
||||
int getParticipationNpcId();
|
||||
|
||||
@Key("ParticipationFee")
|
||||
@ConverterClass(ItemHolderConverter.class)
|
||||
ItemHolder getParticipationFee();
|
||||
|
||||
@Key("ParticipationNpcLoc")
|
||||
@ConverterClass(LocationConverter.class)
|
||||
Location getParticipationNpcLoc();
|
||||
|
||||
@Key("MinPlayersInTeams")
|
||||
int getMinPlayersInTeams();
|
||||
|
||||
@Key("MaxPlayersInTeams")
|
||||
int getMaxPlayersInTeams();
|
||||
|
||||
@Key("MinPlayerLevel")
|
||||
int getMinPlayerLevel();
|
||||
|
||||
@Key("MaxPlayerLevel")
|
||||
int getMaxPlayerLevel();
|
||||
|
||||
@Key("RespawnTeleportDelay")
|
||||
@ConverterClass(Seconds2MillisecondsConverter.class)
|
||||
long getRespawnTeleportDelay();
|
||||
|
||||
@Key("StartLeaveTeleportDelay")
|
||||
@ConverterClass(Seconds2MillisecondsConverter.class)
|
||||
long getStartLeaveTeleportDelay();
|
||||
|
||||
@Key("Team1Name")
|
||||
String getTeam1Name();
|
||||
|
||||
@Key("Team1Loc")
|
||||
@ConverterClass(LocationConverter.class)
|
||||
Location getTeam1Loc();
|
||||
|
||||
@Key("Team2Name")
|
||||
String getTeam2Name();
|
||||
|
||||
@Key("Team2Loc")
|
||||
@ConverterClass(LocationConverter.class)
|
||||
Location getTeam2Loc();
|
||||
|
||||
@Separator(";")
|
||||
@Key("Reward")
|
||||
@ConverterClass(ItemHolderConverter.class)
|
||||
List<ItemHolder> getReward();
|
||||
|
||||
@Key("AllowTargetTeamMember")
|
||||
boolean allowTargetTeamMember();
|
||||
|
||||
@Key("AllowScroll")
|
||||
boolean allowScroll();
|
||||
|
||||
@Key("AllowPotion")
|
||||
boolean allowPotion();
|
||||
|
||||
// TODO(Zoey76): Implement AllowSummonByItem configuration.
|
||||
@Key("AllowSummonByItem")
|
||||
boolean allowSummonByItem();
|
||||
|
||||
@Key("DoorsToOpen")
|
||||
List<Integer> getDoorsToOpen();
|
||||
|
||||
@Key("DoorsToClose")
|
||||
List<Integer> getDoorsToClose();
|
||||
|
||||
@Key("RewardTeamTie")
|
||||
boolean rewardTeamTie();
|
||||
|
||||
@Key("EffectsRemoval")
|
||||
int getEffectsRemoval();
|
||||
|
||||
@Separator(";")
|
||||
@Key("FighterBuffs")
|
||||
@ConverterClass(SkillHolderConverter.class)
|
||||
List<SkillHolder> getFighterBuffs();
|
||||
|
||||
@Separator(";")
|
||||
@Key("MageBuffs")
|
||||
@ConverterClass(SkillHolderConverter.class)
|
||||
List<SkillHolder> getMageBuffs();
|
||||
|
||||
@Key("MaxParticipantsPerIP")
|
||||
int getMaxParticipantsPerIP();
|
||||
|
||||
@Key("AllowVoicedInfoCommand")
|
||||
boolean allowVoicedInfoCommand();
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.aeonbits.owner.Config.HotReloadType.ASYNC;
|
||||
import static org.aeonbits.owner.Config.LoadType.MERGE;
|
||||
|
||||
import org.aeonbits.owner.Config.HotReload;
|
||||
import org.aeonbits.owner.Config.LoadPolicy;
|
||||
import org.aeonbits.owner.Config.Sources;
|
||||
import org.aeonbits.owner.Reloadable;
|
||||
|
||||
/**
|
||||
* Vitality Configuration.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
@Sources({
|
||||
"file:${L2J_HOME}/custom/game/config/vitality.properties",
|
||||
"file:./config/vitality.properties",
|
||||
"classpath:config/vitality.properties"
|
||||
})
|
||||
@LoadPolicy(MERGE)
|
||||
@HotReload(value = 20, unit = MINUTES, type = ASYNC)
|
||||
public interface VitalityConfiguration extends Reloadable {
|
||||
|
||||
@Key("Enabled")
|
||||
boolean enabled();
|
||||
|
||||
@Key("RecoverVitalityOnReconnect")
|
||||
boolean recoverVitalityOnReconnect();
|
||||
|
||||
@Key("StartingVitalityPoints")
|
||||
int getStartingVitalityPoints();
|
||||
|
||||
@Key("RateVitalityLevel1")
|
||||
float getRateVitalityLevel1();
|
||||
|
||||
@Key("RateVitalityLevel2")
|
||||
float getRateVitalityLevel2();
|
||||
|
||||
@Key("RateVitalityLevel3")
|
||||
float getRateVitalityLevel3();
|
||||
|
||||
@Key("RateVitalityLevel4")
|
||||
float getRateVitalityLevel4();
|
||||
|
||||
@Key("RateVitalityGain")
|
||||
float getRateVitalityGain();
|
||||
|
||||
@Key("RateVitalityLost")
|
||||
float getRateVitalityLost();
|
||||
|
||||
@Key("RateRecoveryPeaceZone")
|
||||
float getRateRecoveryPeaceZone();
|
||||
|
||||
@Key("RateRecoveryOnReconnect")
|
||||
float getRateRecoveryOnReconnect();
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config.converter;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.l2jserver.gameserver.model.holders.ItemHolder;
|
||||
|
||||
/**
|
||||
* Class Master Setting.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
public class ClassMasterSetting {
|
||||
|
||||
private final Map<Integer, List<ItemHolder>> claimItems = new HashMap<>(3);
|
||||
|
||||
private final Map<Integer, List<ItemHolder>> rewardItems = new HashMap<>(3);
|
||||
|
||||
private final Map<Integer, Boolean> allowedClassChange = new HashMap<>(3);
|
||||
|
||||
public void addClaimItems(Integer job, List<ItemHolder> items) {
|
||||
claimItems.put(job, items);
|
||||
}
|
||||
|
||||
public void addRewardItems(Integer job, List<ItemHolder> items) {
|
||||
rewardItems.put(job, items);
|
||||
}
|
||||
|
||||
public void addAllowedClassChange(Integer job) {
|
||||
allowedClassChange.put(job, true);
|
||||
}
|
||||
|
||||
public boolean isAllowed(int job) {
|
||||
return allowedClassChange.getOrDefault(job, false);
|
||||
}
|
||||
|
||||
public List<ItemHolder> getRewardItems(int job) {
|
||||
return rewardItems.getOrDefault(job, Collections.emptyList());
|
||||
}
|
||||
|
||||
public List<ItemHolder> getRequireItems(int job) {
|
||||
return claimItems.getOrDefault(job, Collections.emptyList());
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config.converter;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.aeonbits.owner.Converter;
|
||||
|
||||
import com.l2jserver.gameserver.model.holders.ItemHolder;
|
||||
|
||||
/**
|
||||
* Class Master Setting converter.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
public class ClassMasterSettingConverter implements Converter<ClassMasterSetting> {
|
||||
|
||||
@Override
|
||||
public ClassMasterSetting convert(Method method, String input) {
|
||||
final var classMasterSetting = new ClassMasterSetting();
|
||||
input = input.trim();
|
||||
if (input.isEmpty()) {
|
||||
return classMasterSetting;
|
||||
}
|
||||
|
||||
final StringTokenizer st = new StringTokenizer(input, ";");
|
||||
while (st.hasMoreTokens()) {
|
||||
// get allowed class change
|
||||
final int job = Integer.parseInt(st.nextToken());
|
||||
classMasterSetting.addAllowedClassChange(job);
|
||||
|
||||
final List<ItemHolder> requiredItems = new ArrayList<>();
|
||||
// parse items needed for class change
|
||||
if (st.hasMoreTokens()) {
|
||||
final StringTokenizer st2 = new StringTokenizer(st.nextToken(), "[],");
|
||||
while (st2.hasMoreTokens()) {
|
||||
final StringTokenizer st3 = new StringTokenizer(st2.nextToken(), "()");
|
||||
final int itemId = Integer.parseInt(st3.nextToken());
|
||||
final int quantity = Integer.parseInt(st3.nextToken());
|
||||
requiredItems.add(new ItemHolder(itemId, quantity));
|
||||
}
|
||||
}
|
||||
classMasterSetting.addClaimItems(job, requiredItems);
|
||||
|
||||
final List<ItemHolder> rewardItems = new ArrayList<>();
|
||||
// parse gifts after class change
|
||||
if (st.hasMoreTokens()) {
|
||||
final StringTokenizer st2 = new StringTokenizer(st.nextToken(), "[],");
|
||||
while (st2.hasMoreTokens()) {
|
||||
final StringTokenizer st3 = new StringTokenizer(st2.nextToken(), "()");
|
||||
final int itemId = Integer.parseInt(st3.nextToken());
|
||||
final int quantity = Integer.parseInt(st3.nextToken());
|
||||
rewardItems.add(new ItemHolder(itemId, quantity));
|
||||
}
|
||||
}
|
||||
classMasterSetting.addRewardItems(job, rewardItems);
|
||||
}
|
||||
return classMasterSetting;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config.converter;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.aeonbits.owner.Converter;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
|
||||
/**
|
||||
* Color Converter.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
public class ColorConverter implements Converter<Integer> {
|
||||
|
||||
private static final Integer DEFAULT_COLOR = Integer.decode("0x808080");
|
||||
|
||||
@Override
|
||||
public Integer convert(Method method, String input) {
|
||||
if (Strings.isBlank(input)) {
|
||||
return DEFAULT_COLOR;
|
||||
}
|
||||
return Integer.decode("0x" + input);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config.converter;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.aeonbits.owner.Converter;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
|
||||
import com.l2jserver.gameserver.model.Location;
|
||||
import com.l2jserver.gameserver.model.TowerSpawn;
|
||||
|
||||
/**
|
||||
* Control Tower Converter.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
public class ControlTowerConverter implements Converter<TowerSpawn> {
|
||||
|
||||
@Override
|
||||
public TowerSpawn convert(Method method, String input) {
|
||||
if (Strings.isBlank(input)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final StringTokenizer st = new StringTokenizer(input, ",");
|
||||
final int x = Integer.parseInt(st.nextToken());
|
||||
final int y = Integer.parseInt(st.nextToken());
|
||||
final int z = Integer.parseInt(st.nextToken());
|
||||
final int npcId = Integer.parseInt(st.nextToken());
|
||||
|
||||
return new TowerSpawn(npcId, new Location(x, y, z));
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config.converter;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.DAYS;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.aeonbits.owner.Converter;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
|
||||
/**
|
||||
* Days to Milliseconds converter.
|
||||
* @author Zoey76
|
||||
* @version 2.6.3.0
|
||||
*/
|
||||
public class Days2MillisecondsConverter implements Converter<Long> {
|
||||
|
||||
@Override
|
||||
public Long convert(Method method, String input) {
|
||||
if (Strings.isBlank(input)) {
|
||||
return 0L;
|
||||
}
|
||||
return DAYS.toMillis(Long.parseLong(input));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config.converter;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.aeonbits.owner.Converter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* File Converter.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
public class FileConverter implements Converter<File> {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(FileConverter.class);
|
||||
|
||||
@Override
|
||||
public File convert(Method method, String input) {
|
||||
try {
|
||||
return new File(input.replace('\\', '/')).getCanonicalFile();
|
||||
} catch (Exception ex) {
|
||||
LOG.warn("Error setting script root!", ex);
|
||||
return new File(".");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config.converter;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.aeonbits.owner.Converter;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
|
||||
import com.l2jserver.gameserver.model.Location;
|
||||
import com.l2jserver.gameserver.model.TowerSpawn;
|
||||
|
||||
/**
|
||||
* Flame Tower Converter.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
public class FlameTowerConverter implements Converter<TowerSpawn> {
|
||||
|
||||
@Override
|
||||
public TowerSpawn convert(Method method, String input) {
|
||||
if (Strings.isBlank(input)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final StringTokenizer st = new StringTokenizer(input, ",");
|
||||
final int x = Integer.parseInt(st.nextToken());
|
||||
final int y = Integer.parseInt(st.nextToken());
|
||||
final int z = Integer.parseInt(st.nextToken());
|
||||
final int npcId = Integer.parseInt(st.nextToken());
|
||||
final List<Integer> zones = new LinkedList<>();
|
||||
while (st.hasMoreTokens()) {
|
||||
zones.add(Integer.parseInt(st.nextToken()));
|
||||
}
|
||||
|
||||
return new TowerSpawn(npcId, new Location(x, y, z), zones);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config.converter;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigInteger;
|
||||
|
||||
import org.aeonbits.owner.Converter;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
|
||||
/**
|
||||
* Hex Id Converter.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
public class HexIdConverter implements Converter<BigInteger> {
|
||||
|
||||
@Override
|
||||
public BigInteger convert(Method method, String input) {
|
||||
if (Strings.isBlank(input)) {
|
||||
return null;
|
||||
}
|
||||
return new BigInteger(input, 16);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config.converter;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.HOURS;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.aeonbits.owner.Converter;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
|
||||
/**
|
||||
* Hours to Milliseconds converter.
|
||||
* @author Zoey76
|
||||
* @version 2.6.3.0
|
||||
*/
|
||||
public class Hours2MillisecondsConverter implements Converter<Long> {
|
||||
|
||||
@Override
|
||||
public Long convert(Method method, String input) {
|
||||
if (Strings.isBlank(input)) {
|
||||
return 0L;
|
||||
}
|
||||
return HOURS.toMillis(Long.parseLong(input));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config.converter;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.InetAddress;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.aeonbits.owner.Converter;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* IP-Limit Converter.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
public class IPLimitConverter implements Converter<Map<Integer, Integer>> {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(IPLimitConverter.class);
|
||||
|
||||
@Override
|
||||
public Map<Integer, Integer> convert(Method method, String input) {
|
||||
final var map = new HashMap<Integer, Integer>();
|
||||
if (Strings.isBlank(input)) {
|
||||
return map;
|
||||
}
|
||||
|
||||
final var tokens = input.replace(" ", "").split(";");
|
||||
for (var token : tokens) {
|
||||
final var splitToken = token.split(",");
|
||||
try {
|
||||
map.put(InetAddress.getByName(splitToken[0]).hashCode(), Integer.valueOf(splitToken[1]));
|
||||
} catch (Exception ex) {
|
||||
LOG.warn("Invalid DualboxCheckWhitelist IP {} or limit {}!", splitToken[0], splitToken[1], ex);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config.converter;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.aeonbits.owner.Converter;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
|
||||
import com.l2jserver.gameserver.model.holders.ItemChanceHolder;
|
||||
|
||||
/**
|
||||
* Item Chance Holder Converter.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
public class ItemChanceHolderConverter implements Converter<ItemChanceHolder> {
|
||||
|
||||
@Override
|
||||
public ItemChanceHolder convert(Method method, String input) {
|
||||
if (Strings.isBlank(input)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final var tokens = input.replace(" ", "").split(",");
|
||||
return new ItemChanceHolder(Integer.parseInt(tokens[0]), Float.parseFloat(tokens[1]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config.converter;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.aeonbits.owner.Converter;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
|
||||
import com.l2jserver.gameserver.model.holders.ItemHolder;
|
||||
|
||||
/**
|
||||
* Item Holder Converter.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
public class ItemHolderConverter implements Converter<ItemHolder> {
|
||||
|
||||
@Override
|
||||
public ItemHolder convert(Method method, String input) {
|
||||
if (Strings.isBlank(input)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final var tokens = input.replace(" ", "").split(",");
|
||||
return new ItemHolder(Integer.parseInt(tokens[0]), Long.parseLong(tokens[1]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config.converter;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.aeonbits.owner.Converter;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
|
||||
import com.l2jserver.gameserver.model.Location;
|
||||
|
||||
/**
|
||||
* Location Converter.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
public class LocationConverter implements Converter<Location> {
|
||||
|
||||
@Override
|
||||
public Location convert(Method method, String input) {
|
||||
if (Strings.isBlank(input)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final var tokens = input.replace(" ", "").split(",");
|
||||
final var location = new Location(Integer.parseInt(tokens[0]), Integer.parseInt(tokens[1]), Integer.parseInt(tokens[2]));
|
||||
if (tokens.length >= 4) {
|
||||
location.setHeading(Integer.parseInt(tokens[3]));
|
||||
}
|
||||
if (tokens.length == 5) {
|
||||
location.setInstanceId(Integer.parseInt(tokens[4]));
|
||||
}
|
||||
return location;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright © 2004-2026 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.config.converter;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.aeonbits.owner.Converter;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
|
||||
/**
|
||||
* Map Integer-Float Converter.
|
||||
* @author Zoey76
|
||||
* @version 2.6.1.0
|
||||
*/
|
||||
public class MapIntegerFloatConverter implements Converter<Map<Integer, Float>> {
|
||||
|
||||
@Override
|
||||
public Map<Integer, Float> convert(Method method, String input) {
|
||||
final var map = new HashMap<Integer, Float>();
|
||||
if (Strings.isBlank(input)) {
|
||||
return map;
|
||||
}
|
||||
|
||||
final var tokens = input.replace(" ", "").split(";");
|
||||
for (var token : tokens) {
|
||||
final var splitToken = token.split(",");
|
||||
map.put(Integer.valueOf(splitToken[0]), Float.valueOf(splitToken[1]));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user