actual freya branch

This commit is contained in:
2026-09-12 15:41:31 +00:00
parent 8049e0a882
commit 68c1cd27d4
3510 changed files with 336689 additions and 302906 deletions
@@ -0,0 +1,256 @@
/*
* $Header: Broadcast.java, 18/11/2005 15:33:35 luisantonioa Exp $
*
* $Author: luisantonioa $
* $Date: 18/11/2005 15:33:35 $
* $Revision: 1 $
* $Log: Broadcast.java,v $
* Revision 1 18/11/2005 15:33:35 luisantonioa
* Added copyright notice
*
*
* 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.l2jserver.gameserver.util;
import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jserver.Config;
import com.l2jserver.gameserver.model.L2World;
import com.l2jserver.gameserver.model.actor.L2Character;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.network.clientpackets.Say2;
import com.l2jserver.gameserver.network.serverpackets.CharInfo;
import com.l2jserver.gameserver.network.serverpackets.CreatureSay;
import com.l2jserver.gameserver.network.serverpackets.L2GameServerPacket;
import com.l2jserver.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 targetted.<BR>
* <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>
* <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>
* <BR>
*/
public static void toPlayersTargettingMyself(L2Character character, L2GameServerPacket mov)
{
if (Config.DEBUG)
{
_log.fine("players to notify:" + character.getKnownList().getKnownPlayers().size() + " packet:" + mov.getType());
}
Collection<L2PcInstance> plrs = character.getKnownList().getKnownPlayers().values();
// synchronized (character.getKnownList().getKnownPlayers())
{
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>
* <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>
* <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>
* <BR>
*/
public static void toKnownPlayers(L2Character character, L2GameServerPacket mov)
{
if (Config.DEBUG)
{
_log.fine("players to notify:" + character.getKnownList().getKnownPlayers().size() + " packet:" + mov.getType());
}
Collection<L2PcInstance> plrs = character.getKnownList().getKnownPlayers().values();
// synchronized (character.getKnownList().getKnownPlayers())
{
for (L2PcInstance player : plrs)
{
if (player == null)
{
continue;
}
try
{
player.sendPacket(mov);
if ((mov instanceof CharInfo) && (character instanceof L2PcInstance))
{
int relation = ((L2PcInstance) character).getRelation(player);
Integer oldrelation = character.getKnownList().getKnownRelations().get(player.getObjectId());
if ((oldrelation != null) && (oldrelation != relation))
{
player.sendPacket(new RelationChanged((L2PcInstance) character, relation, character.isAutoAttackable(player)));
if (((L2PcInstance) character).getPet() != null)
{
player.sendPacket(new RelationChanged(((L2PcInstance) character).getPet(), relation, character.isAutoAttackable(player)));
}
}
}
}
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>
* <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>
* <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>
* <BR>
*/
public static void toKnownPlayersInRadius(L2Character character, L2GameServerPacket mov, int radius)
{
if (radius < 0)
{
radius = 1500;
}
Collection<L2PcInstance> plrs = character.getKnownList().getKnownPlayers().values();
// synchronized (character.getKnownList().getKnownPlayers())
{
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>
* <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>
* <BR>
*/
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, long radiusSq)
{
if (radiusSq < 0)
{
radiusSq = 360000;
}
if (character instanceof L2PcInstance)
{
character.sendPacket(mov);
}
Collection<L2PcInstance> plrs = character.getKnownList().getKnownPlayers().values();
// synchronized (character.getKnownList().getKnownPlayers())
{
for (L2PcInstance player : plrs)
{
if ((player != null) && (character.getDistanceSq(player) <= radiusSq))
{
player.sendPacket(mov);
}
}
}
}
/**
* Send a packet to all L2PcInstance present in the world.<BR>
* <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>
* <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>
* <BR>
*/
public static void toAllOnlinePlayers(L2GameServerPacket mov)
{
if (Config.DEBUG)
{
_log.fine("Players to notify: " + L2World.getInstance().getAllPlayersCount() + " (with packet " + mov.getType() + ")");
}
Collection<L2PcInstance> pls = L2World.getInstance().getAllPlayers().values();
// synchronized (L2World.getInstance().getAllPlayers())
{
for (L2PcInstance onlinePlayer : pls)
{
if ((onlinePlayer != null) && onlinePlayer.isOnline())
{
onlinePlayer.sendPacket(mov);
}
}
}
}
public static void announceToOnlinePlayers(String text)
{
CreatureSay cs = new CreatureSay(0, Say2.ANNOUNCEMENT, "", text);
toAllOnlinePlayers(cs);
}
public static void toPlayersInInstance(L2GameServerPacket mov, int instanceId)
{
Collection<L2PcInstance> pls = L2World.getInstance().getAllPlayers().values();
// synchronized (character.getKnownList().getKnownPlayers())
{
for (L2PcInstance onlinePlayer : pls)
{
if ((onlinePlayer != null) && onlinePlayer.isOnline() && (onlinePlayer.getInstanceId() == instanceId))
{
onlinePlayer.sendPacket(mov);
}
}
}
}
}
@@ -0,0 +1,335 @@
/*
* 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.l2jserver.gameserver.util;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jserver.Config;
import com.l2jserver.L2DatabaseFactory;
import com.l2jserver.gameserver.ThreadPoolManager;
import com.l2jserver.gameserver.datatables.NpcTable;
import com.l2jserver.gameserver.datatables.SummonItemsData;
import com.l2jserver.gameserver.model.L2ItemInstance;
import com.l2jserver.gameserver.model.L2SummonItem;
import com.l2jserver.gameserver.model.L2World;
import com.l2jserver.gameserver.model.actor.L2Npc;
import com.l2jserver.gameserver.model.actor.L2Summon;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.actor.instance.L2PetInstance;
import com.l2jserver.gameserver.network.SystemMessageId;
import com.l2jserver.gameserver.network.serverpackets.InventoryUpdate;
import com.l2jserver.gameserver.network.serverpackets.MagicSkillLaunched;
import com.l2jserver.gameserver.network.serverpackets.MagicSkillUse;
import com.l2jserver.gameserver.network.serverpackets.StatusUpdate;
import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
import com.l2jserver.gameserver.templates.chars.L2NpcTemplate;
public final class Evolve
{
public 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;
}
L2Summon summon = player.getPet();
if ((summon == null) || !(summon instanceof L2PetInstance))
{
return false;
}
L2PetInstance currentPet = (L2PetInstance) summon;
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();
String oldname = currentPet.getName();
int oldX = currentPet.getX();
int oldY = currentPet.getY();
int oldZ = currentPet.getZ();
L2SummonItem olditem = SummonItemsData.getInstance().getSummonItem(itemIdtake);
if (olditem == null)
{
return false;
}
int oldnpcID = olditem.getNpcId();
if ((currentPet.getStat().getLevel() < petminlvl) || (currentPet.getNpcId() != oldnpcID))
{
return false;
}
L2SummonItem sitem = SummonItemsData.getInstance().getSummonItem(itemIdgive);
if (sitem == null)
{
return false;
}
int npcID = sitem.getNpcId();
if (npcID == 0)
{
return false;
}
L2NpcTemplate npcTemplate = NpcTable.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
L2PetInstance petSummon = L2PetInstance.spawnPet(npcTemplate, player, item);
if (petSummon == null)
{
return false;
}
// Fix for non-linear baby pet exp
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.store();
player.setPet(petSummon);
player.sendPacket(new MagicSkillUse(npc, 2046, 1, 1000, 600000));
player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.SUMMON_A_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;
}
L2ItemInstance item = player.getInventory().getItemByItemId(itemIdtake);
if (item == null)
{
return false;
}
int oldpetlvl = item.getEnchantLevel();
if (oldpetlvl < petminlvl)
{
oldpetlvl = petminlvl;
}
L2SummonItem oldItem = SummonItemsData.getInstance().getSummonItem(itemIdtake);
if (oldItem == null)
{
return false;
}
L2SummonItem sItem = SummonItemsData.getInstance().getSummonItem(itemIdgive);
if (sItem == null)
{
return false;
}
int npcId = sItem.getNpcId();
if (npcId == 0)
{
return false;
}
L2NpcTemplate npcTemplate = NpcTable.getInstance().getTemplate(npcId);
// deleting old pet item
L2ItemInstance removedItem = player.getInventory().destroyItem("PetRestore", item, player, npc);
player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED).addItemName(removedItem));
// Give new pet item
L2ItemInstance addedItem = player.getInventory().addItem("PetRestore", itemIdgive, 1, player, npc);
// Summoning new pet
L2PetInstance petSummon = L2PetInstance.spawnPet(npcTemplate, player, addedItem);
if (petSummon == null)
{
return false;
}
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.store();
player.setPet(petSummon);
player.sendPacket(new MagicSkillUse(npc, 2046, 1, 1000, 600000));
player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.SUMMON_A_PET));
// L2World.getInstance().storeObject(petSummon);
petSummon.spawnMe(player.getX(), player.getY(), player.getZ());
petSummon.startFeed();
addedItem.setEnchantLevel(petSummon.getLevel());
// Inventory update
InventoryUpdate iu = new InventoryUpdate();
iu.addRemovedItem(removedItem);
player.sendPacket(iu);
StatusUpdate su = new StatusUpdate(player);
su.addAttribute(StatusUpdate.CUR_LOAD, player.getCurrentLoad());
player.sendPacket(su);
player.broadcastUserInfo();
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
Connection con = null;
try
{
con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement statement = con.prepareStatement("DELETE FROM pets WHERE item_obj_id=?");
statement.setInt(1, removedItem.getObjectId());
statement.execute();
statement.close();
}
catch (Exception e)
{
}
finally
{
L2DatabaseFactory.close(con);
}
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,44 @@
/*
* $HeadURL: $
*
* $Author: $
* $Date: $
* $Revision: $
*
*
* 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.l2jserver.gameserver.util;
/**
* This interface can be implemented by extensions to register simple functions with the DynamicExtension handler It's in the responsibility of the extensions to interpret the get and set functions
* @version $Revision: $ $Date: $
* @author Galun
*/
public interface ExtensionFunction
{
/**
* get an object identified with a name (should have a human readable output with toString())
* @param name the name of an object or a result of a function
* @return the object
*/
public Object get(String name);
/**
* set the named object to the new value supplied in obj
* @param name the name of the object
* @param obj the new value
*/
public void set(String name, Object obj);
}
@@ -0,0 +1,232 @@
/*
* 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.l2jserver.gameserver.util;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jserver.gameserver.GameTimeController;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.network.L2GameClient;
import com.l2jserver.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.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 player player 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.getGameTicks();
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()
{
if (_client.getActiveChar() != null)
{
_client.getActiveChar().setPunishLevel(L2PcInstance.PunishLevel.ACC, _config.PUNISHMENT_TIME);
if (_log.isLoggable(Level.WARNING))
{
log(" banned for flooding ", _config.PUNISHMENT_TIME <= 0 ? "forever" : "for " + _config.PUNISHMENT_TIME + " mins");
}
_client.getActiveChar().logout();
}
else
{
log(" unable to ban account: no active player");
}
}
/**
* Jails char.
*/
private void jailChar()
{
if (_client.getActiveChar() != null)
{
_client.getActiveChar().setPunishLevel(L2PcInstance.PunishLevel.JAIL, _config.PUNISHMENT_TIME);
if (_log.isLoggable(Level.WARNING))
{
log(" jailed for flooding ", _config.PUNISHMENT_TIME <= 0 ? "forever" : "for " + _config.PUNISHMENT_TIME + " mins");
}
}
else
{
log(" unable to jail: no active player");
}
}
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()), ") ");
}
case AUTHED:
if (_client.getAccountName() != null)
{
StringUtil.append(output, _client.getAccountName(), " ");
}
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,56 @@
/*
* 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.l2jserver.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 int PUNISHMENT_TIME;
/**
* Creates new instance of FloodProtectorConfig.
* @param floodProtectorType {@link #FLOOD_PROTECTOR_TYPE}
*/
public FloodProtectorConfig(final String floodProtectorType)
{
super();
FLOOD_PROTECTOR_TYPE = floodProtectorType;
}
}
@@ -0,0 +1,258 @@
/*
* 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.l2jserver.gameserver.util;
import com.l2jserver.Config;
import com.l2jserver.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 player player 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;
}
/**
* Returns {@link #_multisell}.
* @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;
}
}
@@ -0,0 +1,70 @@
/*
* 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.l2jserver.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.l2jserver.util.lib.Log;
public class GMAudit
{
static
{
new File("log/GMAudit").mkdirs();
}
private static final Logger _log = Logger.getLogger(Log.class.getName());
private static final SimpleDateFormat _formatter = new SimpleDateFormat("dd/MM/yyyy H:mm:ss");
public static void auditGMAction(String gmName, String action, String target, String params)
{
String today = _formatter.format(new Date());
FileWriter save = null;
try
{
File file = new File("log/GMAudit/" + gmName + ".txt");
save = new FileWriter(file, true);
String out = (today + ">" + gmName + ">" + action + ">" + target + ">" + params + "\r\n");
save.write(out);
}
catch (IOException e)
{
_log.log(Level.SEVERE, "GMAudit for GM " + gmName + " could not be saved: ", e);
}
finally
{
try
{
save.close();
}
catch (Exception e)
{
}
}
}
public static void auditGMAction(String gmName, String action, String target)
{
auditGMAction(gmName, action, target, "");
}
}
@@ -0,0 +1,101 @@
/*
* $Header: IllegalPlayerAction.java, 21/10/2005 23:32:02 luisantonioa Exp $
*
* $Author: luisantonioa $ $Date: 21/10/2005 23:32:02 $ $Revision: 1 $ $Log:
* IllegalPlayerAction.java,v $ Revision 1 21/10/2005 23:32:02 luisantonioa
* Added copyright notice
*
*
* 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.l2jserver.gameserver.util;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import com.l2jserver.Config;
import com.l2jserver.gameserver.GmListTable;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
/**
* This class ...
* @version $Revision: 1.2 $ $Date: 2004/06/27 08:12:59 $
*/
public final class IllegalPlayerAction implements Runnable
{
private static Logger _logAudit = Logger.getLogger("audit");
private final String _message;
private final int _punishment;
private final L2PcInstance _actor;
public static final int PUNISH_BROADCAST = 1;
public static final int PUNISH_KICK = 2;
public static final int PUNISH_KICKBAN = 3;
public static final int PUNISH_JAIL = 4;
public IllegalPlayerAction(L2PcInstance actor, String message, int punishment)
{
_message = message;
_punishment = punishment;
_actor = actor;
switch (punishment)
{
case PUNISH_KICK:
_actor.sendMessage("You will be kicked for illegal action, GM informed.");
break;
case PUNISH_KICKBAN:
_actor.setAccessLevel(-100);
_actor.setAccountAccesslevel(-100);
_actor.sendMessage("You are banned for illegal action, GM informed.");
break;
case PUNISH_JAIL:
_actor.sendMessage("Illegal action performed!");
_actor.sendMessage("You will be teleported to GM Consultation Service area and jailed.");
break;
}
}
@Override
public void run()
{
LogRecord record = new LogRecord(Level.INFO, "AUDIT:" + _message);
record.setLoggerName("audit");
record.setParameters(new Object[]
{
_actor,
_punishment
});
_logAudit.log(record);
GmListTable.broadcastMessageToGMs(_message);
switch (_punishment)
{
case PUNISH_BROADCAST:
return;
case PUNISH_KICK:
_actor.logout(false);
break;
case PUNISH_KICKBAN:
_actor.logout();
break;
case PUNISH_JAIL:
_actor.setPunishLevel(L2PcInstance.PunishLevel.JAIL, Config.DEFAULT_PUNISH_PARAM);
break;
}
}
}
@@ -0,0 +1,90 @@
/*
* 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.l2jserver.gameserver.util;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* This is a class loader for the dynamic extensions used by DynamicExtension class.
* @version $Revision: $ $Date: $
* @author galun
*/
public class JarClassLoader extends ClassLoader
{
private static Logger _log = Logger.getLogger(JarClassLoader.class.getCanonicalName());
HashSet<String> _jars = new HashSet<>();
public void addJarFile(String filename)
{
_jars.add(filename);
}
@Override
public Class<?> findClass(String name) throws ClassNotFoundException
{
try
{
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
}
catch (Exception e)
{
throw new ClassNotFoundException(name);
}
}
private byte[] loadClassData(String name) throws IOException
{
byte[] classData = null;
for (String jarFile : _jars)
{
File file = new File(jarFile);
try (ZipFile zipFile = new ZipFile(file);)
{
String fileName = name.replace('.', '/') + ".class";
ZipEntry entry = zipFile.getEntry(fileName);
if (entry == null)
{
continue;
}
classData = new byte[(int) entry.getSize()];
try (DataInputStream zipStream = new DataInputStream(zipFile.getInputStream(entry)))
{
zipStream.readFully(classData, 0, (int) entry.getSize());
}
break;
}
catch (IOException e)
{
_log.log(Level.WARNING, jarFile + ": " + e.getMessage(), e);
continue;
}
}
if (classData == null)
{
throw new IOException("class not found in " + _jars);
}
return classData;
}
}
@@ -0,0 +1,429 @@
/*
* 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.l2jserver.gameserver.util;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
import javolution.util.FastList;
import javolution.util.FastSet;
import com.l2jserver.Config;
import com.l2jserver.gameserver.ThreadPoolManager;
import com.l2jserver.gameserver.datatables.NpcTable;
import com.l2jserver.gameserver.idfactory.IdFactory;
import com.l2jserver.gameserver.model.L2MinionData;
import com.l2jserver.gameserver.model.actor.L2Character;
import com.l2jserver.gameserver.model.actor.instance.L2MonsterInstance;
import com.l2jserver.gameserver.templates.chars.L2NpcTemplate;
import com.l2jserver.util.Rnd;
/**
* @author luisantonioa, DS
*/
public class MinionList
{
private static Logger _log = Logger.getLogger(MinionList.class.getName());
protected final L2MonsterInstance _master;
/** List containing the current spawned minions */
private final List<L2MonsterInstance> _minionReferences;
/** 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;
_minionReferences = new FastList<L2MonsterInstance>().shared();
}
/**
* Returns 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>
*/
public final void spawnMinions()
{
if (_master.isAlikeDead())
{
return;
}
List<L2MinionData> minions = _master.getTemplate().getMinionData();
if (minions == null)
{
return;
}
int minionCount, minionId, minionsToSpawn;
for (L2MinionData minion : minions)
{
minionCount = minion.getAmount();
minionId = minion.getMinionId();
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().getMinionData() != null) && (_master.getSpawn() != null) && _master.getSpawn().isRespawnEnabled())
{
_reusedMinionReferences = new FastList<L2MonsterInstance>().shared();
}
}
/**
* Called on the minion spawn and added them in the list of the spawned minions.
*/
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 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.
*/
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(newX, newY, _master.getZ());
}
}
}
private final void spawnMinion(int minionId)
{
if (minionId == 0)
{
return;
}
// searching in reused minions
if ((_reusedMinionReferences != null) && !_reusedMinionReferences.isEmpty())
{
L2MonsterInstance minion;
Iterator<L2MonsterInstance> iter = _reusedMinionReferences.iterator();
while (iter.hasNext())
{
minion = iter.next();
if ((minion != null) && (minion.getNpcId() == minionId))
{
iter.remove();
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
*/
public static final L2MonsterInstance spawnMinion(L2MonsterInstance master, int minionId)
{
// Get the template of the Minion to spawn
L2NpcTemplate minionTemplate = NpcTable.getInstance().getTemplate(minionId);
if (minionTemplate == null)
{
return null;
}
// Create and Init the Minion and generate its Identifier
L2MonsterInstance minion = new L2MonsterInstance(IdFactory.getInstance().getNextId(), minionTemplate);
return initializeNpcInstance(master, minion);
}
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.fine("Spawned minion template " + minion.getNpcId() + " with objid: " + minion.getObjectId() + " to boss " + master.getObjectId() + " ,at: " + minion.getX() + " x, " + minion.getY() + " y, " + minion.getZ() + " z");
}
return minion;
}
// Statistics part
private final int countSpawnedMinionsById(int minionId)
{
int count = 0;
for (L2MonsterInstance minion : _minionReferences)
{
if ((minion != null) && (minion.getNpcId() == minionId))
{
count++;
}
}
return count;
}
public final int countSpawnedMinions()
{
return _minionReferences.size();
}
public final int lazyCountSpawnedMinionsGroups()
{
Set<Integer> seenGroups = new FastSet<>();
for (L2MonsterInstance minion : _minionReferences)
{
if (minion == null)
{
continue;
}
seenGroups.add(minion.getNpcId());
}
return seenGroups.size();
}
}
@@ -0,0 +1,309 @@
/*
* 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.l2jserver.gameserver.util;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javolution.util.FastList;
/**
* Descending Integer Sort Algorithm - Fast ordering system. - Can easily be ported elsewhere. - Can handle any number of values, from a list or even from a map. - Handles duplicate values.
* @author Tempy
*/
public class MultiSort
{
public static final int SORT_ASCENDING = 0;
public static final int SORT_DESCENDING = 1;
private List<?> _keyList;
private List<Integer> _valueList;
private boolean _isSortDescending;
private boolean _isSorted;
public MultiSort(int[] valueList)
{
_valueList = getIntList(valueList);
}
public MultiSort(Collection<Integer> valueList)
{
_valueList = getIntList(valueList);
}
public MultiSort(Object[] keyList, int[] valueList)
{
_keyList = getList(keyList);
_valueList = getIntList(valueList);
}
public MultiSort(Map<?, Integer> valueMap)
{
_keyList = getList(valueMap.keySet());
_valueList = getIntList(valueMap.values());
}
private final List<Integer> getIntList(Collection<Integer> valueList)
{
return Arrays.asList(valueList.toArray(new Integer[valueList.size()]));
}
private final List<Integer> getIntList(int[] valueList)
{
Integer[] tempIntList = new Integer[valueList.length];
for (int i = 0; i < valueList.length; i++)
{
tempIntList[i] = Integer.valueOf(valueList[i]);
}
return Arrays.asList(tempIntList);
}
private final List<?> getList(Collection<?> valueList)
{
return getList(valueList.toArray(new Object[valueList.size()]));
}
private final List<Object> getList(Object[] valueList)
{
return Arrays.asList(valueList);
}
public final int getCount()
{
return getValues().size();
}
public final int getHarmonicMean()
{
if (getValues().isEmpty())
{
return -1;
}
int totalValue = 0;
for (int currValue : getValues())
{
totalValue += (1 / currValue);
}
return (getCount() / totalValue);
}
public final List<?> getKeys()
{
if (_keyList == null)
{
return new FastList<>();
}
return _keyList;
}
public final int getFrequency(int checkValue)
{
return Collections.frequency(getValues(), checkValue);
}
public final int getMaxValue()
{
return Collections.max(getValues());
}
public final int getMinValue()
{
return Collections.min(getValues());
}
public final int getMean()
{
if (getValues().isEmpty())
{
return -1;
}
return (getTotalValue() / getCount());
}
public final double getStandardDeviation()
{
if (getValues().isEmpty())
{
return -1;
}
List<Double> tempValList = new FastList<>();
int meanValue = getMean();
int numValues = getCount();
for (int value : getValues())
{
double adjValue = Math.pow(value - meanValue, 2);
tempValList.add(adjValue);
}
double totalValue = 0;
for (double storedVal : tempValList)
{
totalValue += storedVal;
}
return Math.sqrt(totalValue / (numValues - 1));
}
public final int getTotalValue()
{
if (getValues().isEmpty())
{
return 0;
}
int totalValue = 0;
for (int currValue : getValues())
{
totalValue += currValue;
}
return totalValue;
}
public final List<Integer> getValues()
{
if (_valueList == null)
{
return new FastList<>();
}
return _valueList;
}
public final boolean isSortDescending()
{
return _isSortDescending;
}
public final boolean isSorted()
{
return _isSorted;
}
public final void setSortDescending(boolean isDescending)
{
_isSortDescending = isDescending;
}
public boolean sort()
{
try
{
List<Object> newKeyList = new FastList<>();
List<Integer> newValueList = new FastList<>();
// Sort the list of values in ascending numerical order.
Collections.sort(getValues());
int lastValue = 0;
if (!isSortDescending())
{
// If there are no keys, just return the ascendingly sorted values.
if (getKeys().isEmpty())
{
return true;
}
// Iterate through the list of ordered numerical values.
for (int i = getValues().size() - 1; i > -1; i--)
{
int currValue = getValues().get(i);
// If the current value is equal to the last value, we have at least one
// duplicate that has been outputted already, so continue.
if (currValue == lastValue)
{
continue;
}
// Set the last value to the current value, to prevent duplication.
lastValue = currValue;
// Iterate through each key and match it to its stored integer value,
// then output both sets of data in the correct descending numerical order.
for (int j = 0; j < getKeys().size(); j++)
{
Object currKey = getKeys().get(j);
if (getValues().get(j) == currValue)
{
newKeyList.add(currKey);
newValueList.add(currValue);
}
}
}
}
else
{
// If there are no keys, just sort the value list in reverse order.
if (getKeys().isEmpty())
{
Collections.reverse(getValues());
return true;
}
// Do the exact same as above, but in descending order.
for (int i = 0; i < getValues().size(); i++)
{
int currValue = getValues().get(i);
if (currValue == lastValue)
{
continue;
}
lastValue = currValue;
for (int j = 0; j < getKeys().size(); j++)
{
Object currKey = getKeys().get(j);
if (getValues().get(j) == currValue)
{
newKeyList.add(currKey);
newValueList.add(currValue);
}
}
}
}
_keyList = newKeyList;
_valueList = newValueList;
_isSorted = true;
return true;
}
catch (Exception e)
{
return false;
}
}
}
@@ -0,0 +1,425 @@
/*
* $Header: Util.java, 21/10/2005 23:17:40 luisantonioa Exp $
*
* $Author: luisantonioa $
* $Date: 21/10/2005 23:17:40 $
* $Revision: 1 $
* $Log: Util.java,v $
* Revision 1 21/10/2005 23:17:40 luisantonioa
* Added copyright notice
*
*
* 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.l2jserver.gameserver.util;
import java.io.File;
import javolution.text.TextBuilder;
import com.l2jserver.gameserver.ThreadPoolManager;
import com.l2jserver.gameserver.model.L2Object;
import com.l2jserver.gameserver.model.actor.L2Character;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
/**
* General Utility functions related to Gameserver
*/
public final class Util
{
public static void handleIllegalPlayerAction(L2PcInstance actor, String message, int punishment)
{
ThreadPoolManager.getInstance().scheduleGeneral(new IllegalPlayerAction(actor, message, punishment), 5000);
}
public static String getRelativePath(File base, File file)
{
return file.toURI().getPath().substring(base.toURI().getPath().length());
}
/**
* Return degree value of object 2 to the horizontal line with object 1 being the origin
*/
public static double calculateAngleFrom(L2Object obj1, L2Object obj2)
{
return calculateAngleFrom(obj1.getX(), obj1.getY(), obj2.getX(), obj2.getY());
}
/**
* Return degree value of object 2 to the horizontal line with object 1 being the origin
*/
public final static double calculateAngleFrom(int obj1X, int obj1Y, int obj2X, int obj2Y)
{
double angleTarget = Math.toDegrees(Math.atan2(obj2Y - obj1Y, obj2X - obj1X));
if (angleTarget < 0)
{
angleTarget = 360 + angleTarget;
}
return angleTarget;
}
public final static double convertHeadingToDegree(int clientHeading)
{
double degree = clientHeading / 182.044444444;
return degree;
}
public final static int convertDegreeToClientHeading(double degree)
{
if (degree < 0)
{
degree = 360 + degree;
}
return (int) (degree * 182.044444444);
}
public final static int calculateHeadingFrom(L2Object obj1, L2Object obj2)
{
return calculateHeadingFrom(obj1.getX(), obj1.getY(), obj2.getX(), obj2.getY());
}
public final static int calculateHeadingFrom(int obj1X, int obj1Y, int obj2X, int obj2Y)
{
double angleTarget = Math.toDegrees(Math.atan2(obj2Y - obj1Y, obj2X - obj1X));
if (angleTarget < 0)
{
angleTarget = 360 + angleTarget;
}
return (int) (angleTarget * 182.044444444);
}
public final static int calculateHeadingFrom(double dx, double dy)
{
double angleTarget = Math.toDegrees(Math.atan2(dy, dx));
if (angleTarget < 0)
{
angleTarget = 360 + angleTarget;
}
return (int) (angleTarget * 182.044444444);
}
/**
* @return the distance between the two coordinates in 2D plane
*/
public static double calculateDistance(int x1, int y1, int x2, int y2)
{
return calculateDistance(x1, y1, 0, x2, y2, 0, false);
}
/**
* @param includeZAxis - if true, includes also the Z axis in the calculation
* @return the distance between the two coordinates
*/
public static double calculateDistance(int x1, int y1, int z1, int x2, int y2, int z2, boolean includeZAxis)
{
double dx = (double) x1 - x2;
double dy = (double) y1 - y2;
if (includeZAxis)
{
double dz = z1 - z2;
return Math.sqrt((dx * dx) + (dy * dy) + (dz * dz));
}
return Math.sqrt((dx * dx) + (dy * dy));
}
/**
* @param includeZAxis - if true, includes also the Z axis in the calculation
* @return the distance between the two objects
*/
public static double calculateDistance(L2Object obj1, L2Object obj2, boolean includeZAxis)
{
if ((obj1 == null) || (obj2 == null))
{
return 1000000;
}
return calculateDistance(obj1.getPosition().getX(), obj1.getPosition().getY(), obj1.getPosition().getZ(), obj2.getPosition().getX(), obj2.getPosition().getY(), obj2.getPosition().getZ(), includeZAxis);
}
/**
* @param str - the string whose first letter to capitalize
* @return a string with the first letter of the {@code str} capitalized
*/
public static String capitalizeFirst(String str)
{
if ((str == null) || str.isEmpty())
{
return str;
}
final char[] arr = str.toCharArray();
final char c = arr[0];
if (Character.isLetter(c))
{
arr[0] = Character.toUpperCase(c);
}
return arr.toString();
}
/**
* (Based on ucwords() function of PHP) DrHouse: still functional but must be rewritten to avoid += to concat strings
* @param str - the string to capitalize
* @return a string with the first letter of every word in {@code str} capitalized
*/
@Deprecated
public static String capitalizeWords(String str)
{
if ((str == null) || str.isEmpty())
{
return str;
}
char[] charArray = str.toCharArray();
String result = "";
// Capitalize the first letter in the given string!
charArray[0] = Character.toUpperCase(charArray[0]);
for (int i = 0; i < charArray.length; i++)
{
if (Character.isWhitespace(charArray[i]))
{
charArray[i + 1] = Character.toUpperCase(charArray[i + 1]);
}
result += Character.toString(charArray[i]);
}
return result;
}
/**
* @return {@code true} if the two objects are within specified range between each other, {@code false} otherwise
*/
public static boolean checkIfInRange(int range, L2Object obj1, L2Object obj2, boolean includeZAxis)
{
if ((obj1 == null) || (obj2 == null))
{
return false;
}
if (obj1.getInstanceId() != obj2.getInstanceId())
{
return false;
}
if (range == -1)
{
return true; // not limited
}
int rad = 0;
if (obj1 instanceof L2Character)
{
rad += ((L2Character) obj1).getTemplate().collisionRadius;
}
if (obj2 instanceof L2Character)
{
rad += ((L2Character) obj2).getTemplate().collisionRadius;
}
double dx = obj1.getX() - obj2.getX();
double dy = obj1.getY() - obj2.getY();
if (includeZAxis)
{
double dz = obj1.getZ() - obj2.getZ();
double d = (dx * dx) + (dy * dy) + (dz * dz);
return d <= ((range * range) + (2 * range * rad) + (rad * rad));
}
double d = (dx * dx) + (dy * dy);
return d <= ((range * range) + (2 * range * rad) + (rad * rad));
}
/**
* Checks if object is within short (sqrt(int.max_value)) radius, not using collisionRadius. Faster calculation than checkIfInRange if distance is short and collisionRadius isn't needed. Not for long distance checks (potential teleports, far away castles etc).
* @param range - the maximum range between the two objects
* @param includeZAxis - if true, check also Z axis (3-dimensional check), otherwise only 2D
* @return {@code true} if objects are within specified range between each other, {@code false} otherwise
*/
public static boolean checkIfInShortRadius(int radius, L2Object obj1, L2Object obj2, boolean includeZAxis)
{
if ((obj1 == null) || (obj2 == null))
{
return false;
}
if (radius == -1)
{
return true; // not limited
}
int dx = obj1.getX() - obj2.getX();
int dy = obj1.getY() - obj2.getY();
if (includeZAxis)
{
int dz = obj1.getZ() - obj2.getZ();
return ((dx * dx) + (dy * dy) + (dz * dz)) <= (radius * radius);
}
return ((dx * dx) + (dy * dy)) <= (radius * radius);
}
/**
* @param str - the String to count
* @return the number of "words" in a given string.
*/
public static int countWords(String str)
{
return str.trim().split("\\s+").length;
}
/**
* (Based on implode() in PHP)
* @param strArray - an array of strings to concatenate
* @param strDelim - the delimiter to put between the strings
* @return a delimited string for a given array of string elements.
*/
public static String implodeString(Iterable<String> strArray, String strDelim)
{
final TextBuilder sbString = TextBuilder.newInstance();
for (String strValue : strArray)
{
sbString.append(strValue);
sbString.append(strDelim);
}
String result = sbString.toString();
TextBuilder.recycle(sbString);
return result;
}
/**
* (Based on round() in PHP)
* @param number - the number to round
* @param numPlaces - how many digits after decimal point to leave intact
* @return the value of {@code number} rounded to specified number of digits after the decimal point.
*/
public static float roundTo(float number, int numPlaces)
{
if (numPlaces <= 1)
{
return Math.round(number);
}
float exponent = (float) Math.pow(10, numPlaces);
return Math.round(number * exponent) / exponent;
}
/**
* @param text - the text to check
* @return {@code true} if {@code text} contains only numbers, {@code false} otherwise
*/
public static boolean isDigit(String text)
{
if ((text == null) || text.isEmpty())
{
return false;
}
for (char c : text.toCharArray())
{
if (!Character.isDigit(c))
{
return false;
}
}
return true;
}
/**
* @param text - the text to check
* @return {@code true} if {@code text} contains only letters and/or numbers, {@code false} otherwise
*/
public static boolean isAlphaNumeric(String text)
{
if ((text == null) || text.isEmpty())
{
return false;
}
for (char c : text.toCharArray())
{
if (!Character.isLetterOrDigit(c))
{
return false;
}
}
return true;
}
/**
* Format the specified digit using the digit grouping symbol "," (comma). For example, 123456789 becomes 123,456,789.
* @param amount - the amount of adena
* @return the formatted adena amount
*/
public static String formatAdena(long amount)
{
String s = "";
long rem = amount % 1000;
s = Long.toString(rem);
amount = (amount - rem) / 1000;
while (amount > 0)
{
if (rem < 99)
{
s = '0' + s;
}
if (rem < 9)
{
s = '0' + s;
}
rem = amount % 1000;
s = Long.toString(rem) + "," + s;
amount = (amount - rem) / 1000;
}
return s;
}
/**
* @param array - the array to look into
* @param obj - the object to search for
* @return {@code true} if the {@code array} contains the {@code obj}, {@code false} otherwise
*/
public static <T> boolean contains(T[] array, T obj)
{
for (T element : array)
{
if (element == obj)
{
return true;
}
}
return false;
}
/**
* @param array - the array to look into
* @param obj - the integer to search for
* @return {@code true} if the {@code array} contains the {@code obj}, {@code false} otherwise
*/
public static boolean contains(int[] array, int obj)
{
for (int element : array)
{
if (element == obj)
{
return true;
}
}
return false;
}
}