package org.reunionemu.jreunion.server;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.reunionemu.jcommon.ParsedItem;
import org.reunionemu.jcommon.Parser;
import org.reunionemu.jreunion.events.Event;
import org.reunionemu.jreunion.events.map.ItemDropEvent;
import org.reunionemu.jreunion.events.map.ItemPickupEvent;
import org.reunionemu.jreunion.events.map.MapEvent;
import org.reunionemu.jreunion.events.map.PlayerLoginEvent;
import org.reunionemu.jreunion.events.map.PlayerLogoutEvent;
import org.reunionemu.jreunion.events.map.SpawnEvent;
import org.reunionemu.jreunion.events.session.NewSessionEvent;
import org.reunionemu.jreunion.events.session.SessionEvent;
import org.reunionemu.jreunion.game.Entity;
import org.reunionemu.jreunion.game.Item;
import org.reunionemu.jreunion.game.LivingObject;
import org.reunionemu.jreunion.game.Npc;
import org.reunionemu.jreunion.game.NpcSpawn;
import org.reunionemu.jreunion.game.NpcType;
import org.reunionemu.jreunion.game.Party;
import org.reunionemu.jreunion.game.Pet;
import org.reunionemu.jreunion.game.Player;
import org.reunionemu.jreunion.game.PlayerSpawn;
import org.reunionemu.jreunion.game.Position;
import org.reunionemu.jreunion.game.RoamingItem;
import org.reunionemu.jreunion.game.Shop;
import org.reunionemu.jreunion.game.Spawn;
import org.reunionemu.jreunion.game.WorldObject;
import org.reunionemu.jreunion.game.npc.Mob;
import org.reunionemu.jreunion.server.Area.Field;
import org.reunionemu.jreunion.server.PacketFactory.Type;
/**
* @author Aidamina
* @license http://reunion.googlecode.com/svn/trunk/license.txt
*/
public class LocalMap extends Map implements Runnable{
private List<Spawn> npcSpawnList = new Vector<Spawn>();
private List<Spawn> playerSpawnList = new Vector<Spawn>();
private Area area = new Area();
private SessionList<Session> sessions = new SessionList<Session>();
private HashMap<Integer, Entity> entities = new HashMap<Integer, Entity>();
private Parser playerSpawnReference;
private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
private ScheduledFuture<?> mobsAI;
private Thread thread;
private Parser mobSpawnReference;
private Parser npcSpawnReference;
private PlayerSpawn defaultSpawn;
private World world;
private List<RoamingItem> roamingItemList = new Vector<RoamingItem>();
private List<Party> parties = new Vector<Party>();
private List<Shop> shops = new Vector<Shop>();
public World getWorld() {
return world;
}
public LocalMap(World world, int id) {
super(id);
this.world = world;
thread = new Thread(this);
thread.setDaemon(true);
thread.start();
this.addEventListener(SpawnEvent.class, this);
this.addEventListener(PlayerLoginEvent.class, this);
this.addEventListener(PlayerLogoutEvent.class, this);
this.addEventListener(ItemDropEvent.class, this);
this.addEventListener(ItemPickupEvent.class, this);
//if(world.getQuestManager().isEmpty())
// world.getQuestManager().loadQuests();
}
public Entity getEntity(int id) {
synchronized(entities){
return (Entity) entities.get(id);
}
}
public int getEntitiesListSize(){
return entities.size();
}
private int entityIter = 0;
public synchronized int createEntityId(Entity obj){
synchronized(this.entities){
if(!entities.containsValue(obj)){
int counter = 0;
while(entities.containsKey(entityIter)){
entityIter++;
if(entityIter >= Integer.MAX_VALUE) {
entityIter=0;
}
counter++;
if(counter >= Integer.MAX_VALUE)
throw new RuntimeException("No more available entity ids");
}
int id = entityIter;
obj.setEntityId(id);
entities.put(id, obj);
entityIter++;
return id;
} else {
return obj.getEntityId();
}
}
}
private void createMobSpawns() {
Iterator<ParsedItem> iter = mobSpawnReference.getItemListIterator();
while (iter.hasNext()) {
ParsedItem item = iter.next();
if (!item.checkMembers(new String[] { "ID", "X", "Y",
"Radius", "RespawnTime", "Type" })) {
LoggerFactory.getLogger(LocalMap.class).info("Error loading a mob spawn on map: "
+ getId());
continue;
}
NpcSpawn spawn = new NpcSpawn();
spawn.setId(Integer.parseInt(item.getMemberValue("Id")));
int posZ = item.getMemberValue("Z") == null ? 0 : Integer.parseInt(item.getMemberValue("Z"));
String rotValue = item.getMemberValue("Rotation");
double rotation = rotValue == null ? Double.NaN : Double.parseDouble(rotValue);
Position position = new Position(
Integer.parseInt(item.getMemberValue("X")),
Integer.parseInt(item.getMemberValue("Y")),
posZ,
this,
rotation);
spawn.setPosition(position);
spawn.setRadius(Integer.parseInt(item.getMemberValue("Radius")));
spawn.setNpcType(Integer.parseInt(item.getMemberValue("Type")));
spawn.setRespawnTime(Integer.parseInt(item.getMemberValue("RespawnTime")));
getNpcSpawnList().add(spawn);
spawn.spawn();
}
LoggerFactory.getLogger(LocalMap.class).info("Loaded "+getNpcSpawnList().size()+" mob spawns in "+getName());
}
public Parser getMobSpawnReference() {
return mobSpawnReference;
}
public Parser getNpcSpawnReference() {
return npcSpawnReference;
}
public List<RoamingItem> getRoamingItemList() {
return roamingItemList;
}
public RoamingItem getRoamingItem(int entityId){
for(RoamingItem roamingItem: getRoamingItemList()){
if(roamingItem.getItem().getEntityId() == entityId){
return roamingItem;
}
}
return null;
}
public void addRoamingItem(RoamingItem roamingItem){
roamingItemList.add(roamingItem);
}
public void removeRoamingItem(RoamingItem roamingItem){
roamingItemList.remove(roamingItem);
}
public List<Spawn> getNpcSpawnList(){
return npcSpawnList;
}
public List<Spawn> getPlayerSpawnList(){
return playerSpawnList;
}
public List<Entity> getEntities(){
return new Vector<Entity>(entities.values());
}
private void createNpcSpawns() {
int mobSpawnAmmount = getNpcSpawnList().size();
Iterator<ParsedItem> iter = npcSpawnReference.getItemListIterator();
while (iter.hasNext()) {
ParsedItem item = iter.next();
if (!item.checkMembers(new String[] { "ID", "X", "Y",
"Rotation", "Type" })) {
LoggerFactory.getLogger(LocalMap.class).warn("Failed to load npc spawn {name:"+item.getName()+"} on map "
+ this);
continue;
}
NpcSpawn spawn = new NpcSpawn();
spawn.setId(Integer.parseInt(item.getMemberValue("Id")));
int posZ = item.getMemberValue("Z") == null ? 0 : Integer.parseInt(item.getMemberValue("Z"));
double rotation = item.getMemberValue("Rotation") == null ? Double.NaN : Double.parseDouble(item.getMemberValue("Rotation"));
Position position = new Position(
Integer.parseInt(item.getMemberValue("X")),
Integer.parseInt(item.getMemberValue("Y")),
posZ,
this,
rotation);
spawn.setPosition(position);
spawn.setNpcType(Integer.parseInt(item.getMemberValue("Type")));
getNpcSpawnList().add(spawn);
spawn.spawn();
}
LoggerFactory.getLogger(LocalMap.class).info("Loaded "+(getNpcSpawnList().size()-mobSpawnAmmount)+" npc spawns in "+getName());
}
/**
* @return Returns the mapid.
*/
public Area getArea() {
return area;
}
public Parser getPlayerSpawnReference() {
return playerSpawnReference;
}
public void load() {
super.load();
thread.setName("Map: "+getName());
LoggerFactory.getLogger(LocalMap.class).info("Loading "+getName()+"["+getId()+"]");
if(!Server.getInstance().getNetwork().register(getAddress())){
System.out.println("huh?");
return;
}
playerSpawnReference = new Parser();
mobSpawnReference = new Parser();
npcSpawnReference = new Parser();
loadFromReference(getId());
getNpcSpawnList().clear();
createMobSpawns();
createNpcSpawns();
createPlayerSpawns();
synchronized(entities){
roamingItemList = DatabaseUtils.getDinamicInstance().loadRoamingItems(this);
for(RoamingItem roamingItem : roamingItemList){
//TODO: A better way to manage items going in and out of the map
int itemEntityId = createEntityId(roamingItem);
roamingItem.getItem().setEntityId(itemEntityId);
roamingItem.startDeleteTimer(true);
}
}
LoggerFactory.getLogger(LocalMap.class).info("Loaded "+getRoamingItemList().size()+" roaming items in "+getName());
//startMobsAI(1000);
/*
executorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
List<Entity> objects = null;
synchronized(entities){
objects = new Vector<Entity>(entities.values());
}
for (Entity entity : objects) {
if (entity instanceof Npc) {
if (((Npc<?>) entity).getType() instanceof Mob) {
((Npc<?>) entity).work();
}
}
}
}
}, 0, 1500, TimeUnit.MILLISECONDS);
*/
LoggerFactory.getLogger(LocalMap.class).info(getName()+" running on "+getAddress());
}
public void startMobsAI(long period){
mobsAI = executorService.scheduleAtFixedRate(createMobsAI(), 0, period, TimeUnit.MILLISECONDS);
}
public void stopMobsAI(){
mobsAI.cancel(false);
}
public ScheduledFuture<?> getMobsAI(){
return mobsAI;
}
public Runnable createMobsAI(){
return new REHandler(new Runnable() {
@Override
public void run() {
List<Entity> objects = null;
synchronized (entities) {
objects = new Vector<Entity>(entities.values());
}
for (Entity entity : objects) {
if (entity instanceof Npc) {
Npc<?> npc = (Npc<?>) entity;
if (npc.getType() instanceof Mob) {
npc.work();
}
}
}
}
});
}
private void createPlayerSpawns() {
int defaultSpawnId = Integer.parseInt(Reference.getInstance().getMapReference().getItemById(this.getId()).getMemberValue("DefaultSpawnId"));
getPlayerSpawnList().clear();
Iterator<ParsedItem> iter = playerSpawnReference.getItemListIterator();
while (iter.hasNext()) {
ParsedItem item = iter.next();
if (!item.checkMembers(new String[] { "ID", "X", "Y"
})) {
LoggerFactory.getLogger(LocalMap.class).info("Error loading a player spawn on map: "
+ getId());
continue;
}
PlayerSpawn spawn = new PlayerSpawn();
spawn.setId(Integer.parseInt(item.getMemberValue("Id")));
int posZ = item.getMemberValue("Z") == null ? 0 : Integer.parseInt(item.getMemberValue("Z"));
double rotation = item.getMemberValue("Rotation") == null ? Double.NaN : Double.parseDouble(item.getMemberValue("Rotation"));
Position position = new Position(
Integer.parseInt(item.getMemberValue("X")),
Integer.parseInt(item.getMemberValue("Y")),
posZ,
this,
rotation);
int targetX = item.getMemberValue("TargetX")==null?-1:Integer.parseInt(item.getMemberValue("TargetX"));
int targetY = item.getMemberValue("TargetY")==null?-1:Integer.parseInt(item.getMemberValue("TargetY"));
int targetWidth = item.getMemberValue("TargetWidth")==null?-1:Integer.parseInt(item.getMemberValue("TargetWidth"));
int targetHeight = item.getMemberValue("TargetHeight")==null?-1:Integer.parseInt(item.getMemberValue("TargetHeight"));
int radius = item.getMemberValue("Radius")==null?0:Integer.parseInt(item.getMemberValue("Radius"));
spawn.setRadius(radius);
spawn.setTargetX(targetX);
spawn.setTargetY(targetY);
spawn.setTargetWidth(targetWidth);
spawn.setTargetHeight(targetHeight);
spawn.setPosition(position);
if(Integer.parseInt(item.getMemberValue("ID"))==defaultSpawnId){
this.defaultSpawn = spawn;
}
getPlayerSpawnList().add(spawn);
}
}
public void loadFromReference(int id) {
try{
playerSpawnReference.Parse(Reference.getDataResource(Reference.getInstance().getMapReference()
.getItemById(id).getMemberValue("PlayerSpawn")));
//Reference.getDataResource(filename)
mobSpawnReference.Parse(Reference.getDataResource(Reference.getInstance().getMapReference()
.getItemById(id).getMemberValue("MobSpawn")));
npcSpawnReference.Parse(Reference.getDataResource(Reference.getInstance().getMapReference()
.getItemById(id).getMemberValue("NpcSpawn")));
} catch(Exception e){
LoggerFactory.getLogger(this.getClass()).warn("Exception",e);
}
Area area = getArea();
area.load(Reference.getDataResource(Reference.getInstance().getMapReference()
.getItemById(id).getMemberValue("PlayerArea")),Field.PLAYER);
area.load(Reference.getDataResource(Reference.getInstance().getMapReference()
.getItemById(id).getMemberValue("MobArea")),Field.MOB);
area.load(Reference.getDataResource(Reference.getInstance().getMapReference()
.getItemById(id).getMemberValue("PvpArea")),Field.PVP);
}
public SessionList<Session> GetSessions(Position position){
SessionList<Session> results = new SessionList<Session>();
synchronized(sessions){
for(Session session:sessions){
if(session.contains(position)){
results.add(session);
}
}
}
return results;
}
public SessionList<Session> GetSessions(WorldObject entity){
SessionList<Session> results = new SessionList<Session>();
synchronized(sessions){
for(Session session:sessions){
if(session.contains(entity)){
results.add(session);
}
}
}
return results;
}
@Override
public void handleEvent(Event event) {
//LoggerFactory.getLogger(LocalMap.class).info(event);
if(event instanceof MapEvent){
LocalMap map = ((MapEvent)event).getMap();
if(event instanceof SpawnEvent){
SpawnEvent spawnEvent = (SpawnEvent)event;
LivingObject entity = spawnEvent.getSpawnee();
SessionList<Session> list = GetSessions(entity.getPosition());
synchronized(entities) {
list.enter(entity, false);
}
if(entity instanceof Player){
Player player = (Player)entity;
Session session = player.getSession();
sessions.add(session);
player.getClient().setState(Client.State.INGAME);
if(list.contains(session)){
//TODO: Gracefully handle respawn
throw new RuntimeException("This should never happen! 2");
}
list.sendPacket(Type.IN_CHAR, player, true);
} else if(entity instanceof Pet){
Pet pet = (Pet)entity;
if(pet.getState() == 12){
list.sendPacket(Type.IN_PET, pet.getOwner(), true);
}
} else {
list.sendPacket(Type.IN_NPC, entity, true);
}
entity.update();
} else
if(event instanceof ItemDropEvent){
ItemDropEvent itemDropEvent = (ItemDropEvent)event;
RoamingItem roamingItem = itemDropEvent.getRoamingItem();
//select all the players within roaming item session
SessionList<Session> list = GetSessions(roamingItem.getPosition());
Item<?> item = roamingItem.getItem();
synchronized(entities) {
createEntityId(roamingItem);
item.setEntityId(roamingItem.getEntityId());
addRoamingItem(roamingItem);
}
list.enter(roamingItem, false);
DatabaseUtils.getDinamicInstance().saveRoamingItem(roamingItem);
list.sendPacket(Type.DROP, roamingItem);
} else
if(event instanceof ItemPickupEvent){
ItemPickupEvent itemPickupEvent = (ItemPickupEvent)event;
RoamingItem roamingItem = itemPickupEvent.getRoamingItem();
Player player = itemPickupEvent.getPlayer();
// select other players within roaming item session
SessionList<Session> otherPlayersList = player.getInterested().getSessions();
Item<?> item = roamingItem.getItem();
synchronized(entities) {
removeRoamingItem(roamingItem);
removeEntity(roamingItem);
}
player.getClient().sendPacket(Type.OUT, roamingItem); //sent to the client owner only
player.pickItem(item, -1); //sent to the client owner only
player.getClient().sendPacket(Type.PICKUP, player);
otherPlayersList.sendPacket(Type.PICKUP, player);
otherPlayersList.exit(roamingItem, true); //sent to other clients
DatabaseUtils.getDinamicInstance().deleteRoamingItem(item);
} else
if(event instanceof PlayerLoginEvent){
PlayerLoginEvent playerLoginEvent = (PlayerLoginEvent)event;
Player player = playerLoginEvent.getPlayer();
Session session = new Session(player);
Position position = playerLoginEvent.getPosition();
if(position==null){
defaultSpawn.spawn(player);
} else {
new PlayerSpawn(position).spawn(player);
}
if(mobsAI == null){
startMobsAI(1000);
} else if(mobsAI.isCancelled()){
startMobsAI(1000);
}
} else
if(event instanceof PlayerLogoutEvent){
PlayerLogoutEvent playerLogoutEvent = (PlayerLogoutEvent)event;
Player player = playerLogoutEvent.getPlayer();
Pet pet = player.getPet();
Session playerSession = player.getSession();
if(playerSession!=null){
playerSession.close();
}
synchronized(entities) {
sessions.remove(playerSession);
entities.remove(player.getEntityId());
if(pet != null && pet.getState() == 12){
entities.remove(pet.getEntityId());
}
}
//remove player from other players session
SessionList<Session> playerList = player.getInterested().getSessions();
playerList.exit(player, false);
playerList.sendPacket(Type.OUT, player);
//remove pet from other players session
if(pet != null && pet.getState() == 12){
SessionList<Session> petList = pet.getInterested().getSessions();
petList.exit(player, false);
petList.sendPacket(Type.OUT, pet);
}
//remove player from party
if(player.getParty() != null){
player.getParty().exit(player);
}
//remove player shop from local map
if(player.getShop() != null){
player.getShop().close();
}
player.save();
world.getPlayerManager().removePlayer(player);
if(mobsAI != null && !mobsAI.isCancelled()){
if(this.getPlayerList().size() == 0){
stopMobsAI();
}
}
} else if(event instanceof SessionEvent) {
Session session = ((SessionEvent)event).getSession();
if(event instanceof NewSessionEvent){
NewSessionEvent newSessionEvent = (NewSessionEvent)event;
synchronized(sessions){
this.sessions.add(session);
}
}
}
}
}
@Override
public void run() {
while(true){
try {
synchronized(this){
this.wait();
}
//LoggerFactory.getLogger(LocalMap.class).info(this+" work");
List<Entity> objects = null;
SessionList<Session> sessionList = null;
synchronized(this.entities){
objects = new Vector<Entity>(this.entities.values());
}
synchronized(this.sessions){
sessionList = (SessionList<Session>) this.sessions.clone();
}
for(Entity entity: objects){
try{
for(Session session: sessionList){
Player owner = session.getOwner();
if(entity instanceof WorldObject){
WorldObject object = (WorldObject)entity;
if(owner.equals(entity))
continue;
if(entity instanceof Pet){
if(((Pet)entity).getOwner() == owner){
continue;
}
}
boolean within = owner.getPosition().within(object.getPosition(), owner.getSessionRadius());
boolean contains = session.contains(object);
if(contains && !within) {
session.exit(object);
} else if(!contains && within) {
session.enter(object);
}
}
}
/*
if(entity instanceof Npc){
if(((Npc<?>)entity).getType() instanceof Mob){
Npc<?> npc = (Npc<?>)entity;
SessionList<Session> list = GetSessions(npc.getPosition());
if(list.size() > 0){
scheduleServiceOnce(npc, 1000);
}
}
}
*/
}catch(Exception e ){
LoggerFactory.getLogger(this.getClass()).warn("Exception in mapworker", e);
}
}
} catch (Exception e) {
LoggerFactory.getLogger(this.getClass()).warn("Exception",e);
throw new RuntimeException(e);
}
finally{
}
}
}
public void addEntity(Entity entity) {
synchronized(entities){
if (!entities.containsValue(entity)) {
entities.put(entity.getEntityId(), entity);
}
}
}
public Entity removeEntity(Entity entity) {
synchronized(entities){
int entitiId = entity.getEntityId();
if(entities.containsValue(entity)){
Entity removedEntity = entities.remove(entitiId);
return removedEntity;
}
}
return null;
}
public List<Player> getPlayerList(){
List<Entity> entitiesList = new Vector<Entity> (entities.values());
List<Player> playerList = new Vector<Player> ();
for(Entity entity : entitiesList){
if(entity instanceof Player){
playerList.add((Player)entity);
}
}
return playerList;
}
public PlayerSpawn getDefaultSpawn(){
return defaultSpawn;
}
public String toString(){
StringBuffer buffer = new StringBuffer();
buffer.append("{");
buffer.append("id:");
buffer.append(getId());
buffer.append(", ");
buffer.append("name:");
buffer.append(getName());
buffer.append("}");
return buffer.toString();
}
public List<Party> getPartiesList() {
return parties;
}
public void inviteParty(Player member, Player newMember, int expOption, int itemOption){
if(member.getParty() == null){
addParty(new Party(member, expOption, itemOption));
}
member.getParty().request(newMember);
}
public void addParty(Party party){
if(!parties.contains(party)){
parties.add(party);
}
}
public void removeParty(Party party){
while(parties.contains(party)){
parties.remove(party);
}
}
public Party getParty(int memberEntityId){
for(Party party : parties){
for(Player member : party.getMembers()){
if(member.getEntityId() == memberEntityId)
return party;
}
}
return null;
}
public List<Shop> getShopList() {
return shops;
}
public void addShop(Shop shop){
if(!shops.contains(shop)){
shops.add(shop);
}
}
public void removeShop(Shop shop){
while(shops.contains(shop)){
shops.remove(shop);
}
}
public Shop getShop(Player player){
for(Shop shop : shops){
if(shop.getOwner() == player)
return shop;
}
return null;
}
public Shop getShopBuying(Player buyer){
for(Shop shop : shops){
for(Shop buyerShop : shop.getPlayersBuying()){
if(buyerShop.getOwner() == buyer)
return shop;
}
}
return null;
}
}