com.l2jfree.gameserver.model.entity.Instance.java Source code

Java tutorial

Introduction

Here is the source code for com.l2jfree.gameserver.model.entity.Instance.java

Source

/*
 * 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.l2jfree.gameserver.model.entity;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import javax.xml.parsers.DocumentBuilderFactory;

import javolution.util.FastMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

import com.l2jfree.Config;
import com.l2jfree.gameserver.Announcements;
import com.l2jfree.gameserver.ThreadPoolManager;
import com.l2jfree.gameserver.datatables.DoorTable;
import com.l2jfree.gameserver.datatables.NpcTable;
import com.l2jfree.gameserver.gameobjects.L2Npc;
import com.l2jfree.gameserver.gameobjects.L2Player;
import com.l2jfree.gameserver.gameobjects.instance.L2DoorInstance;
import com.l2jfree.gameserver.gameobjects.templates.L2NpcTemplate;
import com.l2jfree.gameserver.idfactory.IdFactory;
import com.l2jfree.gameserver.instancemanager.InstanceManager;
import com.l2jfree.gameserver.instancemanager.MapRegionManager;
import com.l2jfree.gameserver.model.Location;
import com.l2jfree.gameserver.model.mapregion.TeleportWhereType;
import com.l2jfree.gameserver.model.world.L2World;
import com.l2jfree.gameserver.model.world.spawn.L2Spawn;
import com.l2jfree.gameserver.network.SystemChatChannelId;
import com.l2jfree.gameserver.network.SystemMessageId;
import com.l2jfree.gameserver.network.packets.server.CreatureSay;
import com.l2jfree.gameserver.network.packets.server.SystemMessage;
import com.l2jfree.util.L2FastSet;

/**
 * @author evill33t
 * 
 */
public class Instance {
    private final static Log _log = LogFactory.getLog(Instance.class);

    public interface InstanceFactory {
        public Instance createInstance(int instanceId, String template);

        public String[] getInstanceTemplates();
    }

    private static final Map<String, InstanceFactory> _factories = new HashMap<String, InstanceFactory>();

    public static void registerInstanceFactory(InstanceFactory factory) {
        for (String template : factory.getInstanceTemplates())
            _factories.put(template, factory);
    }

    public static Instance createInstance(int id, String template) {
        final InstanceFactory factory = _factories.get(template);

        if (factory != null)
            return factory.createInstance(id, template);

        return new Instance(id, template);
    }

    private final int _id;
    private int _template;
    private Location _tp;
    private String _name;
    private final Set<Integer> _players = new L2FastSet<Integer>().setShared(true);
    private final Set<L2Npc> _npcs = new L2FastSet<L2Npc>().setShared(true);
    private final Map<Integer, L2DoorInstance> _doors = new FastMap<Integer, L2DoorInstance>().setShared(true);
    private L2DoorInstance[] _doorArray;
    private Location _spawnLoc;
    private boolean _allowSummon = true;
    private boolean _isPvPInstance = false;
    protected ScheduledFuture<?> _checkTimeUpTask = null;
    private long _emptyDestroyTime = -1;
    private long _lastLeft = -1;
    private long _instanceEndTime = -1;

    public Instance(int id) {
        _id = id;
        _template = -1;
    }

    public Instance(int id, String fileName) {
        this(id);

        loadInstanceTemplate(fileName);
    }

    public int getId() {
        return _id;
    }

    public int getTemplate() {
        return _template;
    }

    public String getName() {
        return _name;
    }

    public void setName(String name) {
        _name = name;
    }

    public void setReturnTeleport(int tpx, int tpy, int tpz) {
        if (tpx == 0 && tpy == 0 && tpz == 0)
            _tp = null;
        else
            _tp = new Location(tpx, tpy, tpz);
    }

    public Location getReturnTeleport() {
        return _tp;
    }

    /**
     * Returns whether summon friend type skills are allowed for this instance
     */
    public boolean isSummonAllowed() {
        return _allowSummon;
    }

    /**
     * Sets the status for the instance for summon friend type skills
     */
    public void setAllowSummon(boolean b) {
        _allowSummon = b;
    }

    /**
     * Returns true if entire instance is PvP zone
     */
    public boolean isPvPInstance() {
        return _isPvPInstance;
    }

    /**
     * Sets PvP zone status of the instance
     */
    public void setPvPInstance(boolean b) {
        _isPvPInstance = b;
    }

    /**
     * Set the instance duration task
     * @param duration in milliseconds
     */
    public void setDuration(int duration) {
        cancelTimer();
        if (duration > 0) {
            _checkTimeUpTask = ThreadPoolManager.getInstance().scheduleGeneral(new CheckTimeUp(duration), 500);
            _instanceEndTime = System.currentTimeMillis() + duration + 500;
        }
    }

    /**
     * Set time before empty instance will be removed
     * 
     * @param time in milliseconds
     */
    public void setEmptyDestroyTime(long time) {
        _emptyDestroyTime = time;
    }

    public boolean containsPlayer(Integer objectId) {
        return _players.contains(objectId);
    }

    public void addPlayer(Integer objectId) {
        _players.add(objectId);
    }

    public void removePlayer(Integer objectId) {
        if (_players.remove(objectId) && _players.isEmpty() && _emptyDestroyTime >= 0) {
            _lastLeft = System.currentTimeMillis();
            setDuration((int) (_instanceEndTime - System.currentTimeMillis() - 1000));
        }
    }

    public void ejectPlayer(Integer objectId) {
        L2Player player = L2World.getInstance().findPlayer(objectId);
        if (player != null && player.isSameInstance(getId())) {
            if (!player.isInMultiverse())
                player.setInstanceId(0);
            //player.sendPacket(SystemMessageId.INSTANCE_ZONE_DELETED_CANT_ACCESSED);
            if (_tp == null)
                player.teleToLocation(TeleportWhereType.Town);
            else
                player.teleToLocation(_tp);
        }
    }

    public void addNpc(L2Npc npc) {
        _npcs.add(npc);
    }

    public void removeNpc(L2Npc npc) {
        _npcs.remove(npc);
    }

    public void removeDoor(L2DoorInstance door) {
        _doors.remove(door.getDoorId());
        _doorArray = null;
    }

    /**
     * Adds a door into the instance
     * @param doorId - from doors.csv
     * @param open - initial state of the door
     */
    public void addDoor(int doorId, boolean open) {
        if (_doors.containsKey(doorId)) {
            _log.warn("Door ID " + doorId + " already exists in instance " + getId());
            return;
        }

        L2DoorInstance temp = DoorTable.getInstance().getDoor(doorId);
        L2DoorInstance newdoor = new L2DoorInstance(IdFactory.getInstance().getNextId(), temp.getTemplate(),
                temp.getDoorId(), temp.getName(), temp.isUnlockable());
        newdoor.setInstanceId(getId());
        newdoor.setRange(temp.getXMin(), temp.getYMin(), temp.getZMin(), temp.getXMax(), temp.getYMax(),
                temp.getZMax());
        try {
            newdoor.setMapRegion(MapRegionManager.getInstance().getRegion(temp.getX(), temp.getY(), temp.getZ()));
        } catch (Exception e) {
            _log.fatal("Error in door data, ID:" + temp.getDoorId(), e);
        }
        newdoor.getStatus().setCurrentHpMp(newdoor.getMaxHp(), newdoor.getMaxMp());
        newdoor.setOpen(open);
        newdoor.getPosition().setXYZInvisible(temp.getX(), temp.getY(), temp.getZ());
        newdoor.spawnMe(newdoor.getX(), newdoor.getY(), newdoor.getZ());

        _doors.put(newdoor.getDoorId(), newdoor);
        _doorArray = null;
    }

    public Set<Integer> getPlayers() {
        return _players;
    }

    public Set<L2Npc> getNpcs() {
        return _npcs;
    }

    public L2DoorInstance[] getDoors() {
        if (_doorArray == null)
            _doorArray = _doors.values().toArray(new L2DoorInstance[_doors.size()]);

        return _doorArray;
    }

    public L2DoorInstance getDoor(int doorId) {
        return _doors.get(doorId);
    }

    /**
     * Returns the spawn location for this instance to be used when leaving the instance
     * 
     * @return
     */
    public Location getSpawnLoc() {
        return _spawnLoc;
    }

    public void setSpawnLoc(Location loc) {
        _spawnLoc = loc;
    }

    public void removePlayers() {
        for (Integer objectId : _players) {
            removePlayer(objectId);
            ejectPlayer(objectId);
        }
        _players.clear();
    }

    public void removeNpcs() {
        for (L2Npc mob : _npcs) {
            if (mob != null) {
                if (mob.getSpawn() != null)
                    mob.getSpawn().stopRespawn();
                mob.deleteMe();
            }
        }
        _npcs.clear();
    }

    public void removeDoors() {
        for (L2DoorInstance door : _doors.values()) {
            if (door != null)
                door.decayMe();
        }
        _doors.clear();
        _doorArray = null;
    }

    public void loadInstanceTemplate(String filename) {
        Document doc = null;
        File xml = new File(Config.DATAPACK_ROOT, "data/instances/" + filename);

        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setValidating(false); // DTD isn't used
            factory.setIgnoringComments(true);
            // Such validation will not find element 'instance'
            //SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
            //factory.setSchema(sf.newSchema(new File(Config.DATAPACK_ROOT, "data/templates/instances.xsd")));
            doc = factory.newDocumentBuilder().parse(xml);

            for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling()) {
                if ("instance".equalsIgnoreCase(n.getNodeName())) {
                    parseInstance(n);
                }
            }
        } catch (IOException e) {
            _log.warn("Instance: can not find " + xml.getAbsolutePath() + " !", e);
        } catch (Exception e) {
            _log.warn("Instance: error while loading " + xml.getAbsolutePath() + " !", e);
        }
    }

    private void parseInstance(Node n) throws Exception {
        L2Spawn spawnDat;
        L2NpcTemplate npcTemplate;
        String name;
        name = n.getAttributes().getNamedItem("name").getNodeValue();
        setName(name);
        Node template = n.getAttributes().getNamedItem("template");
        if (template != null)
            _template = Integer.parseInt(template.getNodeValue());

        Node a;
        Node first = n.getFirstChild();
        for (n = first; n != null; n = n.getNextSibling()) {
            if ("activityTime".equalsIgnoreCase(n.getNodeName())) {
                a = n.getAttributes().getNamedItem("val");
                if (a != null) {
                    _checkTimeUpTask = ThreadPoolManager.getInstance()
                            .scheduleGeneral(new CheckTimeUp(Integer.parseInt(a.getNodeValue()) * 60000), 15000);
                    _instanceEndTime = System.currentTimeMillis()
                            + _checkTimeUpTask.getDelay(TimeUnit.MILLISECONDS);
                }
            }
            /*else if ("timeDelay".equalsIgnoreCase(n.getNodeName()))
            {
               a = n.getAttributes().getNamedItem("val");
               if (a != null)
                  setTimeDelay(Integer.parseInt(a.getNodeValue()));
            }*/
            else if ("allowSummon".equalsIgnoreCase(n.getNodeName())) {
                a = n.getAttributes().getNamedItem("val");
                if (a != null)
                    setAllowSummon(Boolean.parseBoolean(a.getNodeValue()));
            } else if ("emptyDestroyTime".equalsIgnoreCase(n.getNodeName())) {
                a = n.getAttributes().getNamedItem("val");
                if (a != null)
                    _emptyDestroyTime = Long.parseLong(a.getNodeValue()) * 1000;
            } else if ("PvPInstance".equalsIgnoreCase(n.getNodeName())) {
                a = n.getAttributes().getNamedItem("val");
                if (a != null)
                    setPvPInstance(Boolean.parseBoolean(a.getNodeValue()));
            } else if ("returnTeleport".equalsIgnoreCase(n.getNodeName())) {
                int tpx = 0, tpy = 0, tpz = 0;

                tpx = Integer.parseInt(n.getAttributes().getNamedItem("x").getNodeValue());
                tpy = Integer.parseInt(n.getAttributes().getNamedItem("y").getNodeValue());
                tpz = Integer.parseInt(n.getAttributes().getNamedItem("z").getNodeValue());

                setReturnTeleport(tpx, tpy, tpz);
            } else if ("doorList".equalsIgnoreCase(n.getNodeName())) {
                for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling()) {
                    int doorId = 0;
                    boolean doorState = false;
                    if ("door".equalsIgnoreCase(d.getNodeName())) {
                        doorId = Integer.parseInt(d.getAttributes().getNamedItem("doorId").getNodeValue());
                        if (d.getAttributes().getNamedItem("open") != null)
                            doorState = Boolean.parseBoolean(d.getAttributes().getNamedItem("open").getNodeValue());
                        addDoor(doorId, doorState);
                    }
                }
            } else if ("spawnList".equalsIgnoreCase(n.getNodeName())) {
                for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling()) {
                    int npcId = 0, x = 0, y = 0, z = 0, respawn = 0, heading = 0, amount = 1;

                    if ("spawn".equalsIgnoreCase(d.getNodeName())) {
                        npcId = Integer.parseInt(d.getAttributes().getNamedItem("npcId").getNodeValue());
                        x = Integer.parseInt(d.getAttributes().getNamedItem("x").getNodeValue());
                        y = Integer.parseInt(d.getAttributes().getNamedItem("y").getNodeValue());
                        z = Integer.parseInt(d.getAttributes().getNamedItem("z").getNodeValue());
                        heading = Integer.parseInt(d.getAttributes().getNamedItem("heading").getNodeValue());
                        // these have default values in schema, but DF doesn't seem to care
                        Node opt = d.getAttributes().getNamedItem("respawnDelay");
                        if (opt != null)
                            respawn = Integer.parseInt(opt.getNodeValue());
                        opt = d.getAttributes().getNamedItem("amount");
                        if (opt != null)
                            amount = Integer.parseInt(opt.getNodeValue());

                        npcTemplate = NpcTable.getInstance().getTemplate(npcId);
                        if (npcTemplate != null) {
                            spawnDat = new L2Spawn(npcTemplate);
                            spawnDat.setLocx(x);
                            spawnDat.setLocy(y);
                            spawnDat.setLocz(z);
                            spawnDat.setAmount(amount);
                            spawnDat.setHeading(heading);
                            spawnDat.setRespawnDelay(respawn);
                            if (respawn == 0)
                                spawnDat.stopRespawn();
                            else
                                spawnDat.startRespawn();
                            spawnDat.setInstanceId(getId());
                            spawnDat.doSpawn();
                        } else {
                            _log.warn("Instance: Data missing in NPC table for ID: " + npcTemplate + " in Instance "
                                    + getId());
                        }
                    }
                }
            } else if ("spawnPoint".equalsIgnoreCase(n.getNodeName())) {
                try {
                    int x = Integer.parseInt(n.getAttributes().getNamedItem("x").getNodeValue());
                    int y = Integer.parseInt(n.getAttributes().getNamedItem("y").getNodeValue());
                    int z = Integer.parseInt(n.getAttributes().getNamedItem("z").getNodeValue());

                    _spawnLoc = new Location(x, y, z);
                } catch (Exception e) {
                    _log.warn("Error parsing instance xml: ", e);
                }
            }
        }
        if (_log.isDebugEnabled())
            _log.info(name + " Instance Template for Instance " + getId() + " loaded");
    }

    protected void doCheckTimeUp(int remaining) {
        CreatureSay cs = null;
        int timeLeft;
        int interval;

        if (_players.isEmpty() && _emptyDestroyTime == 0) {
            remaining = 0;
            interval = 500;
        } else if (_players.isEmpty() && _emptyDestroyTime > 0) {
            Long emptyTimeLeft = _lastLeft + _emptyDestroyTime - System.currentTimeMillis();
            if (emptyTimeLeft <= 0) {
                interval = 0;
                remaining = 0;
            } else if (remaining > 300000 && emptyTimeLeft > 300000) {
                interval = 300000;
                remaining = remaining - 300000;
            } else if (remaining > 60000 && emptyTimeLeft > 60000) {
                interval = 60000;
                remaining = remaining - 60000;
            } else if (remaining > 30000 && emptyTimeLeft > 30000) {
                interval = 30000;
                remaining = remaining - 30000;
            } else {
                interval = 10000;
                remaining = remaining - 10000;
            }
        } else if (remaining > 300000) {
            timeLeft = remaining / 60000;
            interval = 300000;
            SystemMessage sm = new SystemMessage(SystemMessageId.DUNGEON_EXPIRES_IN_S1_MINUTES);
            sm.addString(Integer.toString(timeLeft));
            Announcements.getInstance().announceToInstance(sm, getId());
            remaining = remaining - 300000;
        } else if (remaining > 60000) {
            timeLeft = remaining / 60000;
            interval = 60000;
            SystemMessage sm = new SystemMessage(SystemMessageId.DUNGEON_EXPIRES_IN_S1_MINUTES);
            sm.addString(Integer.toString(timeLeft));
            Announcements.getInstance().announceToInstance(sm, getId());
            remaining = remaining - 60000;
        } else if (remaining > 30000) {
            timeLeft = remaining / 1000;
            interval = 30000;
            cs = new CreatureSay(0, SystemChatChannelId.Chat_Alliance, "Notice", timeLeft + " seconds left.");
            remaining = remaining - 30000;
        } else {
            timeLeft = remaining / 1000;
            interval = 10000;
            cs = new CreatureSay(0, SystemChatChannelId.Chat_Alliance, "Notice", timeLeft + " seconds left.");
            remaining = remaining - 10000;
        }
        if (cs != null) {
            for (int objectId : _players) {
                L2Player player = L2World.getInstance().findPlayer(objectId);
                if (player != null && player.isSameInstance(getId())) {
                    player.sendPacket(cs);
                }
            }
        }

        cancelTimer();
        scheduleCheckTimeUp(remaining, interval);
    }

    private class CheckTimeUp implements Runnable {
        private final int _remaining;

        public CheckTimeUp(int remaining) {
            _remaining = remaining;
        }

        @Override
        public void run() {
            doCheckTimeUp(_remaining);
        }
    }

    private class TimeUp implements Runnable {
        @Override
        public void run() {
            InstanceManager.getInstance().destroyInstance(getId());
        }
    }

    public void cancelTimer() {
        if (_checkTimeUpTask != null)
            _checkTimeUpTask.cancel(false);
    }

    protected void scheduleCheckTimeUp(int remaining, int interval) {
        if (remaining >= 10000)
            _checkTimeUpTask = ThreadPoolManager.getInstance().scheduleGeneral(new CheckTimeUp(remaining),
                    interval);
        else
            _checkTimeUpTask = ThreadPoolManager.getInstance().scheduleGeneral(new TimeUp(), interval);
    }
}