Project update.
This commit is contained in:
229
trunk/java/com/l2jmobius/gameserver/util/Broadcast.java
Normal file
229
trunk/java/com/l2jmobius/gameserver/util/Broadcast.java
Normal file
@ -0,0 +1,229 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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.l2jmobius.gameserver.util;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.l2jmobius.gameserver.enums.ChatType;
|
||||
import com.l2jmobius.gameserver.model.L2World;
|
||||
import com.l2jmobius.gameserver.model.actor.L2Character;
|
||||
import com.l2jmobius.gameserver.model.actor.L2Summon;
|
||||
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
|
||||
import com.l2jmobius.gameserver.network.serverpackets.CharInfo;
|
||||
import com.l2jmobius.gameserver.network.serverpackets.CreatureSay;
|
||||
import com.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
|
||||
import com.l2jmobius.gameserver.network.serverpackets.L2GameServerPacket;
|
||||
import com.l2jmobius.gameserver.network.serverpackets.RelationChanged;
|
||||
|
||||
/**
|
||||
* This class ...
|
||||
* @version $Revision: 1.2 $ $Date: 2004/06/27 08:12:59 $
|
||||
*/
|
||||
public final class Broadcast
|
||||
{
|
||||
private static Logger _log = Logger.getLogger(Broadcast.class.getName());
|
||||
|
||||
/**
|
||||
* Send a packet to all L2PcInstance in the _KnownPlayers of the L2Character that have the Character targeted.<BR>
|
||||
* <B><U> Concept</U> :</B><BR>
|
||||
* L2PcInstance in the detection area of the L2Character are identified in <B>_knownPlayers</B>.<BR>
|
||||
* In order to inform other players of state modification on the L2Character, server just need to go through _knownPlayers to send Server->Client Packet<BR>
|
||||
* <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T SEND Server->Client packet to this L2Character (to do this use method toSelfAndKnownPlayers)</B></FONT><BR>
|
||||
* @param character
|
||||
* @param mov
|
||||
*/
|
||||
public static void toPlayersTargettingMyself(L2Character character, L2GameServerPacket mov)
|
||||
{
|
||||
final Collection<L2PcInstance> plrs = character.getKnownList().getKnownPlayers().values();
|
||||
for (L2PcInstance player : plrs)
|
||||
{
|
||||
if (player.getTarget() != character)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
player.sendPacket(mov);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a packet to all L2PcInstance in the _KnownPlayers of the L2Character.<BR>
|
||||
* <B><U> Concept</U> :</B><BR>
|
||||
* L2PcInstance in the detection area of the L2Character are identified in <B>_knownPlayers</B>.<BR>
|
||||
* In order to inform other players of state modification on the L2Character, server just need to go through _knownPlayers to send Server->Client Packet<BR>
|
||||
* <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T SEND Server->Client packet to this L2Character (to do this use method toSelfAndKnownPlayers)</B></FONT><BR>
|
||||
* @param character
|
||||
* @param mov
|
||||
*/
|
||||
public static void toKnownPlayers(L2Character character, L2GameServerPacket mov)
|
||||
{
|
||||
final Collection<L2PcInstance> plrs = character.getKnownList().getKnownPlayers().values();
|
||||
for (L2PcInstance player : plrs)
|
||||
{
|
||||
if (player == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
try
|
||||
{
|
||||
player.sendPacket(mov);
|
||||
if ((mov instanceof CharInfo) && (character.isPlayer()))
|
||||
{
|
||||
final int relation = ((L2PcInstance) character).getRelation(player);
|
||||
final Integer oldrelation = character.getKnownList().getKnownRelations().get(player.getObjectId());
|
||||
if ((oldrelation != null) && (oldrelation != relation))
|
||||
{
|
||||
final RelationChanged rc = new RelationChanged();
|
||||
rc.addRelation((L2PcInstance) character, relation, character.isAutoAttackable(player));
|
||||
if (character.hasSummon())
|
||||
{
|
||||
final L2Summon pet = character.getPet();
|
||||
if (pet != null)
|
||||
{
|
||||
rc.addRelation(pet, relation, character.isAutoAttackable(player));
|
||||
}
|
||||
if (character.hasServitors())
|
||||
{
|
||||
character.getServitors().values().forEach(s -> rc.addRelation(s, relation, character.isAutoAttackable(player)));
|
||||
}
|
||||
}
|
||||
player.sendPacket(rc);
|
||||
character.getKnownList().getKnownRelations().put(player.getObjectId(), relation);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (NullPointerException e)
|
||||
{
|
||||
_log.log(Level.WARNING, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a packet to all L2PcInstance in the _KnownPlayers (in the specified radius) of the L2Character.<BR>
|
||||
* <B><U> Concept</U> :</B><BR>
|
||||
* L2PcInstance in the detection area of the L2Character are identified in <B>_knownPlayers</B>.<BR>
|
||||
* In order to inform other players of state modification on the L2Character, server just needs to go through _knownPlayers to send Server->Client Packet and check the distance between the targets.<BR>
|
||||
* <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T SEND Server->Client packet to this L2Character (to do this use method toSelfAndKnownPlayers)</B></FONT><BR>
|
||||
* @param character
|
||||
* @param mov
|
||||
* @param radius
|
||||
*/
|
||||
public static void toKnownPlayersInRadius(L2Character character, L2GameServerPacket mov, int radius)
|
||||
{
|
||||
if (radius < 0)
|
||||
{
|
||||
radius = 1500;
|
||||
}
|
||||
|
||||
final Collection<L2PcInstance> plrs = character.getKnownList().getKnownPlayers().values();
|
||||
for (L2PcInstance player : plrs)
|
||||
{
|
||||
if (character.isInsideRadius(player, radius, false, false))
|
||||
{
|
||||
player.sendPacket(mov);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a packet to all L2PcInstance in the _KnownPlayers of the L2Character and to the specified character.<BR>
|
||||
* <B><U> Concept</U> :</B><BR>
|
||||
* L2PcInstance in the detection area of the L2Character are identified in <B>_knownPlayers</B>.<BR>
|
||||
* In order to inform other players of state modification on the L2Character, server just need to go through _knownPlayers to send Server->Client Packet<BR>
|
||||
* @param character
|
||||
* @param mov
|
||||
*/
|
||||
public static void toSelfAndKnownPlayers(L2Character character, L2GameServerPacket mov)
|
||||
{
|
||||
if (character instanceof L2PcInstance)
|
||||
{
|
||||
character.sendPacket(mov);
|
||||
}
|
||||
|
||||
toKnownPlayers(character, mov);
|
||||
}
|
||||
|
||||
// To improve performance we are comparing values of radius^2 instead of calculating sqrt all the time
|
||||
public static void toSelfAndKnownPlayersInRadius(L2Character character, L2GameServerPacket mov, int radius)
|
||||
{
|
||||
if (radius < 0)
|
||||
{
|
||||
radius = 600;
|
||||
}
|
||||
|
||||
if (character instanceof L2PcInstance)
|
||||
{
|
||||
character.sendPacket(mov);
|
||||
}
|
||||
|
||||
final Collection<L2PcInstance> plrs = character.getKnownList().getKnownPlayers().values();
|
||||
for (L2PcInstance player : plrs)
|
||||
{
|
||||
if ((player != null) && Util.checkIfInRange(radius, character, player, false))
|
||||
{
|
||||
player.sendPacket(mov);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a packet to all L2PcInstance present in the world.<BR>
|
||||
* <B><U> Concept</U> :</B><BR>
|
||||
* In order to inform other players of state modification on the L2Character, server just need to go through _allPlayers to send Server->Client Packet<BR>
|
||||
* <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T SEND Server->Client packet to this L2Character (to do this use method toSelfAndKnownPlayers)</B></FONT><BR>
|
||||
* @param packet
|
||||
*/
|
||||
public static void toAllOnlinePlayers(L2GameServerPacket packet)
|
||||
{
|
||||
for (L2PcInstance player : L2World.getInstance().getPlayers())
|
||||
{
|
||||
if (player.isOnline())
|
||||
{
|
||||
player.sendPacket(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void toAllOnlinePlayers(String text)
|
||||
{
|
||||
toAllOnlinePlayers(text, false);
|
||||
}
|
||||
|
||||
public static void toAllOnlinePlayers(String text, boolean isCritical)
|
||||
{
|
||||
toAllOnlinePlayers(new CreatureSay(0, isCritical ? ChatType.CRITICAL_ANNOUNCE : ChatType.ANNOUNCEMENT, "", text));
|
||||
}
|
||||
|
||||
public static void toPlayersInInstance(L2GameServerPacket packet, int instanceId)
|
||||
{
|
||||
for (L2PcInstance player : L2World.getInstance().getPlayers())
|
||||
{
|
||||
if (player.isOnline() && (player.getInstanceId() == instanceId))
|
||||
{
|
||||
player.sendPacket(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void toAllOnlinePlayersOnScreen(String text)
|
||||
{
|
||||
toAllOnlinePlayers(new ExShowScreenMessage(text, 10000));
|
||||
}
|
||||
}
|
332
trunk/java/com/l2jmobius/gameserver/util/Evolve.java
Normal file
332
trunk/java/com/l2jmobius/gameserver/util/Evolve.java
Normal file
@ -0,0 +1,332 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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.l2jmobius.gameserver.util;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
import com.l2jmobius.commons.database.pool.impl.ConnectionFactory;
|
||||
import com.l2jmobius.gameserver.ThreadPoolManager;
|
||||
import com.l2jmobius.gameserver.data.xml.impl.NpcData;
|
||||
import com.l2jmobius.gameserver.data.xml.impl.PetDataTable;
|
||||
import com.l2jmobius.gameserver.model.L2PetData;
|
||||
import com.l2jmobius.gameserver.model.L2World;
|
||||
import com.l2jmobius.gameserver.model.actor.L2Npc;
|
||||
import com.l2jmobius.gameserver.model.actor.L2Summon;
|
||||
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
|
||||
import com.l2jmobius.gameserver.model.actor.instance.L2PetInstance;
|
||||
import com.l2jmobius.gameserver.model.actor.templates.L2NpcTemplate;
|
||||
import com.l2jmobius.gameserver.model.items.instance.L2ItemInstance;
|
||||
import com.l2jmobius.gameserver.network.SystemMessageId;
|
||||
import com.l2jmobius.gameserver.network.serverpackets.ExAdenaInvenCount;
|
||||
import com.l2jmobius.gameserver.network.serverpackets.ExUserInfoInvenWeight;
|
||||
import com.l2jmobius.gameserver.network.serverpackets.InventoryUpdate;
|
||||
import com.l2jmobius.gameserver.network.serverpackets.MagicSkillLaunched;
|
||||
import com.l2jmobius.gameserver.network.serverpackets.MagicSkillUse;
|
||||
import com.l2jmobius.gameserver.network.serverpackets.SystemMessage;
|
||||
|
||||
/**
|
||||
* UnAfraid: TODO: MOVE IT TO DP AI
|
||||
*/
|
||||
public final class Evolve
|
||||
{
|
||||
protected static final Logger _log = Logger.getLogger(Evolve.class.getName());
|
||||
|
||||
public static final boolean doEvolve(L2PcInstance player, L2Npc npc, int itemIdtake, int itemIdgive, int petminlvl)
|
||||
{
|
||||
if ((itemIdtake == 0) || (itemIdgive == 0) || (petminlvl == 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final L2Summon pet = player.getPet();
|
||||
if (pet == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final L2PetInstance currentPet = (L2PetInstance) pet;
|
||||
if (currentPet.isAlikeDead())
|
||||
{
|
||||
Util.handleIllegalPlayerAction(player, "Player " + player.getName() + " tried to use death pet exploit!", Config.DEFAULT_PUNISH);
|
||||
return false;
|
||||
}
|
||||
|
||||
L2ItemInstance item = null;
|
||||
long petexp = currentPet.getStat().getExp();
|
||||
final String oldname = currentPet.getName();
|
||||
final int oldX = currentPet.getX();
|
||||
final int oldY = currentPet.getY();
|
||||
final int oldZ = currentPet.getZ();
|
||||
|
||||
final L2PetData oldData = PetDataTable.getInstance().getPetDataByItemId(itemIdtake);
|
||||
|
||||
if (oldData == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final int oldnpcID = oldData.getNpcId();
|
||||
|
||||
if ((currentPet.getStat().getLevel() < petminlvl) || (currentPet.getId() != oldnpcID))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final L2PetData petData = PetDataTable.getInstance().getPetDataByItemId(itemIdgive);
|
||||
|
||||
if (petData == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final int npcID = petData.getNpcId();
|
||||
|
||||
if (npcID == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final L2NpcTemplate npcTemplate = NpcData.getInstance().getTemplate(npcID);
|
||||
|
||||
currentPet.unSummon(player);
|
||||
|
||||
// deleting old pet item
|
||||
currentPet.destroyControlItem(player, true);
|
||||
|
||||
item = player.getInventory().addItem("Evolve", itemIdgive, 1, player, npc);
|
||||
|
||||
// Summoning new pet
|
||||
final L2PetInstance petSummon = L2PetInstance.spawnPet(npcTemplate, player, item);
|
||||
|
||||
if (petSummon == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fix for non-linear baby pet exp
|
||||
final long _minimumexp = petSummon.getStat().getExpForLevel(petminlvl);
|
||||
if (petexp < _minimumexp)
|
||||
{
|
||||
petexp = _minimumexp;
|
||||
}
|
||||
|
||||
petSummon.getStat().addExp(petexp);
|
||||
petSummon.setCurrentHp(petSummon.getMaxHp());
|
||||
petSummon.setCurrentMp(petSummon.getMaxMp());
|
||||
petSummon.setCurrentFed(petSummon.getMaxFed());
|
||||
petSummon.setTitle(player.getName());
|
||||
petSummon.setName(oldname);
|
||||
petSummon.setRunning();
|
||||
petSummon.storeMe();
|
||||
|
||||
player.setPet(petSummon);
|
||||
|
||||
player.sendPacket(new MagicSkillUse(npc, 2046, 1, 1000, 600000));
|
||||
player.sendPacket(SystemMessageId.SUMMONING_YOUR_PET);
|
||||
// L2World.getInstance().storeObject(petSummon);
|
||||
petSummon.spawnMe(oldX, oldY, oldZ);
|
||||
petSummon.startFeed();
|
||||
item.setEnchantLevel(petSummon.getLevel());
|
||||
|
||||
ThreadPoolManager.getInstance().scheduleGeneral(new EvolveFinalizer(player, petSummon), 900);
|
||||
|
||||
if (petSummon.getCurrentFed() <= 0)
|
||||
{
|
||||
ThreadPoolManager.getInstance().scheduleGeneral(new EvolveFeedWait(player, petSummon), 60000);
|
||||
}
|
||||
else
|
||||
{
|
||||
petSummon.startFeed();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static final boolean doRestore(L2PcInstance player, L2Npc npc, int itemIdtake, int itemIdgive, int petminlvl)
|
||||
{
|
||||
if ((itemIdtake == 0) || (itemIdgive == 0) || (petminlvl == 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final L2ItemInstance item = player.getInventory().getItemByItemId(itemIdtake);
|
||||
if (item == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int oldpetlvl = item.getEnchantLevel();
|
||||
if (oldpetlvl < petminlvl)
|
||||
{
|
||||
oldpetlvl = petminlvl;
|
||||
}
|
||||
|
||||
final L2PetData oldData = PetDataTable.getInstance().getPetDataByItemId(itemIdtake);
|
||||
if (oldData == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final L2PetData petData = PetDataTable.getInstance().getPetDataByItemId(itemIdgive);
|
||||
if (petData == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final int npcId = petData.getNpcId();
|
||||
if (npcId == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final L2NpcTemplate npcTemplate = NpcData.getInstance().getTemplate(npcId);
|
||||
|
||||
// deleting old pet item
|
||||
final L2ItemInstance removedItem = player.getInventory().destroyItem("PetRestore", item, player, npc);
|
||||
final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED);
|
||||
sm.addItemName(removedItem);
|
||||
player.sendPacket(sm);
|
||||
|
||||
// Give new pet item
|
||||
final L2ItemInstance addedItem = player.getInventory().addItem("PetRestore", itemIdgive, 1, player, npc);
|
||||
|
||||
// Summoning new pet
|
||||
final L2PetInstance petSummon = L2PetInstance.spawnPet(npcTemplate, player, addedItem);
|
||||
if (petSummon == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final long _maxexp = petSummon.getStat().getExpForLevel(oldpetlvl);
|
||||
|
||||
petSummon.getStat().addExp(_maxexp);
|
||||
petSummon.setCurrentHp(petSummon.getMaxHp());
|
||||
petSummon.setCurrentMp(petSummon.getMaxMp());
|
||||
petSummon.setCurrentFed(petSummon.getMaxFed());
|
||||
petSummon.setTitle(player.getName());
|
||||
petSummon.setRunning();
|
||||
petSummon.storeMe();
|
||||
|
||||
player.setPet(petSummon);
|
||||
|
||||
player.sendPacket(new MagicSkillUse(npc, 2046, 1, 1000, 600000));
|
||||
player.sendPacket(SystemMessageId.SUMMONING_YOUR_PET);
|
||||
// L2World.getInstance().storeObject(petSummon);
|
||||
petSummon.spawnMe(player.getX(), player.getY(), player.getZ());
|
||||
petSummon.startFeed();
|
||||
addedItem.setEnchantLevel(petSummon.getLevel());
|
||||
|
||||
// Inventory update
|
||||
final InventoryUpdate iu = new InventoryUpdate();
|
||||
iu.addRemovedItem(removedItem);
|
||||
player.sendPacket(iu);
|
||||
|
||||
player.sendPacket(new ExUserInfoInvenWeight(player));
|
||||
player.sendPacket(new ExAdenaInvenCount(player));
|
||||
|
||||
player.broadcastUserInfo();
|
||||
|
||||
final L2World world = L2World.getInstance();
|
||||
world.removeObject(removedItem);
|
||||
|
||||
ThreadPoolManager.getInstance().scheduleGeneral(new EvolveFinalizer(player, petSummon), 900);
|
||||
|
||||
if (petSummon.getCurrentFed() <= 0)
|
||||
{
|
||||
ThreadPoolManager.getInstance().scheduleGeneral(new EvolveFeedWait(player, petSummon), 60000);
|
||||
}
|
||||
else
|
||||
{
|
||||
petSummon.startFeed();
|
||||
}
|
||||
|
||||
// pet control item no longer exists, delete the pet from the db
|
||||
try (Connection con = ConnectionFactory.getInstance().getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("DELETE FROM pets WHERE item_obj_id=?"))
|
||||
{
|
||||
ps.setInt(1, removedItem.getObjectId());
|
||||
ps.execute();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static final class EvolveFeedWait implements Runnable
|
||||
{
|
||||
private final L2PcInstance _activeChar;
|
||||
private final L2PetInstance _petSummon;
|
||||
|
||||
EvolveFeedWait(L2PcInstance activeChar, L2PetInstance petSummon)
|
||||
{
|
||||
_activeChar = activeChar;
|
||||
_petSummon = petSummon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_petSummon.getCurrentFed() <= 0)
|
||||
{
|
||||
_petSummon.unSummon(_activeChar);
|
||||
}
|
||||
else
|
||||
{
|
||||
_petSummon.startFeed();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_log.log(Level.WARNING, "", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static final class EvolveFinalizer implements Runnable
|
||||
{
|
||||
private final L2PcInstance _activeChar;
|
||||
private final L2PetInstance _petSummon;
|
||||
|
||||
EvolveFinalizer(L2PcInstance activeChar, L2PetInstance petSummon)
|
||||
{
|
||||
_activeChar = activeChar;
|
||||
_petSummon = petSummon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
_activeChar.sendPacket(new MagicSkillLaunched(_activeChar, 2046, 1));
|
||||
_petSummon.setFollowStatus(true);
|
||||
_petSummon.setShowSummonAnimation(false);
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
_log.log(Level.WARNING, "", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,242 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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.l2jmobius.gameserver.util;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.l2jmobius.gameserver.GameTimeController;
|
||||
import com.l2jmobius.gameserver.instancemanager.PunishmentManager;
|
||||
import com.l2jmobius.gameserver.model.PcCondOverride;
|
||||
import com.l2jmobius.gameserver.model.punishment.PunishmentAffect;
|
||||
import com.l2jmobius.gameserver.model.punishment.PunishmentTask;
|
||||
import com.l2jmobius.gameserver.model.punishment.PunishmentType;
|
||||
import com.l2jmobius.gameserver.network.L2GameClient;
|
||||
import com.l2jmobius.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Flood protector implementation.
|
||||
* @author fordfrog
|
||||
*/
|
||||
public final class FloodProtectorAction
|
||||
{
|
||||
/**
|
||||
* Logger
|
||||
*/
|
||||
private static final Logger _log = Logger.getLogger(FloodProtectorAction.class.getName());
|
||||
/**
|
||||
* Client for this instance of flood protector.
|
||||
*/
|
||||
private final L2GameClient _client;
|
||||
/**
|
||||
* Configuration of this instance of flood protector.
|
||||
*/
|
||||
private final FloodProtectorConfig _config;
|
||||
/**
|
||||
* Next game tick when new request is allowed.
|
||||
*/
|
||||
private volatile int _nextGameTick = GameTimeController.getInstance().getGameTicks();
|
||||
/**
|
||||
* Request counter.
|
||||
*/
|
||||
private final AtomicInteger _count = new AtomicInteger(0);
|
||||
/**
|
||||
* Flag determining whether exceeding request has been logged.
|
||||
*/
|
||||
private boolean _logged;
|
||||
/**
|
||||
* Flag determining whether punishment application is in progress so that we do not apply punisment multiple times (flooding).
|
||||
*/
|
||||
private volatile boolean _punishmentInProgress;
|
||||
|
||||
/**
|
||||
* Creates new instance of FloodProtectorAction.
|
||||
* @param client the game client for which flood protection is being created
|
||||
* @param config flood protector configuration
|
||||
*/
|
||||
public FloodProtectorAction(final L2GameClient client, final FloodProtectorConfig config)
|
||||
{
|
||||
super();
|
||||
_client = client;
|
||||
_config = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the request is flood protected or not.
|
||||
* @param command command issued or short command description
|
||||
* @return true if action is allowed, otherwise false
|
||||
*/
|
||||
public boolean tryPerformAction(final String command)
|
||||
{
|
||||
final int curTick = GameTimeController.getInstance().getGameTicks();
|
||||
|
||||
if ((_client.getActiveChar() != null) && _client.getActiveChar().canOverrideCond(PcCondOverride.FLOOD_CONDITIONS))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((curTick < _nextGameTick) || _punishmentInProgress)
|
||||
{
|
||||
if (_config.LOG_FLOODING && !_logged && _log.isLoggable(Level.WARNING))
|
||||
{
|
||||
log(" called command ", command, " ~", String.valueOf((_config.FLOOD_PROTECTION_INTERVAL - (_nextGameTick - curTick)) * GameTimeController.MILLIS_IN_TICK), " ms after previous command");
|
||||
_logged = true;
|
||||
}
|
||||
|
||||
_count.incrementAndGet();
|
||||
|
||||
if (!_punishmentInProgress && (_config.PUNISHMENT_LIMIT > 0) && (_count.get() >= _config.PUNISHMENT_LIMIT) && (_config.PUNISHMENT_TYPE != null))
|
||||
{
|
||||
_punishmentInProgress = true;
|
||||
|
||||
if ("kick".equals(_config.PUNISHMENT_TYPE))
|
||||
{
|
||||
kickPlayer();
|
||||
}
|
||||
else if ("ban".equals(_config.PUNISHMENT_TYPE))
|
||||
{
|
||||
banAccount();
|
||||
}
|
||||
else if ("jail".equals(_config.PUNISHMENT_TYPE))
|
||||
{
|
||||
jailChar();
|
||||
}
|
||||
|
||||
_punishmentInProgress = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_count.get() > 0)
|
||||
{
|
||||
if (_config.LOG_FLOODING && _log.isLoggable(Level.WARNING))
|
||||
{
|
||||
log(" issued ", String.valueOf(_count), " extra requests within ~", String.valueOf(_config.FLOOD_PROTECTION_INTERVAL * GameTimeController.MILLIS_IN_TICK), " ms");
|
||||
}
|
||||
}
|
||||
|
||||
_nextGameTick = curTick + _config.FLOOD_PROTECTION_INTERVAL;
|
||||
_logged = false;
|
||||
_count.set(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kick player from game (close network connection).
|
||||
*/
|
||||
private void kickPlayer()
|
||||
{
|
||||
if (_client.getActiveChar() != null)
|
||||
{
|
||||
_client.getActiveChar().logout(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
_client.closeNow();
|
||||
}
|
||||
|
||||
if (_log.isLoggable(Level.WARNING))
|
||||
{
|
||||
log("kicked for flooding");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bans char account and logs out the char.
|
||||
*/
|
||||
private void banAccount()
|
||||
{
|
||||
PunishmentManager.getInstance().startPunishment(new PunishmentTask(_client.getAccountName(), PunishmentAffect.ACCOUNT, PunishmentType.BAN, System.currentTimeMillis() + _config.PUNISHMENT_TIME, "", getClass().getSimpleName()));
|
||||
if (_log.isLoggable(Level.WARNING))
|
||||
{
|
||||
log(" banned for flooding ", _config.PUNISHMENT_TIME <= 0 ? "forever" : "for " + (_config.PUNISHMENT_TIME / 60000) + " mins");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Jails char.
|
||||
*/
|
||||
private void jailChar()
|
||||
{
|
||||
if (_client.getActiveChar() != null)
|
||||
{
|
||||
final int charId = _client.getActiveChar().getObjectId();
|
||||
if (charId > 0)
|
||||
{
|
||||
PunishmentManager.getInstance().startPunishment(new PunishmentTask(charId, PunishmentAffect.CHARACTER, PunishmentType.JAIL, System.currentTimeMillis() + _config.PUNISHMENT_TIME, "", getClass().getSimpleName()));
|
||||
}
|
||||
|
||||
if (_log.isLoggable(Level.WARNING))
|
||||
{
|
||||
log(" jailed for flooding ", _config.PUNISHMENT_TIME <= 0 ? "forever" : "for " + (_config.PUNISHMENT_TIME / 60000) + " mins");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void log(String... lines)
|
||||
{
|
||||
final StringBuilder output = StringUtil.startAppend(100, _config.FLOOD_PROTECTOR_TYPE, ": ");
|
||||
String address = null;
|
||||
try
|
||||
{
|
||||
if (!_client.isDetached())
|
||||
{
|
||||
address = _client.getConnection().getInetAddress().getHostAddress();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
|
||||
switch (_client.getState())
|
||||
{
|
||||
case IN_GAME:
|
||||
{
|
||||
if (_client.getActiveChar() != null)
|
||||
{
|
||||
StringUtil.append(output, _client.getActiveChar().getName());
|
||||
StringUtil.append(output, "(", String.valueOf(_client.getActiveChar().getObjectId()), ") ");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AUTHED:
|
||||
{
|
||||
if (_client.getAccountName() != null)
|
||||
{
|
||||
StringUtil.append(output, _client.getAccountName(), " ");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CONNECTED:
|
||||
{
|
||||
if (address != null)
|
||||
{
|
||||
StringUtil.append(output, address);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new IllegalStateException("Missing state on switch");
|
||||
}
|
||||
}
|
||||
|
||||
StringUtil.append(output, lines);
|
||||
_log.warning(output.toString());
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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.l2jmobius.gameserver.util;
|
||||
|
||||
/**
|
||||
* Flood protector configuration
|
||||
* @author fordfrog
|
||||
*/
|
||||
public final class FloodProtectorConfig
|
||||
{
|
||||
/**
|
||||
* Type used for identification of logging output.
|
||||
*/
|
||||
public String FLOOD_PROTECTOR_TYPE;
|
||||
/**
|
||||
* Flood protection interval in game ticks.
|
||||
*/
|
||||
public int FLOOD_PROTECTION_INTERVAL;
|
||||
/**
|
||||
* Whether flooding should be logged.
|
||||
*/
|
||||
public boolean LOG_FLOODING;
|
||||
/**
|
||||
* If specified punishment limit is exceeded, punishment is applied.
|
||||
*/
|
||||
public int PUNISHMENT_LIMIT;
|
||||
/**
|
||||
* Punishment type. Either 'none', 'kick', 'ban' or 'jail'.
|
||||
*/
|
||||
public String PUNISHMENT_TYPE;
|
||||
/**
|
||||
* For how long should the char/account be punished.
|
||||
*/
|
||||
public long PUNISHMENT_TIME;
|
||||
|
||||
/**
|
||||
* Creates new instance of FloodProtectorConfig.
|
||||
* @param floodProtectorType {@link #FLOOD_PROTECTOR_TYPE}
|
||||
*/
|
||||
public FloodProtectorConfig(final String floodProtectorType)
|
||||
{
|
||||
super();
|
||||
FLOOD_PROTECTOR_TYPE = floodProtectorType;
|
||||
}
|
||||
}
|
260
trunk/java/com/l2jmobius/gameserver/util/FloodProtectors.java
Normal file
260
trunk/java/com/l2jmobius/gameserver/util/FloodProtectors.java
Normal file
@ -0,0 +1,260 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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.l2jmobius.gameserver.util;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
import com.l2jmobius.gameserver.network.L2GameClient;
|
||||
|
||||
/**
|
||||
* Collection of flood protectors for single player.
|
||||
* @author fordfrog
|
||||
*/
|
||||
public final class FloodProtectors
|
||||
{
|
||||
/**
|
||||
* Use-item flood protector.
|
||||
*/
|
||||
private final FloodProtectorAction _useItem;
|
||||
/**
|
||||
* Roll-dice flood protector.
|
||||
*/
|
||||
private final FloodProtectorAction _rollDice;
|
||||
/**
|
||||
* Firework flood protector.
|
||||
*/
|
||||
private final FloodProtectorAction _firework;
|
||||
/**
|
||||
* Item-pet-summon flood protector.
|
||||
*/
|
||||
private final FloodProtectorAction _itemPetSummon;
|
||||
/**
|
||||
* Hero-voice flood protector.
|
||||
*/
|
||||
private final FloodProtectorAction _heroVoice;
|
||||
/**
|
||||
* Global-chat flood protector.
|
||||
*/
|
||||
private final FloodProtectorAction _globalChat;
|
||||
/**
|
||||
* Subclass flood protector.
|
||||
*/
|
||||
private final FloodProtectorAction _subclass;
|
||||
/**
|
||||
* Drop-item flood protector.
|
||||
*/
|
||||
private final FloodProtectorAction _dropItem;
|
||||
/**
|
||||
* Server-bypass flood protector.
|
||||
*/
|
||||
private final FloodProtectorAction _serverBypass;
|
||||
/**
|
||||
* Multisell flood protector.
|
||||
*/
|
||||
private final FloodProtectorAction _multiSell;
|
||||
/**
|
||||
* Transaction flood protector.
|
||||
*/
|
||||
private final FloodProtectorAction _transaction;
|
||||
/**
|
||||
* Manufacture flood protector.
|
||||
*/
|
||||
private final FloodProtectorAction _manufacture;
|
||||
/**
|
||||
* Manor flood protector.
|
||||
*/
|
||||
private final FloodProtectorAction _manor;
|
||||
/**
|
||||
* Send mail flood protector.
|
||||
*/
|
||||
private final FloodProtectorAction _sendMail;
|
||||
/**
|
||||
* Character Select protector
|
||||
*/
|
||||
private final FloodProtectorAction _characterSelect;
|
||||
/**
|
||||
* Item Auction
|
||||
*/
|
||||
private final FloodProtectorAction _itemAuction;
|
||||
|
||||
/**
|
||||
* Creates new instance of FloodProtectors.
|
||||
* @param client game client for which the collection of flood protectors is being created.
|
||||
*/
|
||||
public FloodProtectors(final L2GameClient client)
|
||||
{
|
||||
super();
|
||||
_useItem = new FloodProtectorAction(client, Config.FLOOD_PROTECTOR_USE_ITEM);
|
||||
_rollDice = new FloodProtectorAction(client, Config.FLOOD_PROTECTOR_ROLL_DICE);
|
||||
_firework = new FloodProtectorAction(client, Config.FLOOD_PROTECTOR_FIREWORK);
|
||||
_itemPetSummon = new FloodProtectorAction(client, Config.FLOOD_PROTECTOR_ITEM_PET_SUMMON);
|
||||
_heroVoice = new FloodProtectorAction(client, Config.FLOOD_PROTECTOR_HERO_VOICE);
|
||||
_globalChat = new FloodProtectorAction(client, Config.FLOOD_PROTECTOR_GLOBAL_CHAT);
|
||||
_subclass = new FloodProtectorAction(client, Config.FLOOD_PROTECTOR_SUBCLASS);
|
||||
_dropItem = new FloodProtectorAction(client, Config.FLOOD_PROTECTOR_DROP_ITEM);
|
||||
_serverBypass = new FloodProtectorAction(client, Config.FLOOD_PROTECTOR_SERVER_BYPASS);
|
||||
_multiSell = new FloodProtectorAction(client, Config.FLOOD_PROTECTOR_MULTISELL);
|
||||
_transaction = new FloodProtectorAction(client, Config.FLOOD_PROTECTOR_TRANSACTION);
|
||||
_manufacture = new FloodProtectorAction(client, Config.FLOOD_PROTECTOR_MANUFACTURE);
|
||||
_manor = new FloodProtectorAction(client, Config.FLOOD_PROTECTOR_MANOR);
|
||||
_sendMail = new FloodProtectorAction(client, Config.FLOOD_PROTECTOR_SENDMAIL);
|
||||
_characterSelect = new FloodProtectorAction(client, Config.FLOOD_PROTECTOR_CHARACTER_SELECT);
|
||||
_itemAuction = new FloodProtectorAction(client, Config.FLOOD_PROTECTOR_ITEM_AUCTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #_useItem}.
|
||||
* @return {@link #_useItem}
|
||||
*/
|
||||
public FloodProtectorAction getUseItem()
|
||||
{
|
||||
return _useItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #_rollDice}.
|
||||
* @return {@link #_rollDice}
|
||||
*/
|
||||
public FloodProtectorAction getRollDice()
|
||||
{
|
||||
return _rollDice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #_firework}.
|
||||
* @return {@link #_firework}
|
||||
*/
|
||||
public FloodProtectorAction getFirework()
|
||||
{
|
||||
return _firework;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #_itemPetSummon}.
|
||||
* @return {@link #_itemPetSummon}
|
||||
*/
|
||||
public FloodProtectorAction getItemPetSummon()
|
||||
{
|
||||
return _itemPetSummon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #_heroVoice}.
|
||||
* @return {@link #_heroVoice}
|
||||
*/
|
||||
public FloodProtectorAction getHeroVoice()
|
||||
{
|
||||
return _heroVoice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #_globalChat}.
|
||||
* @return {@link #_globalChat}
|
||||
*/
|
||||
public FloodProtectorAction getGlobalChat()
|
||||
{
|
||||
return _globalChat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #_subclass}.
|
||||
* @return {@link #_subclass}
|
||||
*/
|
||||
public FloodProtectorAction getSubclass()
|
||||
{
|
||||
return _subclass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #_dropItem}.
|
||||
* @return {@link #_dropItem}
|
||||
*/
|
||||
public FloodProtectorAction getDropItem()
|
||||
{
|
||||
return _dropItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #_serverBypass}.
|
||||
* @return {@link #_serverBypass}
|
||||
*/
|
||||
public FloodProtectorAction getServerBypass()
|
||||
{
|
||||
return _serverBypass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link #_multiSell}
|
||||
*/
|
||||
public FloodProtectorAction getMultiSell()
|
||||
{
|
||||
return _multiSell;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #_transaction}.
|
||||
* @return {@link #_transaction}
|
||||
*/
|
||||
public FloodProtectorAction getTransaction()
|
||||
{
|
||||
return _transaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #_manufacture}.
|
||||
* @return {@link #_manufacture}
|
||||
*/
|
||||
public FloodProtectorAction getManufacture()
|
||||
{
|
||||
return _manufacture;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #_manor}.
|
||||
* @return {@link #_manor}
|
||||
*/
|
||||
public FloodProtectorAction getManor()
|
||||
{
|
||||
return _manor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #_sendMail}.
|
||||
* @return {@link #_sendMail}
|
||||
*/
|
||||
public FloodProtectorAction getSendMail()
|
||||
{
|
||||
return _sendMail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #_characterSelect}.
|
||||
* @return {@link #_characterSelect}
|
||||
*/
|
||||
public FloodProtectorAction getCharacterSelect()
|
||||
{
|
||||
return _characterSelect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #_itemAuction}.
|
||||
* @return {@link #_itemAuction}
|
||||
*/
|
||||
public FloodProtectorAction getItemAuction()
|
||||
{
|
||||
return _itemAuction;
|
||||
}
|
||||
}
|
78
trunk/java/com/l2jmobius/gameserver/util/GMAudit.java
Normal file
78
trunk/java/com/l2jmobius/gameserver/util/GMAudit.java
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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.l2jmobius.gameserver.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
|
||||
/**
|
||||
* Audits Game Master's actions.
|
||||
*/
|
||||
public class GMAudit
|
||||
{
|
||||
private static final Logger _log = Logger.getLogger(GMAudit.class.getName());
|
||||
static
|
||||
{
|
||||
new File("log/GMAudit").mkdirs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a Game Master's action into a file.
|
||||
* @param gmName the Game Master's name
|
||||
* @param action the performed action
|
||||
* @param target the target's name
|
||||
* @param params the parameters
|
||||
*/
|
||||
public static void auditGMAction(String gmName, String action, String target, String params)
|
||||
{
|
||||
final SimpleDateFormat _formatter = new SimpleDateFormat("dd/MM/yyyy H:mm:ss");
|
||||
final String date = _formatter.format(new Date());
|
||||
String name = com.l2jmobius.util.Util.replaceIllegalCharacters(gmName);
|
||||
if (!com.l2jmobius.util.Util.isValidFileName(name))
|
||||
{
|
||||
name = "INVALID_GM_NAME_" + date;
|
||||
}
|
||||
|
||||
final File file = new File("log/GMAudit/" + name + ".txt");
|
||||
try (FileWriter save = new FileWriter(file, true))
|
||||
{
|
||||
save.write(date + ">" + gmName + ">" + action + ">" + target + ">" + params + Config.EOL);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
_log.log(Level.SEVERE, "GMAudit for GM " + gmName + " could not be saved: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper method.
|
||||
* @param gmName the Game Master's name
|
||||
* @param action the performed action
|
||||
* @param target the target's name
|
||||
*/
|
||||
public static void auditGMAction(String gmName, String action, String target)
|
||||
{
|
||||
auditGMAction(gmName, action, target, "");
|
||||
}
|
||||
}
|
233
trunk/java/com/l2jmobius/gameserver/util/GeoUtils.java
Normal file
233
trunk/java/com/l2jmobius/gameserver/util/GeoUtils.java
Normal file
@ -0,0 +1,233 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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.l2jmobius.gameserver.util;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import com.l2jmobius.commons.geodriver.Cell;
|
||||
import com.l2jmobius.gameserver.GeoData;
|
||||
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
|
||||
import com.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive;
|
||||
|
||||
/**
|
||||
* @author HorridoJoho
|
||||
*/
|
||||
public final class GeoUtils
|
||||
{
|
||||
public static void debug2DLine(L2PcInstance player, int x, int y, int tx, int ty, int z)
|
||||
{
|
||||
final int gx = GeoData.getInstance().getGeoX(x);
|
||||
final int gy = GeoData.getInstance().getGeoY(y);
|
||||
|
||||
final int tgx = GeoData.getInstance().getGeoX(tx);
|
||||
final int tgy = GeoData.getInstance().getGeoY(ty);
|
||||
|
||||
final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z);
|
||||
prim.addLine(Color.BLUE, GeoData.getInstance().getWorldX(gx), GeoData.getInstance().getWorldY(gy), z, GeoData.getInstance().getWorldX(tgx), GeoData.getInstance().getWorldY(tgy), z);
|
||||
|
||||
final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy);
|
||||
|
||||
while (iter.next())
|
||||
{
|
||||
final int wx = GeoData.getInstance().getWorldX(iter.x());
|
||||
final int wy = GeoData.getInstance().getWorldY(iter.y());
|
||||
|
||||
prim.addPoint(Color.RED, wx, wy, z);
|
||||
}
|
||||
player.sendPacket(prim);
|
||||
}
|
||||
|
||||
public static void debug3DLine(L2PcInstance player, int x, int y, int z, int tx, int ty, int tz)
|
||||
{
|
||||
final int gx = GeoData.getInstance().getGeoX(x);
|
||||
final int gy = GeoData.getInstance().getGeoY(y);
|
||||
|
||||
final int tgx = GeoData.getInstance().getGeoX(tx);
|
||||
final int tgy = GeoData.getInstance().getGeoY(ty);
|
||||
|
||||
final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z);
|
||||
prim.addLine(Color.BLUE, GeoData.getInstance().getWorldX(gx), GeoData.getInstance().getWorldY(gy), z, GeoData.getInstance().getWorldX(tgx), GeoData.getInstance().getWorldY(tgy), tz);
|
||||
|
||||
final LinePointIterator3D iter = new LinePointIterator3D(gx, gy, z, tgx, tgy, tz);
|
||||
iter.next();
|
||||
int prevX = iter.x();
|
||||
int prevY = iter.y();
|
||||
int wx = GeoData.getInstance().getWorldX(prevX);
|
||||
int wy = GeoData.getInstance().getWorldY(prevY);
|
||||
int wz = iter.z();
|
||||
prim.addPoint(Color.RED, wx, wy, wz);
|
||||
|
||||
while (iter.next())
|
||||
{
|
||||
final int curX = iter.x();
|
||||
final int curY = iter.y();
|
||||
|
||||
if ((curX != prevX) || (curY != prevY))
|
||||
{
|
||||
wx = GeoData.getInstance().getWorldX(curX);
|
||||
wy = GeoData.getInstance().getWorldY(curY);
|
||||
wz = iter.z();
|
||||
|
||||
prim.addPoint(Color.RED, wx, wy, wz);
|
||||
|
||||
prevX = curX;
|
||||
prevY = curY;
|
||||
}
|
||||
}
|
||||
player.sendPacket(prim);
|
||||
}
|
||||
|
||||
private static Color getDirectionColor(int x, int y, int z, int nswe)
|
||||
{
|
||||
if (GeoData.getInstance().checkNearestNswe(x, y, z, nswe))
|
||||
{
|
||||
return Color.GREEN;
|
||||
}
|
||||
return Color.RED;
|
||||
}
|
||||
|
||||
public static void debugGrid(L2PcInstance player)
|
||||
{
|
||||
final int geoRadius = 10;
|
||||
final int blocksPerPacket = 49;
|
||||
int iBlock = blocksPerPacket;
|
||||
int iPacket = 0;
|
||||
|
||||
ExServerPrimitive exsp = null;
|
||||
final GeoData gd = GeoData.getInstance();
|
||||
final int playerGx = gd.getGeoX(player.getX());
|
||||
final int playerGy = gd.getGeoY(player.getY());
|
||||
for (int dx = -geoRadius; dx <= geoRadius; ++dx)
|
||||
{
|
||||
for (int dy = -geoRadius; dy <= geoRadius; ++dy)
|
||||
{
|
||||
if (iBlock >= blocksPerPacket)
|
||||
{
|
||||
iBlock = 0;
|
||||
if (exsp != null)
|
||||
{
|
||||
++iPacket;
|
||||
player.sendPacket(exsp);
|
||||
}
|
||||
exsp = new ExServerPrimitive("DebugGrid_" + iPacket, player.getX(), player.getY(), -16000);
|
||||
}
|
||||
|
||||
if (exsp == null)
|
||||
{
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
final int gx = playerGx + dx;
|
||||
final int gy = playerGy + dy;
|
||||
|
||||
final int x = gd.getWorldX(gx);
|
||||
final int y = gd.getWorldY(gy);
|
||||
final int z = gd.getNearestZ(gx, gy, player.getZ());
|
||||
|
||||
// north arrow
|
||||
Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH);
|
||||
exsp.addLine(col, x - 1, y - 7, z, x + 1, y - 7, z);
|
||||
exsp.addLine(col, x - 2, y - 6, z, x + 2, y - 6, z);
|
||||
exsp.addLine(col, x - 3, y - 5, z, x + 3, y - 5, z);
|
||||
exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z);
|
||||
|
||||
// east arrow
|
||||
col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST);
|
||||
exsp.addLine(col, x + 7, y - 1, z, x + 7, y + 1, z);
|
||||
exsp.addLine(col, x + 6, y - 2, z, x + 6, y + 2, z);
|
||||
exsp.addLine(col, x + 5, y - 3, z, x + 5, y + 3, z);
|
||||
exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z);
|
||||
|
||||
// south arrow
|
||||
col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH);
|
||||
exsp.addLine(col, x - 1, y + 7, z, x + 1, y + 7, z);
|
||||
exsp.addLine(col, x - 2, y + 6, z, x + 2, y + 6, z);
|
||||
exsp.addLine(col, x - 3, y + 5, z, x + 3, y + 5, z);
|
||||
exsp.addLine(col, x - 4, y + 4, z, x + 4, y + 4, z);
|
||||
|
||||
col = getDirectionColor(gx, gy, z, Cell.NSWE_WEST);
|
||||
exsp.addLine(col, x - 7, y - 1, z, x - 7, y + 1, z);
|
||||
exsp.addLine(col, x - 6, y - 2, z, x - 6, y + 2, z);
|
||||
exsp.addLine(col, x - 5, y - 3, z, x - 5, y + 3, z);
|
||||
exsp.addLine(col, x - 4, y - 4, z, x - 4, y + 4, z);
|
||||
|
||||
++iBlock;
|
||||
}
|
||||
}
|
||||
|
||||
player.sendPacket(exsp);
|
||||
}
|
||||
|
||||
/**
|
||||
* difference between x values: never above 1<br>
|
||||
* difference between y values: never above 1
|
||||
* @param lastX
|
||||
* @param lastY
|
||||
* @param x
|
||||
* @param y
|
||||
* @return
|
||||
*/
|
||||
public static int computeNswe(int lastX, int lastY, int x, int y)
|
||||
{
|
||||
if (x > lastX) // east
|
||||
{
|
||||
if (y > lastY)
|
||||
{
|
||||
return Cell.NSWE_SOUTH_EAST;// Direction.SOUTH_EAST;
|
||||
}
|
||||
else if (y < lastY)
|
||||
{
|
||||
return Cell.NSWE_NORTH_EAST;// Direction.NORTH_EAST;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Cell.NSWE_EAST;// Direction.EAST;
|
||||
}
|
||||
}
|
||||
else if (x < lastX) // west
|
||||
{
|
||||
if (y > lastY)
|
||||
{
|
||||
return Cell.NSWE_SOUTH_WEST;// Direction.SOUTH_WEST;
|
||||
}
|
||||
else if (y < lastY)
|
||||
{
|
||||
return Cell.NSWE_NORTH_WEST;// Direction.NORTH_WEST;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Cell.NSWE_WEST;// Direction.WEST;
|
||||
}
|
||||
}
|
||||
else
|
||||
// unchanged x
|
||||
{
|
||||
if (y > lastY)
|
||||
{
|
||||
return Cell.NSWE_SOUTH;// Direction.SOUTH;
|
||||
}
|
||||
else if (y < lastY)
|
||||
{
|
||||
return Cell.NSWE_NORTH;// Direction.NORTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
247
trunk/java/com/l2jmobius/gameserver/util/HtmlUtil.java
Normal file
247
trunk/java/com/l2jmobius/gameserver/util/HtmlUtil.java
Normal file
@ -0,0 +1,247 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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.l2jmobius.gameserver.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.l2jmobius.gameserver.model.PageResult;
|
||||
import com.l2jmobius.util.StringUtil;
|
||||
|
||||
/**
|
||||
* A class containing useful methods for constructing HTML
|
||||
* @author NosBit
|
||||
*/
|
||||
public class HtmlUtil
|
||||
{
|
||||
/**
|
||||
* Gets the HTML representation of CP gauge.
|
||||
* @param width the width
|
||||
* @param current the current value
|
||||
* @param max the max value
|
||||
* @param displayAsPercentage if {@code true} the text in middle will be displayed as percent else it will be displayed as "current / max"
|
||||
* @return the HTML
|
||||
*/
|
||||
public static String getCpGauge(int width, long current, long max, boolean displayAsPercentage)
|
||||
{
|
||||
return getGauge(width, current, max, displayAsPercentage, "L2UI_CT1.Gauges.Gauge_DF_Large_CP_bg_Center", "L2UI_CT1.Gauges.Gauge_DF_Large_CP_Center", 17, -13);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the HTML representation of HP gauge.
|
||||
* @param width the width
|
||||
* @param current the current value
|
||||
* @param max the max value
|
||||
* @param displayAsPercentage if {@code true} the text in middle will be displayed as percent else it will be displayed as "current / max"
|
||||
* @return the HTML
|
||||
*/
|
||||
public static String getHpGauge(int width, long current, long max, boolean displayAsPercentage)
|
||||
{
|
||||
return getGauge(width, current, max, displayAsPercentage, "L2UI_CT1.Gauges.Gauge_DF_Large_HP_bg_Center", "L2UI_CT1.Gauges.Gauge_DF_Large_HP_Center", 17, -13);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the HTML representation of HP Warn gauge.
|
||||
* @param width the width
|
||||
* @param current the current value
|
||||
* @param max the max value
|
||||
* @param displayAsPercentage if {@code true} the text in middle will be displayed as percent else it will be displayed as "current / max"
|
||||
* @return the HTML
|
||||
*/
|
||||
public static String getHpWarnGauge(int width, long current, long max, boolean displayAsPercentage)
|
||||
{
|
||||
return getGauge(width, current, max, displayAsPercentage, "L2UI_CT1.Gauges.Gauge_DF_Large_HPWarn_bg_Center", "L2UI_CT1.Gauges.Gauge_DF_Large_HPWarn_Center", 17, -13);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the HTML representation of HP Fill gauge.
|
||||
* @param width the width
|
||||
* @param current the current value
|
||||
* @param max the max value
|
||||
* @param displayAsPercentage if {@code true} the text in middle will be displayed as percent else it will be displayed as "current / max"
|
||||
* @return the HTML
|
||||
*/
|
||||
public static String getHpFillGauge(int width, long current, long max, boolean displayAsPercentage)
|
||||
{
|
||||
return getGauge(width, current, max, displayAsPercentage, "L2UI_CT1.Gauges.Gauge_DF_Large_HPFill_bg_Center", "L2UI_CT1.Gauges.Gauge_DF_Large_HPFill_Center", 17, -13);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the HTML representation of MP Warn gauge.
|
||||
* @param width the width
|
||||
* @param current the current value
|
||||
* @param max the max value
|
||||
* @param displayAsPercentage if {@code true} the text in middle will be displayed as percent else it will be displayed as "current / max"
|
||||
* @return the HTML
|
||||
*/
|
||||
public static String getMpGauge(int width, long current, long max, boolean displayAsPercentage)
|
||||
{
|
||||
return getGauge(width, current, max, displayAsPercentage, "L2UI_CT1.Gauges.Gauge_DF_Large_MP_bg_Center", "L2UI_CT1.Gauges.Gauge_DF_Large_MP_Center", 17, -13);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the HTML representation of EXP Warn gauge.
|
||||
* @param width the width
|
||||
* @param current the current value
|
||||
* @param max the max value
|
||||
* @param displayAsPercentage if {@code true} the text in middle will be displayed as percent else it will be displayed as "current / max"
|
||||
* @return the HTML
|
||||
*/
|
||||
public static String getExpGauge(int width, long current, long max, boolean displayAsPercentage)
|
||||
{
|
||||
return getGauge(width, current, max, displayAsPercentage, "L2UI_CT1.Gauges.Gauge_DF_Large_EXP_bg_Center", "L2UI_CT1.Gauges.Gauge_DF_Large_EXP_Center", 17, -13);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the HTML representation of Food gauge.
|
||||
* @param width the width
|
||||
* @param current the current value
|
||||
* @param max the max value
|
||||
* @param displayAsPercentage if {@code true} the text in middle will be displayed as percent else it will be displayed as "current / max"
|
||||
* @return the HTML
|
||||
*/
|
||||
public static String getFoodGauge(int width, long current, long max, boolean displayAsPercentage)
|
||||
{
|
||||
return getGauge(width, current, max, displayAsPercentage, "L2UI_CT1.Gauges.Gauge_DF_Large_Food_Bg_Center", "L2UI_CT1.Gauges.Gauge_DF_Large_Food_Center", 17, -13);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the HTML representation of Weight gauge automatically changing level depending on current/max.
|
||||
* @param width the width
|
||||
* @param current the current value
|
||||
* @param max the max value
|
||||
* @param displayAsPercentage if {@code true} the text in middle will be displayed as percent else it will be displayed as "current / max"
|
||||
* @return the HTML
|
||||
*/
|
||||
public static String getWeightGauge(int width, long current, long max, boolean displayAsPercentage)
|
||||
{
|
||||
return getWeightGauge(width, current, max, displayAsPercentage, Util.map(current, 0, max, 1, 5));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the HTML representation of Weight gauge.
|
||||
* @param width the width
|
||||
* @param current the current value
|
||||
* @param max the max value
|
||||
* @param displayAsPercentage if {@code true} the text in middle will be displayed as percent else it will be displayed as "current / max"
|
||||
* @param level a number from 1 to 5 for the 5 different colors of weight gauge
|
||||
* @return the HTML
|
||||
*/
|
||||
public static String getWeightGauge(int width, long current, long max, boolean displayAsPercentage, long level)
|
||||
{
|
||||
return getGauge(width, current, max, displayAsPercentage, "L2UI_CT1.Gauges.Gauge_DF_Large_Weight_bg_Center" + level, "L2UI_CT1.Gauges.Gauge_DF_Large_Weight_Center" + level, 17, -13);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the HTML representation of a gauge.
|
||||
* @param width the width
|
||||
* @param current the current value
|
||||
* @param max the max value
|
||||
* @param displayAsPercentage if {@code true} the text in middle will be displayed as percent else it will be displayed as "current / max"
|
||||
* @param backgroundImage the background image
|
||||
* @param image the foreground image
|
||||
* @param imageHeight the image height
|
||||
* @param top the top adjustment
|
||||
* @return the HTML
|
||||
*/
|
||||
private static String getGauge(int width, long current, long max, boolean displayAsPercentage, String backgroundImage, String image, long imageHeight, long top)
|
||||
{
|
||||
current = Math.min(current, max);
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
StringUtil.append(sb, "<table width=", String.valueOf(width), " cellpadding=0 cellspacing=0><tr><td background=\"" + backgroundImage + "\">");
|
||||
StringUtil.append(sb, "<img src=\"" + image + "\" width=", String.valueOf((long) (((double) current / max) * width)), " height=", String.valueOf(imageHeight), ">");
|
||||
StringUtil.append(sb, "</td></tr><tr><td align=center><table cellpadding=0 cellspacing=", String.valueOf(top), "><tr><td>");
|
||||
if (displayAsPercentage)
|
||||
{
|
||||
StringUtil.append(sb, "<table cellpadding=0 cellspacing=2><tr><td>", String.format("%.2f%%", ((double) current / max) * 100), "</td></tr></table>");
|
||||
}
|
||||
else
|
||||
{
|
||||
final String tdWidth = String.valueOf((width - 10) / 2);
|
||||
StringUtil.append(sb, "<table cellpadding=0 cellspacing=0><tr><td width=" + tdWidth + " align=right>", String.valueOf(current), "</td>");
|
||||
StringUtil.append(sb, "<td width=10 align=center>/</td><td width=" + tdWidth + ">", String.valueOf(max), "</td></tr></table>");
|
||||
}
|
||||
StringUtil.append(sb, "</td></tr></table></td></tr></table>");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static <T> PageResult createPage(Collection<T> elements, int page, int elementsPerPage, Function<Integer, String> pagerFunction, Function<T, String> bodyFunction)
|
||||
{
|
||||
return createPage(elements, elements.size(), page, elementsPerPage, pagerFunction, bodyFunction);
|
||||
}
|
||||
|
||||
public static <T> PageResult createPage(T[] elements, int page, int elementsPerPage, Function<Integer, String> pagerFunction, Function<T, String> bodyFunction)
|
||||
{
|
||||
return createPage(Arrays.asList(elements), elements.length, page, elementsPerPage, pagerFunction, bodyFunction);
|
||||
}
|
||||
|
||||
public static <T> PageResult createPage(Iterable<T> elements, int size, int page, int elementsPerPage, Function<Integer, String> pagerFunction, Function<T, String> bodyFunction)
|
||||
{
|
||||
int pages = size / elementsPerPage;
|
||||
if ((elementsPerPage * pages) < size)
|
||||
{
|
||||
pages++;
|
||||
}
|
||||
|
||||
final StringBuilder pagerTemplate = new StringBuilder();
|
||||
if (pages > 1)
|
||||
{
|
||||
int breakit = 0;
|
||||
for (int i = 0; i < pages; i++)
|
||||
{
|
||||
pagerTemplate.append(pagerFunction.apply(i));
|
||||
breakit++;
|
||||
|
||||
if (breakit > 5)
|
||||
{
|
||||
pagerTemplate.append("</tr><tr>");
|
||||
breakit = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (page >= pages)
|
||||
{
|
||||
page = pages - 1;
|
||||
}
|
||||
|
||||
int start = 0;
|
||||
if (page > 0)
|
||||
{
|
||||
start = elementsPerPage * page;
|
||||
}
|
||||
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
int i = 0;
|
||||
for (T element : elements)
|
||||
{
|
||||
if (i++ < start)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
sb.append(bodyFunction.apply(element));
|
||||
|
||||
if (i >= (elementsPerPage + start))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return new PageResult(pages, pagerTemplate, sb);
|
||||
}
|
||||
}
|
113
trunk/java/com/l2jmobius/gameserver/util/LinePointIterator.java
Normal file
113
trunk/java/com/l2jmobius/gameserver/util/LinePointIterator.java
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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.l2jmobius.gameserver.util;
|
||||
|
||||
/**
|
||||
* @author HorridoJoho
|
||||
*/
|
||||
public final class LinePointIterator
|
||||
{
|
||||
// src is moved towards dst in next()
|
||||
private int _srcX;
|
||||
private int _srcY;
|
||||
private final int _dstX;
|
||||
private final int _dstY;
|
||||
|
||||
private final long _dx;
|
||||
private final long _dy;
|
||||
private final long _sx;
|
||||
private final long _sy;
|
||||
private long _error;
|
||||
|
||||
private boolean _first;
|
||||
|
||||
public LinePointIterator(int srcX, int srcY, int dstX, int dstY)
|
||||
{
|
||||
_srcX = srcX;
|
||||
_srcY = srcY;
|
||||
_dstX = dstX;
|
||||
_dstY = dstY;
|
||||
_dx = Math.abs((long) dstX - srcX);
|
||||
_dy = Math.abs((long) dstY - srcY);
|
||||
_sx = srcX < dstX ? 1 : -1;
|
||||
_sy = srcY < dstY ? 1 : -1;
|
||||
|
||||
if (_dx >= _dy)
|
||||
{
|
||||
_error = _dx / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
_error = _dy / 2;
|
||||
}
|
||||
|
||||
_first = true;
|
||||
}
|
||||
|
||||
public boolean next()
|
||||
{
|
||||
if (_first)
|
||||
{
|
||||
_first = false;
|
||||
return true;
|
||||
}
|
||||
else if (_dx >= _dy)
|
||||
{
|
||||
if (_srcX != _dstX)
|
||||
{
|
||||
_srcX += _sx;
|
||||
|
||||
_error += _dy;
|
||||
if (_error >= _dx)
|
||||
{
|
||||
_srcY += _sy;
|
||||
_error -= _dx;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_srcY != _dstY)
|
||||
{
|
||||
_srcY += _sy;
|
||||
|
||||
_error += _dx;
|
||||
if (_error >= _dy)
|
||||
{
|
||||
_srcX += _sx;
|
||||
_error -= _dy;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public int x()
|
||||
{
|
||||
return _srcX;
|
||||
}
|
||||
|
||||
public int y()
|
||||
{
|
||||
return _srcY;
|
||||
}
|
||||
}
|
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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.l2jmobius.gameserver.util;
|
||||
|
||||
/**
|
||||
* @author HorridoJoho
|
||||
*/
|
||||
public final class LinePointIterator3D
|
||||
{
|
||||
private int _srcX;
|
||||
private int _srcY;
|
||||
private int _srcZ;
|
||||
private final int _dstX;
|
||||
private final int _dstY;
|
||||
private final int _dstZ;
|
||||
private final long _dx;
|
||||
private final long _dy;
|
||||
private final long _dz;
|
||||
private final long _sx;
|
||||
private final long _sy;
|
||||
private final long _sz;
|
||||
private long _error;
|
||||
private long _error2;
|
||||
private boolean _first;
|
||||
|
||||
public LinePointIterator3D(int srcX, int srcY, int srcZ, int dstX, int dstY, int dstZ)
|
||||
{
|
||||
_srcX = srcX;
|
||||
_srcY = srcY;
|
||||
_srcZ = srcZ;
|
||||
_dstX = dstX;
|
||||
_dstY = dstY;
|
||||
_dstZ = dstZ;
|
||||
_dx = Math.abs((long) dstX - srcX);
|
||||
_dy = Math.abs((long) dstY - srcY);
|
||||
_dz = Math.abs((long) dstZ - srcZ);
|
||||
_sx = srcX < dstX ? 1 : -1;
|
||||
_sy = srcY < dstY ? 1 : -1;
|
||||
_sz = srcZ < dstZ ? 1 : -1;
|
||||
|
||||
if ((_dx >= _dy) && (_dx >= _dz))
|
||||
{
|
||||
_error = _error2 = _dx / 2;
|
||||
}
|
||||
else if ((_dy >= _dx) && (_dy >= _dz))
|
||||
{
|
||||
_error = _error2 = _dy / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
_error = _error2 = _dz / 2;
|
||||
}
|
||||
|
||||
_first = true;
|
||||
}
|
||||
|
||||
public boolean next()
|
||||
{
|
||||
if (_first)
|
||||
{
|
||||
_first = false;
|
||||
return true;
|
||||
}
|
||||
else if ((_dx >= _dy) && (_dx >= _dz))
|
||||
{
|
||||
if (_srcX != _dstX)
|
||||
{
|
||||
_srcX += _sx;
|
||||
|
||||
_error += _dy;
|
||||
if (_error >= _dx)
|
||||
{
|
||||
_srcY += _sy;
|
||||
_error -= _dx;
|
||||
}
|
||||
|
||||
_error2 += _dz;
|
||||
if (_error2 >= _dx)
|
||||
{
|
||||
_srcZ += _sz;
|
||||
_error2 -= _dx;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if ((_dy >= _dx) && (_dy >= _dz))
|
||||
{
|
||||
if (_srcY != _dstY)
|
||||
{
|
||||
_srcY += _sy;
|
||||
|
||||
_error += _dx;
|
||||
if (_error >= _dy)
|
||||
{
|
||||
_srcX += _sx;
|
||||
_error -= _dy;
|
||||
}
|
||||
|
||||
_error2 += _dz;
|
||||
if (_error2 >= _dy)
|
||||
{
|
||||
_srcZ += _sz;
|
||||
_error2 -= _dy;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_srcZ != _dstZ)
|
||||
{
|
||||
_srcZ += _sz;
|
||||
|
||||
_error += _dx;
|
||||
if (_error >= _dz)
|
||||
{
|
||||
_srcX += _sx;
|
||||
_error -= _dz;
|
||||
}
|
||||
|
||||
_error2 += _dy;
|
||||
if (_error2 >= _dz)
|
||||
{
|
||||
_srcY += _sy;
|
||||
_error2 -= _dz;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public int x()
|
||||
{
|
||||
return _srcX;
|
||||
}
|
||||
|
||||
public int y()
|
||||
{
|
||||
return _srcY;
|
||||
}
|
||||
|
||||
public int z()
|
||||
{
|
||||
return _srcZ;
|
||||
}
|
||||
}
|
414
trunk/java/com/l2jmobius/gameserver/util/MinionList.java
Normal file
414
trunk/java/com/l2jmobius/gameserver/util/MinionList.java
Normal file
@ -0,0 +1,414 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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.l2jmobius.gameserver.util;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
import com.l2jmobius.gameserver.ThreadPoolManager;
|
||||
import com.l2jmobius.gameserver.data.xml.impl.NpcData;
|
||||
import com.l2jmobius.gameserver.model.Location;
|
||||
import com.l2jmobius.gameserver.model.actor.L2Character;
|
||||
import com.l2jmobius.gameserver.model.actor.instance.L2MonsterInstance;
|
||||
import com.l2jmobius.gameserver.model.actor.templates.L2NpcTemplate;
|
||||
import com.l2jmobius.gameserver.model.holders.MinionHolder;
|
||||
import com.l2jmobius.gameserver.taskmanager.DecayTaskManager;
|
||||
import com.l2jmobius.util.Rnd;
|
||||
|
||||
/**
|
||||
* @author luisantonioa, DS
|
||||
*/
|
||||
public class MinionList
|
||||
{
|
||||
private static final Logger _log = Logger.getLogger(MinionList.class.getName());
|
||||
|
||||
protected final L2MonsterInstance _master;
|
||||
/** List containing the current spawned minions */
|
||||
private final List<L2MonsterInstance> _minionReferences = new CopyOnWriteArrayList<>();
|
||||
/** List containing the cached deleted minions for reuse */
|
||||
protected List<L2MonsterInstance> _reusedMinionReferences = null;
|
||||
|
||||
public MinionList(L2MonsterInstance pMaster)
|
||||
{
|
||||
if (pMaster == null)
|
||||
{
|
||||
throw new NullPointerException("MinionList: master is null");
|
||||
}
|
||||
_master = pMaster;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list of the spawned (alive) minions.
|
||||
*/
|
||||
public List<L2MonsterInstance> getSpawnedMinions()
|
||||
{
|
||||
return _minionReferences;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage the spawn of Minions.<BR>
|
||||
* <BR>
|
||||
* <B><U> Actions</U> :</B><BR>
|
||||
* <BR>
|
||||
* <li>Get the Minion data of all Minions that must be spawn</li>
|
||||
* <li>For each Minion type, spawn the amount of Minion needed</li><BR>
|
||||
* <BR>
|
||||
* @param minions
|
||||
*/
|
||||
public final void spawnMinions(final List<MinionHolder> minions)
|
||||
{
|
||||
if (_master.isAlikeDead())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// List<MinionHolder> minions = _master.getTemplate().getParameters().getMinionList("Privates");
|
||||
if (minions == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int minionCount, minionId;
|
||||
long minionsToSpawn;
|
||||
for (MinionHolder minion : minions)
|
||||
{
|
||||
minionCount = minion.getCount();
|
||||
minionId = minion.getId();
|
||||
|
||||
minionsToSpawn = minionCount - countSpawnedMinionsById(minionId);
|
||||
if (minionsToSpawn > 0)
|
||||
{
|
||||
for (int i = 0; i < minionsToSpawn; i++)
|
||||
{
|
||||
spawnMinion(minionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
// remove non-needed minions
|
||||
deleteReusedMinions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all spawned minions and try to reuse them.
|
||||
*/
|
||||
public void deleteSpawnedMinions()
|
||||
{
|
||||
if (!_minionReferences.isEmpty())
|
||||
{
|
||||
for (L2MonsterInstance minion : _minionReferences)
|
||||
{
|
||||
if (minion != null)
|
||||
{
|
||||
minion.setLeader(null);
|
||||
minion.deleteMe();
|
||||
if (_reusedMinionReferences != null)
|
||||
{
|
||||
_reusedMinionReferences.add(minion);
|
||||
}
|
||||
}
|
||||
}
|
||||
_minionReferences.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all reused minions to prevent memory leaks.
|
||||
*/
|
||||
public void deleteReusedMinions()
|
||||
{
|
||||
if (_reusedMinionReferences != null)
|
||||
{
|
||||
_reusedMinionReferences.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// hooks
|
||||
|
||||
/**
|
||||
* Called on the master spawn Old minions (from previous spawn) are deleted. If master can respawn - enabled reuse of the killed minions.
|
||||
*/
|
||||
public void onMasterSpawn()
|
||||
{
|
||||
deleteSpawnedMinions();
|
||||
|
||||
// if master has spawn and can respawn - try to reuse minions
|
||||
if ((_reusedMinionReferences == null) && (_master.getTemplate().getParameters().getSet().get("SummonPrivateRate") == null) && !_master.getTemplate().getParameters().getMinionList("Privates").isEmpty() && (_master.getSpawn() != null) && _master.getSpawn().isRespawnEnabled())
|
||||
{
|
||||
_reusedMinionReferences = new CopyOnWriteArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on the minion spawn and added them in the list of the spawned minions.
|
||||
* @param minion
|
||||
*/
|
||||
public void onMinionSpawn(L2MonsterInstance minion)
|
||||
{
|
||||
_minionReferences.add(minion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on the master death/delete.
|
||||
* @param force if true - force delete of the spawned minions By default minions deleted only for raidbosses
|
||||
*/
|
||||
public void onMasterDie(boolean force)
|
||||
{
|
||||
if (_master.isRaid() || force)
|
||||
{
|
||||
deleteSpawnedMinions();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on the minion death/delete. Removed minion from the list of the spawned minions and reuse if possible.
|
||||
* @param minion
|
||||
* @param respawnTime (ms) enable respawning of this minion while master is alive. -1 - use default value: 0 (disable) for mobs and config value for raids.
|
||||
*/
|
||||
public void onMinionDie(L2MonsterInstance minion, int respawnTime)
|
||||
{
|
||||
minion.setLeader(null); // prevent memory leaks
|
||||
_minionReferences.remove(minion);
|
||||
if (_reusedMinionReferences != null)
|
||||
{
|
||||
_reusedMinionReferences.add(minion);
|
||||
}
|
||||
|
||||
final int time = respawnTime < 0 ? _master.isRaid() ? (int) Config.RAID_MINION_RESPAWN_TIMER : 0 : respawnTime;
|
||||
if ((time > 0) && !_master.isAlikeDead())
|
||||
{
|
||||
ThreadPoolManager.getInstance().scheduleGeneral(new MinionRespawnTask(minion), time);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called if master/minion was attacked. Master and all free minions receive aggro against attacker.
|
||||
* @param caller
|
||||
* @param attacker
|
||||
*/
|
||||
public void onAssist(L2Character caller, L2Character attacker)
|
||||
{
|
||||
if (attacker == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_master.isAlikeDead() && !_master.isInCombat())
|
||||
{
|
||||
_master.addDamageHate(attacker, 0, 1);
|
||||
}
|
||||
|
||||
final boolean callerIsMaster = caller == _master;
|
||||
int aggro = callerIsMaster ? 10 : 1;
|
||||
if (_master.isRaid())
|
||||
{
|
||||
aggro *= 10;
|
||||
}
|
||||
|
||||
for (L2MonsterInstance minion : _minionReferences)
|
||||
{
|
||||
if ((minion != null) && !minion.isDead() && (callerIsMaster || !minion.isInCombat()))
|
||||
{
|
||||
minion.addDamageHate(attacker, 0, aggro);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from onTeleported() of the master Alive and able to move minions teleported to master.
|
||||
*/
|
||||
public void onMasterTeleported()
|
||||
{
|
||||
final int offset = 200;
|
||||
final int minRadius = (int) _master.getCollisionRadius() + 30;
|
||||
|
||||
for (L2MonsterInstance minion : _minionReferences)
|
||||
{
|
||||
if ((minion != null) && !minion.isDead() && !minion.isMovementDisabled())
|
||||
{
|
||||
int newX = Rnd.get(minRadius * 2, offset * 2); // x
|
||||
int newY = Rnd.get(newX, offset * 2); // distance
|
||||
newY = (int) Math.sqrt((newY * newY) - (newX * newX)); // y
|
||||
if (newX > (offset + minRadius))
|
||||
{
|
||||
newX = (_master.getX() + newX) - offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
newX = (_master.getX() - newX) + minRadius;
|
||||
}
|
||||
if (newY > (offset + minRadius))
|
||||
{
|
||||
newY = (_master.getY() + newY) - offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
newY = (_master.getY() - newY) + minRadius;
|
||||
}
|
||||
|
||||
minion.teleToLocation(new Location(newX, newY, _master.getZ()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final void spawnMinion(int minionId)
|
||||
{
|
||||
if (minionId == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// searching in reused minions
|
||||
if (_reusedMinionReferences != null)
|
||||
{
|
||||
final L2MonsterInstance minion = _reusedMinionReferences.stream().filter(m -> (m.getId() == minionId)).findFirst().orElse(null);
|
||||
if (minion != null)
|
||||
{
|
||||
DecayTaskManager.getInstance().cancel(minion);
|
||||
minion.onDecay();
|
||||
_reusedMinionReferences.remove(minion);
|
||||
minion.refreshID();
|
||||
initializeNpcInstance(_master, minion);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// not found in cache
|
||||
spawnMinion(_master, minionId);
|
||||
}
|
||||
|
||||
private final class MinionRespawnTask implements Runnable
|
||||
{
|
||||
private final L2MonsterInstance _minion;
|
||||
|
||||
public MinionRespawnTask(L2MonsterInstance minion)
|
||||
{
|
||||
_minion = minion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if (!_master.isAlikeDead() && _master.isVisible())
|
||||
{
|
||||
// minion can be already spawned or deleted
|
||||
if (!_minion.isVisible())
|
||||
{
|
||||
if (_reusedMinionReferences != null)
|
||||
{
|
||||
_reusedMinionReferences.remove(_minion);
|
||||
}
|
||||
|
||||
_minion.refreshID();
|
||||
initializeNpcInstance(_master, _minion);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init a Minion and add it in the world as a visible object.<BR>
|
||||
* <BR>
|
||||
* <B><U> Actions</U> :</B><BR>
|
||||
* <BR>
|
||||
* <li>Get the template of the Minion to spawn</li>
|
||||
* <li>Create and Init the Minion and generate its Identifier</li>
|
||||
* <li>Set the Minion HP, MP and Heading</li>
|
||||
* <li>Set the Minion leader to this RaidBoss</li>
|
||||
* <li>Init the position of the Minion and add it in the world as a visible object</li><BR>
|
||||
* <BR>
|
||||
* @param master L2MonsterInstance used as master for this minion
|
||||
* @param minionId The L2NpcTemplate Identifier of the Minion to spawn
|
||||
* @return
|
||||
*/
|
||||
public static final L2MonsterInstance spawnMinion(L2MonsterInstance master, int minionId)
|
||||
{
|
||||
// Get the template of the Minion to spawn
|
||||
final L2NpcTemplate minionTemplate = NpcData.getInstance().getTemplate(minionId);
|
||||
if (minionTemplate == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return initializeNpcInstance(master, new L2MonsterInstance(minionTemplate));
|
||||
}
|
||||
|
||||
protected static final L2MonsterInstance initializeNpcInstance(L2MonsterInstance master, L2MonsterInstance minion)
|
||||
{
|
||||
minion.stopAllEffects();
|
||||
minion.setIsDead(false);
|
||||
minion.setDecayed(false);
|
||||
|
||||
// Set the Minion HP, MP and Heading
|
||||
minion.setCurrentHpMp(minion.getMaxHp(), minion.getMaxMp());
|
||||
minion.setHeading(master.getHeading());
|
||||
|
||||
// Set the Minion leader to this RaidBoss
|
||||
minion.setLeader(master);
|
||||
|
||||
// move monster to masters instance
|
||||
minion.setInstanceId(master.getInstanceId());
|
||||
|
||||
// Init the position of the Minion and add it in the world as a visible object
|
||||
final int offset = 200;
|
||||
final int minRadius = (int) master.getCollisionRadius() + 30;
|
||||
|
||||
int newX = Rnd.get(minRadius * 2, offset * 2); // x
|
||||
int newY = Rnd.get(newX, offset * 2); // distance
|
||||
newY = (int) Math.sqrt((newY * newY) - (newX * newX)); // y
|
||||
if (newX > (offset + minRadius))
|
||||
{
|
||||
newX = (master.getX() + newX) - offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
newX = (master.getX() - newX) + minRadius;
|
||||
}
|
||||
if (newY > (offset + minRadius))
|
||||
{
|
||||
newY = (master.getY() + newY) - offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
newY = (master.getY() - newY) + minRadius;
|
||||
}
|
||||
|
||||
minion.spawnMe(newX, newY, master.getZ());
|
||||
|
||||
if (Config.DEBUG)
|
||||
{
|
||||
_log.info("Spawned minion template " + minion.getId() + " with objid: " + minion.getObjectId() + " to boss " + master.getObjectId() + " ,at: " + minion.getX() + " x, " + minion.getY() + " y, " + minion.getZ() + " z");
|
||||
}
|
||||
|
||||
return minion;
|
||||
}
|
||||
|
||||
// Statistics part
|
||||
|
||||
private final long countSpawnedMinionsById(int minionId)
|
||||
{
|
||||
return _minionReferences.stream().filter(npc -> npc.getId() == minionId).count();
|
||||
}
|
||||
|
||||
public final int countSpawnedMinions()
|
||||
{
|
||||
return _minionReferences.size();
|
||||
}
|
||||
|
||||
public final long lazyCountSpawnedMinionsGroups()
|
||||
{
|
||||
return _minionReferences.stream().map(L2MonsterInstance::getId).distinct().count();
|
||||
}
|
||||
}
|
1018
trunk/java/com/l2jmobius/gameserver/util/Util.java
Normal file
1018
trunk/java/com/l2jmobius/gameserver/util/Util.java
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user