/* * Copyright (C) 2004-2014 L2J Server * * This file is part of L2J Server. * * L2J Server is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * L2J Server is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package com.l2jserver.gameserver.model; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; import com.l2jserver.Config; import com.l2jserver.gameserver.datatables.AdminTable; 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.util.StringUtil; public final class L2World { private static final 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 TILE_X_MIN = 11; public static final int TILE_Y_MIN = 10; public static final int TILE_X_MAX = 26; public static final int TILE_Y_MAX = 26; public static final int TILE_ZERO_COORD_X = 20; public static final int TILE_ZERO_COORD_Y = 18; public static final int MAP_MIN_X = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; public static final int MAP_MIN_Y = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; public static final int MAP_MAX_X = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; public static final int MAP_MAX_Y = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * 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; /** Map containing all the players in game. */ private final Map _allPlayers = new ConcurrentHashMap<>(); /** Map containing all visible objects. */ private final Map _allObjects = new ConcurrentHashMap<>(); /** Map used for debug. */ private final Map _allObjectsDebug = new ConcurrentHashMap<>(); /** Map with the pets instances and their owner ID. */ private final Map _petsInstance = new ConcurrentHashMap<>(); private L2WorldRegion[][] _worldRegions; /** Constructor of L2World. */ protected L2World() { initRegions(); } /** * Adds an object to the world.
* Example of use: *
    *
  • Withdraw an item from the warehouse, create an item
  • *
  • Spawn a L2Character (PC, NPC, Pet)
  • *
* @param object */ public void storeObject(L2Object object) { if (_allObjects.containsKey(object.getObjectId())) { _log.log(Level.WARNING, getClass().getSimpleName() + ": Current object: " + object + " already exist in OID map!"); _log.log(Level.WARNING, StringUtil.getTraceString(Thread.currentThread().getStackTrace())); _log.log(Level.WARNING, getClass().getSimpleName() + ": Previous object: " + _allObjects.get(object.getObjectId()) + " already exist in OID map!"); _log.log(Level.WARNING, _allObjectsDebug.get(object.getObjectId())); _log.log(Level.WARNING, "---------------------- End ---------------------"); return; } _allObjects.put(object.getObjectId(), object); _allObjectsDebug.put(object.getObjectId(), StringUtil.getTraceString(Thread.currentThread().getStackTrace())); } /** * Removes an object from the world.
* Example of use: *
    *
  • Delete item from inventory, transfer Item from inventory to warehouse
  • *
  • Crystallize item
  • *
  • Remove NPC/PC/Pet from the world
  • *
* @param object the object to remove */ public void removeObject(L2Object object) { _allObjects.remove(object.getObjectId()); _allObjectsDebug.remove(object.getObjectId()); } /** * Example of use: *
    *
  • Client packets : Action, AttackRequest, RequestJoinParty, RequestJoinPledge...
  • *
* @param objectId Identifier of the L2Object * @return the L2Object object that belongs to an ID or null if no object found. */ public L2Object findObject(int objectId) { return _allObjects.get(objectId); } public Collection getVisibleObjects() { return _allObjects.values(); } /** * Get the count of all visible objects in world. * @return count off all L2World objects */ public int getVisibleObjectsCount() { return _allObjects.size(); } public List getAllGMs() { return AdminTable.getInstance().getAllGms(true); } public Collection getPlayers() { return _allPlayers.values(); } /** * Gets all players sorted by the given comparator. * @param comparator the comparator * @return the players sorted by the comparator */ public L2PcInstance[] getPlayersSortedBy(Comparator comparator) { final L2PcInstance[] players = _allPlayers.values().toArray(new L2PcInstance[_allPlayers.values().size()]); Arrays.sort(players, comparator); return players; } /** * Return how many players are online. * @return number of online players. */ public int getAllPlayersCount() { return _allPlayers.size(); } /** * If you have access to player objectId use {@link #getPlayer(int playerObjId)} * @param name Name of the player to get Instance * @return the player instance corresponding to the given name. */ public L2PcInstance getPlayer(String name) { return getPlayer(CharNameTable.getInstance().getIdByName(name)); } /** * @param objectId of the player to get Instance * @return the player instance corresponding to the given object ID. */ public L2PcInstance getPlayer(int objectId) { return _allPlayers.get(objectId); } /** * @param ownerId ID of the owner * @return the pet instance from the given ownerId. */ public L2PetInstance getPet(int ownerId) { return _petsInstance.get(ownerId); } /** * Add the given pet instance from the given ownerId. * @param ownerId ID of the owner * @param pet L2PetInstance of the pet * @return */ public L2PetInstance addPet(int ownerId, L2PetInstance pet) { return _petsInstance.put(ownerId, pet); } /** * Remove the given pet instance. * @param ownerId ID of the owner */ public void removePet(int ownerId) { _petsInstance.remove(ownerId); } /** * Remove the given pet instance. * @param pet the pet to remove */ public void removePet(L2PetInstance pet) { _petsInstance.remove(pet.getOwner().getObjectId()); } /** * 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) */ public void addVisibleObject(L2Object object, L2WorldRegion newRegion) { // TODO: this code should be obsoleted by protection in putObject func... if (object.isPlayer()) { L2PcInstance player = object.getActingPlayer(); if (!player.isTeleporting()) { final L2PcInstance old = getPlayer(player.getObjectId()); if (old != null) { _log.warning("Duplicate character!? Closing both characters (" + player.getName() + ")"); player.logout(); old.logout(); return; } addPlayerToWorld(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); } } /** * Adds the player to the world. * @param player the player to add */ public void addPlayerToWorld(L2PcInstance player) { _allPlayers.put(player.getObjectId(), player); } /** * Remove the player from the world. * @param player the player to remove */ public void removeFromAllPlayers(L2PcInstance player) { _allPlayers.remove(player.getObjectId()); } /** * 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 which the object was before removing */ public void removeVisibleObject(L2Object object, L2WorldRegion oldRegion) { if (object == null) { return; } 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()) { final Collection vObj = reg.getVisibleObjects().values(); for (L2Object obj : vObj) { if (obj != null) { obj.getKnownList().removeKnownObject(object); } } } // 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.isPlayer()) { final L2PcInstance player = object.getActingPlayer(); if (!player.isTeleporting()) { removeFromAllPlayers(player); } } } } /** * 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 * @return */ 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(); for (L2Object _object : vObj) { if ((_object == null) || _object.equals(object)) { continue; // skip our own character } else 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 * @return */ public List getVisibleObjects(L2Object object, int radius) { if ((object == null) || !object.isVisible()) { return new ArrayList<>(); } final 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(); for (L2Object _object : vObj) { if ((_object == null) || _object.equals(object)) { continue; // skip our own character } if (sqRadius > object.calculateDistance(_object, false, true)) { 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 * @return */ public List getVisibleObjects3D(L2Object object, int radius) { if ((object == null) || !object.isVisible()) { return new ArrayList<>(); } final 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(); for (L2Object _object : vObj) { if ((_object == null) || _object.equals(object)) { continue; // skip our own character } if (sqRadius > object.calculateDistance(_object, true, true)) { result.add(_object); } } } return result; } /** * 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 * @return all visible players of the L2WorldRegion object's and of its surrounding 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 for (L2Playable _object : _playables) { if ((_object == null) || _object.equals(object)) { continue; // skip our own character } if (!_object.isVisible()) { 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 movement

  • * @param point position of the object * @return */ public L2WorldRegion getRegion(Location 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[][] getWorldRegions() { 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)); } /** * Initialize the world regions. */ 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."); } /** * @return the current instance of L2World */ public static L2World getInstance() { return SingletonHolder._instance; } private static class SingletonHolder { protected static final L2World _instance = new L2World(); } }