테스트

aion-server 4.8

Gitteol
최고관리자 · 1 · 💬 0 클론/새로받기
 4.8 61f661d · 1 commits 새로받기(Pull)
game-server/src/com/aionemu/gameserver/world/World.java
package com.aionemu.gameserver.world;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Consumer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.aionemu.commons.utils.GenericValidator;
import com.aionemu.gameserver.dataholders.DataManager;
import com.aionemu.gameserver.dataholders.PlayerInitialData.LocationData;
import com.aionemu.gameserver.model.animations.ObjectDeleteAnimation;
import com.aionemu.gameserver.model.gameobjects.AionObject;
import com.aionemu.gameserver.model.gameobjects.Creature;
import com.aionemu.gameserver.model.gameobjects.Npc;
import com.aionemu.gameserver.model.gameobjects.VisibleObject;
import com.aionemu.gameserver.model.gameobjects.player.BindPointPosition;
import com.aionemu.gameserver.model.gameobjects.player.Player;
import com.aionemu.gameserver.model.gameobjects.siege.SiegeNpc;
import com.aionemu.gameserver.model.templates.spawns.basespawns.BaseSpawnTemplate;
import com.aionemu.gameserver.network.aion.serverpackets.SM_SYSTEM_MESSAGE;
import com.aionemu.gameserver.services.ShieldService;
import com.aionemu.gameserver.utils.audit.AuditLogger;
import com.aionemu.gameserver.utils.collections.CollectionUtil;
import com.aionemu.gameserver.world.container.PlayerContainer;
import com.aionemu.gameserver.world.exceptions.AlreadySpawnedException;
import com.aionemu.gameserver.world.exceptions.DuplicateAionObjectException;

/**
 * World object for storing and spawning, despawning etc players and other in-game objects. It also manage WorldMaps and instances.
 * 
 * @author -Nemesiss-, Source, Wakizashi
 */
public class World {

	private static final Logger log = LoggerFactory.getLogger(World.class);

	/**
	 * Container with all players that entered world.
	 */
	private final PlayerContainer allPlayers = new PlayerContainer();

	/**
	 * Container with all AionObjects in the world [ie Players, Npcs etc]
	 */
	private final Map<Integer, VisibleObject> allObjects = new ConcurrentHashMap<>();

	/**
	 * Container with all SiegeNpcs in the world [SiegeNpcs,SiegeProtectors etc]
	 */
	private final Map<Integer, Collection<SiegeNpc>> localSiegeNpcs = new HashMap<>();

	/**
	 * Container with all Npcs related to base spawns
	 */
	private final Map<Integer, List<Npc>> baseNpc = new ConcurrentHashMap<>();

	/**
	 * Container with all Npcs in the world
	 */
	private final Map<Integer, Npc> allNpcs = new ConcurrentHashMap<>();

	/**
	 * World maps supported by server.
	 */
	private final Map<Integer, WorldMap> worldMaps = new HashMap<>();

	private World() {
		DataManager.WORLD_MAPS_DATA.forEachParalllel(template -> {
			WorldMap worldMap = new WorldMap(template);
			synchronized (worldMaps) {
				worldMaps.put(template.getMapId(), worldMap);
			}
		});
		ShieldService.getInstance().logDetachedShields();
		log.info("World: " + worldMaps.size() + " world maps created.");
	}

	public static World getInstance() {
		return SingletonHolder.instance;
	}

	public void storeObject(VisibleObject object) {
		if (object.getPosition() == null) {
			log.error("Tried to add {} with null position in world", object);
			return;
		}
		VisibleObject oldObject = allObjects.putIfAbsent(object.getObjectId(), object);
		if (oldObject != null)
			throw new DuplicateAionObjectException(object, oldObject);

		if (object instanceof Npc npc) {
			if (object instanceof SiegeNpc siegeNpc) {
				Collection<SiegeNpc> npcs = localSiegeNpcs.get(siegeNpc.getSiegeId());
				if (npcs == null) {
					synchronized (localSiegeNpcs) {
						if (localSiegeNpcs.containsKey(siegeNpc.getSiegeId())) {
							npcs = localSiegeNpcs.get(siegeNpc.getSiegeId());
						} else {
							// We now have multi-threaded siege timers
							// This should be thread-safe
							npcs = new CopyOnWriteArrayList<>();
							localSiegeNpcs.put(siegeNpc.getSiegeId(), npcs);
						}
					}
				}

				npcs.add(siegeNpc);
			}

			if (object.getSpawn() instanceof BaseSpawnTemplate baseSpawnTemplate) {
				int baseId = baseSpawnTemplate.getId();
				if (!baseNpc.containsKey(baseId)) {
					baseNpc.putIfAbsent(baseId, new CopyOnWriteArrayList<>());
				}
				baseNpc.get(baseId).add(npc);
			}

			allNpcs.put(object.getObjectId(), npc);
		} else if (object instanceof Player player) {
			allPlayers.add(player);
		}
	}

	/**
	 * Despawns (if spawned) and completely removes the object from the world.<br>
	 * If the object was instantiated with {@code autoReleaseObjectId = true}, it's objectId will be released from IDFactory once it gets garbage
	 * collected (see {@link AionObject#AionObject(int, boolean)}).
	 * 
	 * @return True if removed, false if the object wasn't in world
	 */
	@SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
	public boolean removeObject(VisibleObject object) {
		boolean removed = false;
		synchronized (object) {
			VisibleObject worldObject = allObjects.get(object.getObjectId());
			if (worldObject == object) {
				try {
					if (object.isSpawned())
						despawn(object);
					object.getController().onDelete();
				} catch (Exception e) {
					log.error(object + " did not leave world cleanly", e);
				}
				removed = allObjects.remove(object.getObjectId(), object);
			} else if (worldObject != null) {
				log.warn("Attempt to remove " + object + " from world but ID already belongs to " + worldObject, new Exception());
			}
		}
		if (removed) {
			if (object instanceof Npc) {
				if (object instanceof SiegeNpc siegeNpc) {
					Collection<SiegeNpc> locSpawn = localSiegeNpcs.get(siegeNpc.getSiegeId());
					if (!GenericValidator.isBlankOrNull(locSpawn)) {
						locSpawn.remove(siegeNpc);
					}
				}

				if (object.getSpawn() instanceof BaseSpawnTemplate baseSpawnTemplate) {
					int baseId = baseSpawnTemplate.getId();
					baseNpc.get(baseId).remove(object);
				}

				allNpcs.remove(object.getObjectId());
			} else if (object instanceof Player player) {
				allPlayers.remove(player);
			}
		}
		return removed;
	}

	public Collection<SiegeNpc> getLocalSiegeNpcs(int locationId) {
		Collection<SiegeNpc> result = localSiegeNpcs.get(locationId);
		return result != null ? result : Collections.emptySet();
	}

	public List<Npc> getBaseSpawns(int baseId) {
		return baseNpc.get(baseId);
	}

	public Collection<Npc> getNpcs() {
		return allNpcs.values();
	}

	/**
	 * @see PlayerContainer#get(String)
	 */
	public Player getPlayer(String name) {
		return allPlayers.get(name);
	}

	/**
	 * @see PlayerContainer#get(int)
	 */
	public Player getPlayer(int objectId) {
		return allPlayers.get(objectId);
	}

	/**
	 * Finds VisibleObject by objectId.
	 * 
	 * @param objectId
	 *          - objectId of AionObject or null if not in world
	 * @return VisibleObject
	 */
	public VisibleObject findVisibleObject(int objectId) {
		return allObjects.get(objectId);
	}

	public boolean isInWorld(int objectId) {
		return allObjects.containsKey(objectId);
	}

	public WorldMap getWorldMap(int id) {
		return worldMaps.get(id);
	}

	/**
	 * Update position of VisibleObject [used when object is moving on one map instance]. Check if active map region changed and do all needed updates.
	 */
	public void updatePosition(VisibleObject object, float newX, float newY, float newZ, byte newHeading) {
		updatePosition(object, newX, newY, newZ, newHeading, true);
	}

	public void updatePosition(VisibleObject object, float newX, float newY, float newZ, byte newHeading, boolean updateKnownList) {
		if (!object.isSpawned()) { // despawned objects should never move
			log.warn("Can't update position of despawned object: {}", object, new Throwable());
			return;
		}

		WorldPosition position = object.getPosition();
		MapRegion oldRegion = position.getMapRegion();
		if (oldRegion == null) {
			if (object instanceof Player player) {
				if (!player.isStaff()) {
					AuditLogger.log(player, "is outside valid regions: " + position);
					// he will be sent to bind point in PlayerLeaveWorldService
					player.getClientConnection().close(SM_SYSTEM_MESSAGE.STR_KICK_CHARACTER());
				}
			} else {
				log.warn("Old MapRegion was null when trying to update position of {}", object, new Throwable());
			}
			return;
		}

		MapRegion newRegion = oldRegion.getParent().getRegion(newX, newY, newZ);
		if (newRegion == null) {
			log.warn("New MapRegion for {} doesn't exist at coordinates: Map {}, X {}, Y {}, Z {}", object, object.getWorldId(), newX, newY, newZ,
				new Throwable());
			if (object instanceof Creature creature)
				creature.getMoveController().abortMove();
			if (object instanceof Player player) {
				float x, y, z;
				int worldId;
				byte h = 0;

				if (player.getBindPoint() != null) {
					BindPointPosition bplist = player.getBindPoint();
					worldId = bplist.getMapId();
					x = bplist.getX();
					y = bplist.getY();
					z = bplist.getZ();
					h = bplist.getHeading();
				} else {
					LocationData locationData = DataManager.PLAYER_INITIAL_DATA.getSpawnLocation(player.getCommonData().getRace());
					worldId = locationData.getMapId();
					x = locationData.getX();
					y = locationData.getY();
					z = locationData.getZ();
				}
				setPosition(object, worldId, x, y, z, h);
			}
			return;
		}

		position.setXYZH(newX, newY, newZ, newHeading);

		if (newRegion != oldRegion) {
			if (object instanceof Creature creature) {
				oldRegion.revalidateZones(creature);
				newRegion.revalidateZones(creature);
			}
			oldRegion.remove(object);
			newRegion.add(object);
			position.setMapRegion(newRegion);
		}

		if (updateKnownList)
			object.updateKnownlist();
	}

	/**
	 * Set position of VisibleObject without spawning [object will be invisible]. If object is spawned it will be despawned first.
	 * 
	 * @return True, if position was set correctly.
	 */
	public boolean setPosition(VisibleObject object, int mapId, float x, float y, float z, byte heading) {
		int instanceId = 1;
		if (object.getPosition() != null && object.getPosition().getMapId() == mapId)
			instanceId = object.getInstanceId();
		return setPosition(object, mapId, instanceId, x, y, z, heading);
	}

	/**
	 * Set position of VisibleObject without spawning [object will be invisible]. If object is spawned it will be despawned first.
	 * 
	 * @return True, if position was set correctly.
	 */
	public boolean setPosition(VisibleObject object, int mapId, int instance, float x, float y, float z, byte heading) {
		if (object == null)
			return false;
		WorldPosition pos = createPosition(mapId, x, y, z, heading, instance);
		if (pos == null)
			return false;
		if (object.isSpawned())
			despawn(object);
		object.setPosition(pos);
		return true;
	}

	public WorldPosition createPosition(int mapId, float x, float y, float z, byte heading, int instanceId) {
		WorldMap map = getWorldMap(mapId);
		if (map == null)
			throw new NullPointerException("Failed to create position (invalid mapId: " + mapId + ")");
		if (map.getWorldMapInstance(instanceId) == null)
			throw new NullPointerException("Failed to create position (invalid instanceId " + instanceId + " for mapId " + mapId + ")");
		MapRegion mr = map.getWorldMapInstance(instanceId).getRegion(x, y, z);
		if (mr == null)
			throw new NullPointerException(
				"Failed to create position (invalid coords: x=" + x + ", y=" + y + ", z=" + z + " for mapId " + mapId + " in instanceId " + instanceId + ")");
		return new WorldPosition(mapId, x, y, z, heading, mr);
	}

	/**
	 * Spawns the object at the current position (use setPosition). Object will be visible by others and will see other objects.
	 */
	public void spawn(VisibleObject object) throws AlreadySpawnedException {
		if (object == null)
			return;
		WorldPosition position = object.getPosition();
		if (position.isSpawned())
			throw new AlreadySpawnedException(object);

		object.getController().onBeforeSpawn();
		position.setIsSpawned(true);

		position.getMapRegion().getParent().addObject(object);
		position.getMapRegion().add(object);
		object.getController().onAfterSpawn();

		object.updateKnownlist();
	}

	/**
	 * Despawns the object with the default delete animation.
	 * 
	 * @see #despawn(VisibleObject, ObjectDeleteAnimation)
	 */
	public void despawn(VisibleObject object) {
		despawn(object, ObjectDeleteAnimation.FADE_OUT);
	}

	/**
	 * Despawns the object, object will become invisible. All other objects will be noticed that this object is no longer visible.
	 */
	public void despawn(VisibleObject object, ObjectDeleteAnimation animation) {
		WorldPosition position = object.getPosition();
		try {
			object.getController().onDespawn();
		} finally {
			MapRegion oldMapRegion = position.getMapRegion();
			position.setIsSpawned(false);
			if (oldMapRegion != null) { // can be null if an instance gets deleted?
				oldMapRegion.getParent().removeObject(object);
				oldMapRegion.remove(object);
				if (object instanceof Creature creature)
					oldMapRegion.revalidateZones(creature);
			}
			object.clearKnownlist(animation);
		}
	}

	public void updateCachedPlayerName(String oldName, Player player) {
		allPlayers.updateCachedPlayerName(oldName, player);
	}

	public Collection<Player> getAllPlayers() {
		return allPlayers.getAllPlayers();
	}

	public void forEachPlayer(Consumer<Player> consumer) {
		CollectionUtil.forEach(allPlayers, consumer);
	}

	public void forEachObject(Consumer<VisibleObject> consumer) {
		CollectionUtil.forEach(allObjects.values(), consumer);
	}

	private static class SingletonHolder {

		protected static final World instance = new World();
	}

}

📎 첨부파일

댓글 작성 권한이 없습니다.
🏆 포인트 랭킹 TOP 10
순위 닉네임 포인트
1 no_profile 타키야겐지쪽지보내기 자기소개 아이디로 검색 전체게시물 102,949
2 no_profile 동가리쪽지보내기 자기소개 아이디로 검색 전체게시물 63,733
3 no_profile 라프텔쪽지보내기 자기소개 아이디로 검색 전체게시물 51,771
4 no_profile 불멸의행복쪽지보내기 자기소개 아이디로 검색 전체게시물 36,923
5 서번트쪽지보내기 자기소개 아이디로 검색 전체게시물 35,011
6 no_profile 닥터스쪽지보내기 자기소개 아이디로 검색 전체게시물 29,470
7 no_profile 검은고양이쪽지보내기 자기소개 아이디로 검색 전체게시물 29,077
8 no_profile Revolution쪽지보내기 자기소개 아이디로 검색 전체게시물 28,199
9 no_profile 보거스쪽지보내기 자기소개 아이디로 검색 전체게시물 26,731
10 no_profile 호롤롤로쪽지보내기 자기소개 아이디로 검색 전체게시물 17,020
알림 0