/* * 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 . */ package com.l2jserver.gameserver.model; import gnu.trove.TIntObjectHashMap; import gnu.trove.TObjectProcedure; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.logging.Logger; import javolution.util.FastList; import com.l2jserver.Config; import com.l2jserver.gameserver.GmListTable; import com.l2jserver.gameserver.datatables.CharNameTable; import com.l2jserver.gameserver.model.actor.L2Playable; import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; import com.l2jserver.gameserver.model.actor.instance.L2PetInstance; import com.l2jserver.gameserver.util.Point3D; import com.l2jserver.util.StringUtil; /** * This class ... * * @version $Revision: 1.21.2.5.2.7 $ $Date: 2005/03/27 15:29:32 $ */ public final class L2World { private static Logger _log = Logger.getLogger(L2World.class.getName()); /** * Gracia border * Flying objects not allowed to the east of it. */ public static final int GRACIA_MAX_X = -166168; public static final int GRACIA_MAX_Z = 6105; public static final int GRACIA_MIN_Z = -895; /* * biteshift, defines number of regions * note, shifting by 15 will result in regions corresponding to map tiles * shifting by 12 divides one tile to 8x8 regions */ public static final int SHIFT_BY = 12; private static final int TILE_SIZE = 32768; /** Map dimensions */ public static final int MAP_MIN_X = (Config.WORLD_X_MIN - 20) * TILE_SIZE; public static final int MAP_MAX_X = (Config.WORLD_X_MAX - 19) * TILE_SIZE; public static final int MAP_MIN_Y = (Config.WORLD_Y_MIN - 18) * TILE_SIZE; public static final int MAP_MAX_Y = (Config.WORLD_Y_MAX - 17) * TILE_SIZE; /** calculated offset used so top left region is 0,0 */ public static final int OFFSET_X = Math.abs(MAP_MIN_X >> SHIFT_BY); public static final int OFFSET_Y = Math.abs(MAP_MIN_Y >> SHIFT_BY); /** number of regions */ private static final int REGIONS_X = (MAP_MAX_X >> SHIFT_BY) + OFFSET_X; private static final int REGIONS_Y = (MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y; //private FastMap _allGms; /** HashMap(Integer Player id, L2PcInstance) containing all the players in game */ private TIntObjectHashMap _allPlayers; private final Lock _apRL; private final Lock _apWL; /** L2ObjectHashMap(L2Object) containing all visible objects */ private TIntObjectHashMap _allObjects; private final Lock _aoRL; private final Lock _aoWL; /** List with the pets instances and their owner id */ private TIntObjectHashMap _petsInstance; private final Lock _piRL; private final Lock _piWL; private L2WorldRegion[][] _worldRegions; /** * Constructor of L2World.

*/ private L2World() { ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); _apRL = lock.readLock(); _apWL = lock.writeLock(); _allPlayers = new TIntObjectHashMap(); lock = new ReentrantReadWriteLock(); _aoRL = lock.readLock(); _aoWL = lock.writeLock(); _allObjects = new TIntObjectHashMap(); lock = new ReentrantReadWriteLock(); _piRL = lock.readLock(); _piWL = lock.writeLock(); _petsInstance = new TIntObjectHashMap(); initRegions(); } /** * Return the current instance of L2World.

*/ public static L2World getInstance() { return SingletonHolder._instance; } /** * Add L2Object object in _allObjects.

* * Example of use :

*
  • Withdraw an item from the warehouse, create an item
  • *
  • Spawn a L2Character (PC, NPC, Pet)

  • */ public void storeObject(L2Object object) { _aoWL.lock(); try { _aoRL.lock(); try { assert !_allObjects.containsKey(object.getObjectId()); if (_allObjects.containsKey(object.getObjectId())) { if (Config.DEBUG) { _log.warning("[L2World] object: " + object + " already exist in OID map!"); _log.info(StringUtil.getTraceString(Thread.currentThread().getStackTrace())); return; } } } finally { _aoRL.unlock(); } _allObjects.put(object.getObjectId(), object); } finally { _aoWL.unlock(); } } public long timeStoreObject(L2Object object) { _aoWL.lock(); try { long time = System.nanoTime(); _allObjects.put(object.getObjectId(), object); time = System.nanoTime() - time; return time; } finally { _aoWL.unlock(); } } /** * Remove L2Object object from _allObjects of L2World.

    * * Example of use :

    *
  • Delete item from inventory, tranfer Item from inventory to warehouse
  • *
  • Crystallize item
  • *
  • Remove NPC/PC/Pet from the world

  • * * @param object L2Object to remove from _allObjects of L2World * */ public void removeObject(L2Object object) { _aoWL.lock(); try { _allObjects.remove(object.getObjectId()); // suggestion by whatev //IdFactory.getInstance().releaseId(object.getObjectId()); } finally { _aoWL.unlock(); } } public void removeObjects(List list) { _aoWL.lock(); try { for (L2Object o : list) { if (o != null) _allObjects.remove(o.getObjectId()); // suggestion by whatev } //IdFactory.getInstance().releaseId(object.getObjectId()); } finally { _aoWL.unlock(); } } public void removeObjects(L2Object[] objects) { _aoWL.lock(); try { for (L2Object o : objects) _allObjects.remove(o.getObjectId()); // suggestion by whatev //IdFactory.getInstance().releaseId(object.getObjectId()); } finally { _aoWL.unlock(); } } public long timeRemoveObject(L2Object object) { _aoWL.lock(); try { long time = System.nanoTime(); _allObjects.remove(object.getObjectId()); time = System.nanoTime() - time; return time; } finally { _aoWL.unlock(); } } /** * Return the L2Object object that belongs to an ID or null if no object found.

    * * Example of use :

    *
  • Client packets : Action, AttackRequest, RequestJoinParty, RequestJoinPledge...

  • * * @param oID Identifier of the L2Object */ public L2Object findObject(int oID) { _aoRL.lock(); try { return _allObjects.get(oID); } finally { _aoRL.unlock(); } } public long timeFindObject(int objectID) { _aoRL.lock(); try { long time = System.nanoTime(); _allObjects.get(objectID); time = System.nanoTime() - time; return time; } finally { _aoRL.unlock(); } } /** * Added by Tempy - 08 Aug 05 * Allows easy retrevial of all visible objects in world. * * -- do not use that fucntion, its unsafe! * * @deprecated */ @Deprecated public final TIntObjectHashMap getAllVisibleObjects() { _aoRL.lock(); try { return _allObjects; } finally { _aoRL.unlock(); } } public final L2Object[] getAllVisibleObjectsArray() { _aoRL.lock(); try { return _allObjects.getValues(new L2Object[_allObjects.size()]); } finally { _aoRL.unlock(); } } public final boolean forEachObject(final TObjectProcedure proc) { _aoRL.lock(); try { return _allObjects.forEachValue(proc); } finally { _aoRL.unlock(); } } /** * Get the count of all visible objects in world.

    * * @return count off all L2World objects */ public final int getAllVisibleObjectsCount() { return _allObjects.size(); } /** * Return a table containing all GMs.

    * */ public FastList getAllGMs() { return GmListTable.getInstance().getAllGms(true); } public TIntObjectHashMap getAllPlayers() { _apRL.lock(); try { return _allPlayers; } finally { _apRL.unlock(); } } public final L2PcInstance[] getAllPlayersArray() { _apRL.lock(); try { return _allPlayers.getValues(new L2PcInstance[_allPlayers.size()]); } finally { _apRL.unlock(); } } public final boolean forEachPlayer(final TObjectProcedure proc) { _apRL.lock(); try { return _allPlayers.forEachValue(proc); } finally { _apRL.unlock(); } } /** * Return how many players are online.

    * * @return number of online players. */ public int getAllPlayersCount() { return _allPlayers.size(); } /** * Return the player instance corresponding to the given name.

    * If you have access to player objectId use {@link #getPlayer(int playerObjId)} *
    * @param name Name of the player to get Instance */ public L2PcInstance getPlayer(String name) { return getPlayer(CharNameTable.getInstance().getIdByName(name)); } /** * Return the player instance corresponding to the given object ID.

    * * @param playerObjId Object ID of the player to get Instance */ public L2PcInstance getPlayer(int playerObjId) { _apRL.lock(); try { return _allPlayers.get(playerObjId); } finally { _apRL.unlock(); } } /** * Return the pet instance from the given ownerId.

    * * @param ownerId ID of the owner */ public L2PetInstance getPet(int ownerId) { _piRL.lock(); try { return _petsInstance.get(ownerId); } finally { _piRL.unlock(); } } /** * Add the given pet instance from the given ownerId.

    * * @param ownerId ID of the owner * @param pet L2PetInstance of the pet */ public L2PetInstance addPet(int ownerId, L2PetInstance pet) { _piWL.lock(); try { return _petsInstance.put(ownerId, pet); } finally { _piWL.unlock(); } } /** * Remove the given pet instance.

    * * @param ownerId ID of the owner */ public void removePet(int ownerId) { _piWL.lock(); try { _petsInstance.remove(ownerId); } finally { _piWL.unlock(); } } /** * Remove the given pet instance.

    * * @param pet the pet to remove */ public void removePet(L2PetInstance pet) { _piWL.lock(); try { _petsInstance.remove(pet.getOwner().getObjectId()); } finally { _piWL.unlock(); } } /** * Add a L2Object in the world.

    * * Concept :

    * L2Object (including L2PcInstance) are identified in _visibleObjects of his current L2WorldRegion and in _knownObjects of other surrounding L2Characters
    * L2PcInstance are identified in _allPlayers of L2World, in _allPlayers of his current L2WorldRegion and in _knownPlayer of other surrounding L2Characters

    * * Actions :

    *
  • Add the L2Object object in _allPlayers* of L2World
  • *
  • Add the L2Object object in _gmList** of GmListTable
  • *
  • Add object in _knownObjects and _knownPlayer* of all surrounding L2WorldRegion L2Characters

  • * *
  • If object is a L2Character, add all surrounding L2Object in its _knownObjects and all surrounding L2PcInstance in its _knownPlayer

  • * * * only if object is a L2PcInstance
    * ** only if object is a GM L2PcInstance

    * * Caution : This method DOESN'T ADD the object in _visibleObjects and _allPlayers* of L2WorldRegion (need synchronisation)
    * Caution : This method DOESN'T ADD the object to _allObjects and _allPlayers* of L2World (need synchronisation)

    * * Example of use :

    *
  • Drop an Item
  • *
  • Spawn a L2Character
  • *
  • Apply Death Penalty of a L2PcInstance


  • * * @param object L2object to add in the world * @param newregion L2WorldRegion in wich the object will be add (not used) * @param dropper L2Character who has dropped the object (if necessary) * */ public void addVisibleObject(L2Object object, L2WorldRegion newRegion) { // If selected L2Object is a L2PcIntance, add it in L2ObjectHashSet(L2PcInstance) _allPlayers of L2World // XXX TODO: this code should be obsoleted by protection in putObject func... if (object instanceof L2PcInstance) { L2PcInstance player = (L2PcInstance) object; if (!player.isTeleporting()) { L2PcInstance tmp = getPlayer(player.getObjectId()); if (tmp != null) { _log.warning("Duplicate character!? Closing both characters (" + player.getName() + ")"); player.logout(); tmp.logout(); return; } addToAllPlayers(player); } } if (!newRegion.isActive()) return; // Get all visible objects contained in the _visibleObjects of L2WorldRegions // in a circular area of 2000 units List visibles = getVisibleObjects(object, 2000); if (Config.DEBUG) _log.finest("objects in range:" + visibles.size()); // tell the player about the surroundings // Go through the visible objects contained in the circular area for (L2Object visible : visibles) { if (visible == null) continue; // Add the object in L2ObjectHashSet(L2Object) _knownObjects of the visible L2Character according to conditions : // - L2Character is visible // - object is not already known // - object is in the watch distance // If L2Object is a L2PcInstance, add L2Object in L2ObjectHashSet(L2PcInstance) _knownPlayer of the visible L2Character visible.getKnownList().addKnownObject(object); // Add the visible L2Object in L2ObjectHashSet(L2Object) _knownObjects of the object according to conditions // If visible L2Object is a L2PcInstance, add visible L2Object in L2ObjectHashSet(L2PcInstance) _knownPlayer of the object object.getKnownList().addKnownObject(visible); } } /** * Add the L2PcInstance to _allPlayers of L2World.

    * */ public void addToAllPlayers(L2PcInstance cha) { _apWL.lock(); try { _allPlayers.put(cha.getObjectId(), cha); } finally { _apWL.unlock(); } } /** * Remove the L2PcInstance from _allPlayers of L2World.

    * * Example of use :

    *
  • Remove a player fom the visible objects

  • * */ public void removeFromAllPlayers(L2PcInstance cha) { _apWL.lock(); try { _allPlayers.remove(cha.getObjectId()); } finally { _apWL.unlock(); } } /** * Remove a L2Object from the world.

    * * Concept :

    * L2Object (including L2PcInstance) are identified in _visibleObjects of his current L2WorldRegion and in _knownObjects of other surrounding L2Characters
    * L2PcInstance are identified in _allPlayers of L2World, in _allPlayers of his current L2WorldRegion and in _knownPlayer of other surrounding L2Characters

    * * Actions :

    *
  • Remove the L2Object object from _allPlayers* of L2World
  • *
  • Remove the L2Object object from _visibleObjects and _allPlayers* of L2WorldRegion
  • *
  • Remove the L2Object object from _gmList** of GmListTable
  • *
  • Remove object from _knownObjects and _knownPlayer* of all surrounding L2WorldRegion L2Characters

  • * *
  • If object is a L2Character, remove all L2Object from its _knownObjects and all L2PcInstance from its _knownPlayer


  • * * Caution : This method DOESN'T REMOVE the object from _allObjects of L2World

    * * * only if object is a L2PcInstance
    * ** only if object is a GM L2PcInstance

    * * Example of use :

    *
  • Pickup an Item
  • *
  • Decay a L2Character


  • * * @param object L2object to remove from the world * @param oldregion L2WorldRegion in wich the object was before removing * */ public void removeVisibleObject(L2Object object, L2WorldRegion oldRegion) { if (object == null) return; //removeObject(object); if (oldRegion != null) { // Remove the object from the L2ObjectHashSet(L2Object) _visibleObjects of L2WorldRegion // If object is a L2PcInstance, remove it from the L2ObjectHashSet(L2PcInstance) _allPlayers of this L2WorldRegion oldRegion.removeVisibleObject(object); // Go through all surrounding L2WorldRegion L2Characters for (L2WorldRegion reg : oldRegion.getSurroundingRegions()) { //synchronized (KnownListUpdateTaskManager.getInstance().getSync()) { Collection vObj = reg.getVisibleObjects().values(); //synchronized (reg.getVisibleObjects()) { for (L2Object obj : vObj) { if (obj != null) { obj.getKnownList().removeKnownObject(object); object.getKnownList().removeKnownObject(obj); } } } } } // If object is a L2Character : // Remove all L2Object from L2ObjectHashSet(L2Object) containing all L2Object detected by the L2Character // Remove all L2PcInstance from L2ObjectHashSet(L2PcInstance) containing all player ingame detected by the L2Character object.getKnownList().removeAllKnownObjects(); // If selected L2Object is a L2PcIntance, remove it from L2ObjectHashSet(L2PcInstance) _allPlayers of L2World if (object instanceof L2PcInstance) { if (!((L2PcInstance) object).isTeleporting()) removeFromAllPlayers((L2PcInstance) object); // If selected L2Object is a GM L2PcInstance, remove it from Set(L2PcInstance) _gmList of GmListTable //if (((L2PcInstance)object).isGM()) //GmListTable.getInstance().deleteGm((L2PcInstance)object); } } } /** * Return all visible objects of the L2WorldRegion object's and of its surrounding L2WorldRegion.

    * * Concept :

    * All visible object are identified in _visibleObjects of their current L2WorldRegion
    * All surrounding L2WorldRegion are identified in _surroundingRegions of the selected L2WorldRegion in order to scan a large area around a L2Object

    * * Example of use :

    *
  • Find Close Objects for L2Character

  • * * @param object L2object that determine the current L2WorldRegion * */ public List getVisibleObjects(L2Object object) { L2WorldRegion reg = object.getWorldRegion(); if (reg == null) return null; // Create an FastList in order to contain all visible L2Object List result = new ArrayList(); // Go through the FastList of region for (L2WorldRegion regi : reg.getSurroundingRegions()) { // Go through visible objects of the selected region Collection vObj = regi.getVisibleObjects().values(); //synchronized (regi.getVisibleObjects()) { for (L2Object _object : vObj) { if (_object == null || _object.equals(object)) continue; // skip our own character if (!_object.isVisible()) continue; // skip dying objects result.add(_object); } } } return result; } /** * Return all visible objects of the L2WorldRegions in the circular area (radius) centered on the object.

    * * Concept :

    * All visible object are identified in _visibleObjects of their current L2WorldRegion
    * All surrounding L2WorldRegion are identified in _surroundingRegions of the selected L2WorldRegion in order to scan a large area around a L2Object

    * * Example of use :

    *
  • Define the aggrolist of monster
  • *
  • Define visible objects of a L2Object
  • *
  • Skill : Confusion...

  • * * @param object L2object that determine the center of the circular area * @param radius Radius of the circular area * */ public List getVisibleObjects(L2Object object, int radius) { if (object == null || !object.isVisible()) return new ArrayList(); int x = object.getX(); int y = object.getY(); int sqRadius = radius * radius; // Create an FastList in order to contain all visible L2Object List result = new ArrayList(); // Go through the FastList of region for (L2WorldRegion regi : object.getWorldRegion().getSurroundingRegions()) { // Go through visible objects of the selected region Collection vObj = regi.getVisibleObjects().values(); //synchronized (regi.getVisibleObjects()) { for (L2Object _object : vObj) { if (_object == null || _object.equals(object)) continue; // skip our own character int x1 = _object.getX(); int y1 = _object.getY(); double dx = x1 - x; double dy = y1 - y; if (dx * dx + dy * dy < sqRadius) result.add(_object); } } } return result; } /** * Return all visible objects of the L2WorldRegions in the spheric area (radius) centered on the object.

    * * Concept :

    * All visible object are identified in _visibleObjects of their current L2WorldRegion
    * All surrounding L2WorldRegion are identified in _surroundingRegions of the selected L2WorldRegion in order to scan a large area around a L2Object

    * * Example of use :

    *
  • Define the target list of a skill
  • *
  • Define the target list of a polearme attack


  • * * @param object L2object that determine the center of the circular area * @param radius Radius of the spheric area * */ public List getVisibleObjects3D(L2Object object, int radius) { if (object == null || !object.isVisible()) return new ArrayList(); int x = object.getX(); int y = object.getY(); int z = object.getZ(); int sqRadius = radius * radius; // Create an FastList in order to contain all visible L2Object List result = new ArrayList(); // Go through visible object of the selected region for (L2WorldRegion regi : object.getWorldRegion().getSurroundingRegions()) { Collection vObj = regi.getVisibleObjects().values(); //synchronized (regi.getVisibleObjects()) { for (L2Object _object : vObj) { if (_object == null || _object.equals(object)) continue; // skip our own character int x1 = _object.getX(); int y1 = _object.getY(); int z1 = _object.getZ(); long dx = x1 - x; long dy = y1 - y; long dz = z1 - z; if (dx * dx + dy * dy + dz * dz < sqRadius) result.add(_object); } } } return result; } /** * Return all visible players of the L2WorldRegion object's and of its surrounding L2WorldRegion.

    * * Concept :

    * All visible object are identified in _visibleObjects of their current L2WorldRegion
    * All surrounding L2WorldRegion are identified in _surroundingRegions of the selected L2WorldRegion in order to scan a large area around a L2Object

    * * Example of use :

    *
  • Find Close Objects for L2Character

  • * * @param object L2object that determine the current L2WorldRegion * */ public List getVisiblePlayable(L2Object object) { L2WorldRegion reg = object.getWorldRegion(); if (reg == null) return null; // Create an FastList in order to contain all visible L2Object List result = new ArrayList(); // Go through the FastList of region for (L2WorldRegion regi : reg.getSurroundingRegions()) { // Create an Iterator to go through the visible L2Object of the L2WorldRegion Map _allpls = regi.getVisiblePlayable(); Collection _playables = _allpls.values(); // Go through visible object of the selected region //synchronized (_allpls) { for (L2Playable _object : _playables) { if (_object == null || _object.equals(object)) continue; // skip our own character if (!_object.isVisible()) // GM invisible is different than this... continue; // skip dying objects result.add(_object); } } } return result; } /** * Calculate the current L2WorldRegions of the object according to its position (x,y).

    * * Example of use :

    *
  • Set position of a new L2Object (drop, spawn...)
  • *
  • Update position of a L2Object after a mouvement

  • * * @param Point3D point position of the object */ public L2WorldRegion getRegion(Point3D point) { return _worldRegions[(point.getX() >> SHIFT_BY) + OFFSET_X][(point.getY() >> SHIFT_BY) + OFFSET_Y]; } public L2WorldRegion getRegion(int x, int y) { return _worldRegions[(x >> SHIFT_BY) + OFFSET_X][(y >> SHIFT_BY) + OFFSET_Y]; } /** * Returns the whole 2d array containing the world regions * used by ZoneData.java to setup zones inside the world regions * @return */ public L2WorldRegion[][] getAllWorldRegions() { return _worldRegions; } /** * Check if the current L2WorldRegions of the object is valid according to its position (x,y).

    * * Example of use :

    *
  • Init L2WorldRegions

  • * * @param x X position of the object * @param y Y position of the object * * @return True if the L2WorldRegion is valid */ private boolean validRegion(int x, int y) { return (x >= 0 && x <= REGIONS_X && y >= 0 && y <= REGIONS_Y); } /** * Init each L2WorldRegion and their surrounding table.

    * * Concept :

    * All surrounding L2WorldRegion are identified in _surroundingRegions of the selected L2WorldRegion in order to scan a large area around a L2Object

    * * Example of use :

    *
  • Constructor of L2World

  • * */ private void initRegions() { _worldRegions = new L2WorldRegion[REGIONS_X + 1][REGIONS_Y + 1]; for (int i = 0; i <= REGIONS_X; i++) { for (int j = 0; j <= REGIONS_Y; j++) { _worldRegions[i][j] = new L2WorldRegion(i, j); } } for (int x = 0; x <= REGIONS_X; x++) { for (int y = 0; y <= REGIONS_Y; y++) { for (int a = -1; a <= 1; a++) { for (int b = -1; b <= 1; b++) { if (validRegion(x + a, y + b)) { _worldRegions[x + a][y + b].addSurroundingRegion(_worldRegions[x][y]); } } } } } _log.info("L2World: (" + REGIONS_X + " by " + REGIONS_Y + ") World Region Grid set up."); } /** * Deleted all spawns in the world. */ public void deleteVisibleNpcSpawns() { _log.info("Deleting all visible NPC's."); for (int i = 0; i <= REGIONS_X; i++) { for (int j = 0; j <= REGIONS_Y; j++) { _worldRegions[i][j].deleteVisibleNpcSpawns(); } } _log.info("All visible NPC's deleted."); } @SuppressWarnings("synthetic-access") private static class SingletonHolder { protected static final L2World _instance = new L2World(); } }