· 6 years ago · Mar 07, 2019, 05:02 PM
1Index: /swamp_datapack/data/html/admin/charinfo.htm
2===================================================================
3--- /swamp_datapack/data/html/admin/charinfo.htm (revision 5)
4+++ /swamp_datapack/data/html/admin/charinfo.htm (revision 6)
5@@ -42,4 +42,6 @@
6 <tr>
7 <td>Name:</td><td>%name%</td><td><button value="Refresh" action="bypass -h admin_current_player" width=65 height=19 back="L2UI_ch3.smallbutton2_over" fore="L2UI_ch3.smallbutton2"></td>
8+ </tr><tr>
9+ <td>Instance:</td><td>%inst%</td>
10 </tr><tr>
11 <td>Clan:</td><td>%clan%</td>
12Index: /swamp_datapack/data/html/admin/instance_players.htm
13===================================================================
14--- /swamp_datapack/data/html/admin/instance_players.htm (revision 6)
15+++ /swamp_datapack/data/html/admin/instance_players.htm (revision 6)
16@@ -0,0 +1,26 @@
17+<html>
18+<body>
19+ <center>
20+ <table width="270">
21+ <tr>
22+ <td><button value="Main" action="bypass -h admin_admin" width=50 height=15 back="sek.cbui94" fore="sek.cbui92"></td>
23+ <td width="180" align="center">Instance Details</td>
24+ <td><button value="Instance" action="bypass -h admin_instance" width=50 height=15 back="sek.cbui94" fore="sek.cbui92"></td>
25+ </tr>
26+ </table>
27+ <br> <img src="L2UI.SquareGray" width="180" height="1">
28+ <table width="180" bgcolor="000000">
29+ <tr>
30+ <td align="center" width="90">Instance: %instanceId%</td>
31+ <td align="center" width="90">Players: %playersCount%</td>
32+ </tr>
33+ </table>
34+ <img src="L2UI.SquareGray" width="180" height="1"> <br>
35+ <br> <img src="L2UI.SquareWhite" width="270" height="1">
36+ <table width="250">
37+ %players%
38+ </table>
39+ <img src="L2UI.SquareBlank" width="1" height="3"> <img src="L2UI.SquareWhite" width="270" height="1">
40+ </center>
41+</body>
42+</html>
43Index: /swamp_datapack/data/html/admin/instances.htm
44===================================================================
45--- /swamp_datapack/data/html/admin/instances.htm (revision 6)
46+++ /swamp_datapack/data/html/admin/instances.htm (revision 6)
47@@ -0,0 +1,41 @@
48+<html>
49+<body>
50+ <center>
51+ <table width="270">
52+ <tr>
53+ <td><button value="Main" action="bypass -h admin_admin" width=50 height=15 back="sek.cbui94" fore="sek.cbui92"></td>
54+ <td width="180" align="center">Instance Menu</td>
55+ <td><button value="Instances" action="bypass -h admin_instancezone" width=52 height=15 back="sek.cbui94" fore="sek.cbui92"></td>
56+ </tr>
57+ </table>
58+ <br> <img src="L2UI.SquareGray" width="220" height="1"> <br>
59+ <table width="200" border="1" bgcolor="000000">
60+ <tr>
61+ <td align="center" fixwidth="150">Actual instance:</td>
62+ <td align="center" fixwidth="50"><font color="LEVEL">%instanceId%</font></td>
63+ </tr>
64+ <tr>
65+ <td align="center" fixwidth="150">Active instances:</td>
66+ <td align="center" fixwidth="50"><font color="LEVEL">%instCount%</font></td>
67+ </tr>
68+ </table>
69+ <br> <img src="L2UI.SquareGray" width="220" height="1">
70+ <br> <font color="LEVEL">Instances</font>
71+ <table>
72+ <tr>
73+ <td width="7"></td>
74+ <td><button value="Create new" action="bypass -h admin_instancecreate" width="135" height="21" back="L2UI_CH3.bigbutton3_down" fore="L2UI_CH3.bigbutton3" /></td>
75+ <td><button value="Reset" action="bypass -h admin_instancereset" width="135" height="21" back="L2UI_CH3.bigbutton3_down" fore="L2UI_CH3.bigbutton3" /></td>
76+ </tr>
77+ </table>
78+ <br> <img src="L2UI.SquareGray" width="255" height="1">
79+ %details%
80+ <br>
81+ <table width="300" bgcolor="444444">
82+ <tr>
83+ <td width="270">Page: %pages%</td>
84+ </tr>
85+ </table>
86+ </center>
87+</body>
88+</html>
89Index: /swamp_datapack/data/html/admin/main_menu.htm
90===================================================================
91--- /swamp_datapack/data/html/admin/main_menu.htm (revision 5)
92+++ /swamp_datapack/data/html/admin/main_menu.htm (revision 6)
93@@ -44,4 +44,5 @@
94 <td><button value="Set Title" action="bypass -h admin_settitle $menu_command" width=55 height=15 back="sek.cbui94" fore="sek.cbui92"></td>
95 <td><button value="Get Buffs" action="bypass -h admin_getbuffs $menu_command" width=55 height=15 back="sek.cbui94" fore="sek.cbui92"></td>
96+ <td><button value="Instance" action="bypass -h admin_instance" width=55 height=15 back="sek.cbui94" fore="sek.cbui92"></td>
97 <td></td>
98 </tr>
99Index: /swamp_datapack/data/xml/adminCommands.xml
100===================================================================
101--- /swamp_datapack/data/xml/adminCommands.xml (revision 5)
102+++ /swamp_datapack/data/xml/adminCommands.xml (revision 6)
103@@ -17,4 +17,15 @@
104 <aCar name="admin_ann" accessLevel="7"/>
105 <aCar name="admin_say" accessLevel="7"/>
106+
107+ <!-- INSTANCE -->
108+ <aCar name="admin_instance" accessLevel="7"/>
109+ <aCar name="admin_instancezone" accessLevel="7"/>
110+ <aCar name="admin_instancepage" accessLevel="7"/>
111+ <aCar name="admin_instancereset" accessLevel="7"/>
112+ <aCar name="admin_instancecreate" accessLevel="7"/>
113+ <aCar name="admin_instanceplayers" accessLevel="7"/>
114+ <aCar name="admin_instanceteleport" accessLevel="7"/>
115+ <aCar name="admin_instancedestroy" accessLevel="7"/>
116+ <aCar name="admin_instancezone_clear" accessLevel="7"/>
117
118 <!-- BAN -->
119Index: /swamp_datapack/data/xml/instanceNames.xml
120===================================================================
121--- /swamp_datapack/data/xml/instanceNames.xml (revision 6)
122+++ /swamp_datapack/data/xml/instanceNames.xml (revision 6)
123@@ -0,0 +1,5 @@
124+<?xml version='1.0' encoding='utf-8'?>
125+<list>
126+ <instance id="1" name="Kamaloka (Labyrinth of the Abyss)" />
127+ <instance id="2" name="Kamaloka (Hall of the Abyss)" />
128+</list>
129Index: /swamp_datapack/sql/character_instance_time.sql
130===================================================================
131--- /swamp_datapack/sql/character_instance_time.sql (revision 6)
132+++ /swamp_datapack/sql/character_instance_time.sql (revision 6)
133@@ -0,0 +1,6 @@
134+CREATE TABLE IF NOT EXISTS `character_instance_time` (
135+ `charId` INT UNSIGNED NOT NULL DEFAULT '0',
136+ `instanceId` int(3) NOT NULL DEFAULT '0',
137+ `time` bigint(13) unsigned NOT NULL DEFAULT '0',
138+ PRIMARY KEY (`charId`,`instanceId`)
139+);
140Index: /swamp_datapack/tools/database_installer.bat
141===================================================================
142--- /swamp_datapack/tools/database_installer.bat (revision 5)
143+++ /swamp_datapack/tools/database_installer.bat (revision 6)
144@@ -69,4 +69,5 @@
145 %mysqlPath% -h %gshost% -u %gsuser% --password=%gspass% -D %gsdb% < ../sql/character_friends.sql
146 %mysqlPath% -h %gshost% -u %gsuser% --password=%gspass% -D %gsdb% < ../sql/character_hennas.sql
147+%mysqlPath% -h %gshost% -u %gsuser% --password=%gspass% -D %gsdb% < ../sql/character_instance_time.sql
148 %mysqlPath% -h %gshost% -u %gsuser% --password=%gspass% -D %gsdb% < ../sql/character_macroses.sql
149 %mysqlPath% -h %gshost% -u %gsuser% --password=%gspass% -D %gsdb% < ../sql/character_mail.sql
150Index: /swamp_datapack/tools/database_installer.sh
151===================================================================
152--- /swamp_datapack/tools/database_installer.sh (revision 5)
153+++ /swamp_datapack/tools/database_installer.sh (revision 6)
154@@ -89,4 +89,5 @@
155 $MYG < ../sql/character_friends.sql &> /dev/null
156 $MYG < ../sql/character_hennas.sql &> /dev/null
157+$MYG < ../sql/character_instance_time.sql &> /dev/null
158 $MYG < ../sql/character_macroses.sql &> /dev/null
159 $MYG < ../sql/character_mail.sql &> /dev/null
160Index: /swamp_datapack/tools/full_install.sql
161===================================================================
162--- /swamp_datapack/tools/full_install.sql (revision 5)
163+++ /swamp_datapack/tools/full_install.sql (revision 6)
164@@ -13,4 +13,5 @@
165 DROP TABLE IF EXISTS character_friends;
166 DROP TABLE IF EXISTS character_hennas;
167+DROP TABLE IF EXISTS character_instance_time;
168 DROP TABLE IF EXISTS character_macroses;
169 DROP TABLE IF EXISTS character_mail;
170Index: amp_gameserver/config/custom.properties
171===================================================================
172--- /swamp_gameserver/config/custom.properties (revision 5)
173+++ (revision )
174@@ -1,39 +1,0 @@
175-# =================================================================
176-# Offline trade/craft
177-# =================================================================
178-
179-# Option to enable or disable offline trade feature.
180-# Default: False
181-OfflineTradeEnable = False
182-
183-# Option to enable or disable offline craft feature.
184-# Default: False
185-OfflineCraftEnable = False
186-
187-# If set to True, offline shops will be possible only in peace zones.
188-# Default: False
189-OfflineModeInPeaceZone = True
190-
191-# If set to True, players in offline shop mode will not take any damage, thus they cannot be killed.
192-# Default: False
193-OfflineModeNoDamage = False
194-
195-# If set to True, name color will be changed when entering offline mode.
196-OfflineSetNameColor = True
197-
198-# Color of the name in offline mode.
199-OfflineNameColor = 808080
200-
201-# Restore offline traders/crafters after restart/shutdown.
202-# Default: False
203-RestoreOffliners = True
204-
205-# Do not restore offline characters, after OfflineMaxDays days spent from first restore.
206-# Require server restart to disconnect expired shops.
207-# 0 = disabled (always restore).
208-# Default: 7
209-OfflineMaxDays = 7
210-
211-# Disconnect shop after finished selling, buying.
212-# Default: True
213-OfflineDisconnectFinished = True
214Index: /swamp_gameserver/config/customs.properties
215===================================================================
216--- /swamp_gameserver/config/customs.properties (revision 6)
217+++ /swamp_gameserver/config/customs.properties (revision 6)
218@@ -0,0 +1,51 @@
219+#=============================================================
220+# Offline trade/craft
221+#=============================================================
222+
223+# Option to enable or disable offline trade feature.
224+# Default: False
225+OfflineTradeEnable = False
226+
227+# Option to enable or disable offline craft feature.
228+# Default: False
229+OfflineCraftEnable = False
230+
231+# If set to True, offline shops will be possible only in peace zones.
232+# Default: False
233+OfflineModeInPeaceZone = True
234+
235+# If set to True, players in offline shop mode will not take any damage, thus they cannot be killed.
236+# Default: False
237+OfflineModeNoDamage = False
238+
239+# If set to True, name color will be changed when entering offline mode.
240+OfflineSetNameColor = True
241+
242+# Color of the name in offline mode.
243+OfflineNameColor = 808080
244+
245+# Restore offline traders/crafters after restart/shutdown.
246+# Default: False
247+RestoreOffliners = True
248+
249+# Do not restore offline characters, after OfflineMaxDays days spent from first restore.
250+# Require server restart to disconnect expired shops.
251+# 0 = disabled (always restore).
252+# Default: 7
253+OfflineMaxDays = 7
254+
255+# Disconnect shop after finished selling, buying.
256+# Default: True
257+OfflineDisconnectFinished = True
258+
259+#=============================================================
260+# Instances
261+#=============================================================
262+# Restores the player to their previous instance on EnterWorld.
263+# Default: False
264+RestorePlayerInstance = False
265+
266+# When a player dies, is removed from instance after a fixed period of time.
267+# Time in minutes.
268+# Default: 1
269+EjectDeadPlayerTime = 1
270Index: /swamp_gameserver/java/net/sf/l2j/Config.java
271===================================================================
272--- /swamp_gameserver/java/net/sf/l2j/Config.java (revision 5)
273+++ /swamp_gameserver/java/net/sf/l2j/Config.java (revision 6)
274@@ -38,5 +38,5 @@
275 public static final String SERVER_FILE = "./config/server.properties";
276 public static final String SIEGE_FILE = "./config/siege.properties";
277- public static final String CUSTOM_FILE = "./config/custom.properties";
278+ public static final String CUSTOMS_FILE = "./config/customs.properties";
279
280 // --------------------------------------------------
281@@ -54,4 +54,8 @@
282 public static boolean OFFLINE_SET_NAME_COLOR;
283 public static int OFFLINE_NAME_COLOR;
284+
285+ /** Instance settings */
286+ public static boolean RESTORE_PLAYER_INSTANCE;
287+ public static int EJECT_DEAD_PLAYER_TIME;
288
289 // --------------------------------------------------
290@@ -923,14 +927,17 @@
291 private static final void loadCustoms()
292 {
293- final ExProperties custom = initProperties(CUSTOM_FILE);
294- OFFLINE_TRADE_ENABLE = custom.getProperty("OfflineTradeEnable", false);
295- OFFLINE_CRAFT_ENABLE = custom.getProperty("OfflineCraftEnable", false);
296- OFFLINE_MODE_IN_PEACE_ZONE = custom.getProperty("OfflineModeInPeaceZone", false);
297- OFFLINE_MODE_NO_DAMAGE = custom.getProperty("OfflineModeNoDamage", false);
298- OFFLINE_SET_NAME_COLOR = custom.getProperty("OfflineSetNameColor", false);
299- OFFLINE_NAME_COLOR = Integer.decode("0x" + custom.getProperty("OfflineNameColor", "808080"));
300- RESTORE_OFFLINERS = custom.getProperty("RestoreOffliners", false);
301- OFFLINE_MAX_DAYS = custom.getProperty("OfflineMaxDays", 10);
302- OFFLINE_DISCONNECT_FINISHED = custom.getProperty("OfflineDisconnectFinished", true);
303+ final ExProperties customs = initProperties(CUSTOMS_FILE);
304+ OFFLINE_TRADE_ENABLE = customs.getProperty("OfflineTradeEnable", false);
305+ OFFLINE_CRAFT_ENABLE = customs.getProperty("OfflineCraftEnable", false);
306+ OFFLINE_MODE_IN_PEACE_ZONE = customs.getProperty("OfflineModeInPeaceZone", false);
307+ OFFLINE_MODE_NO_DAMAGE = customs.getProperty("OfflineModeNoDamage", false);
308+ OFFLINE_SET_NAME_COLOR = customs.getProperty("OfflineSetNameColor", false);
309+ OFFLINE_NAME_COLOR = Integer.decode("0x" + customs.getProperty("OfflineNameColor", "808080"));
310+ RESTORE_OFFLINERS = customs.getProperty("RestoreOffliners", false);
311+ OFFLINE_MAX_DAYS = customs.getProperty("OfflineMaxDays", 10);
312+ OFFLINE_DISCONNECT_FINISHED = customs.getProperty("OfflineDisconnectFinished", true);
313+
314+ RESTORE_PLAYER_INSTANCE = customs.getProperty("RestorePlayerInstance", false);
315+ EJECT_DEAD_PLAYER_TIME = customs.getProperty("EjectDeadPlayerTime", 1);
316 }
317
318Index: /swamp_gameserver/java/net/sf/l2j/commons/math/MathUtil.java
319===================================================================
320--- /swamp_gameserver/java/net/sf/l2j/commons/math/MathUtil.java (revision 5)
321+++ /swamp_gameserver/java/net/sf/l2j/commons/math/MathUtil.java (revision 6)
322@@ -114,5 +114,5 @@
323 public static boolean checkIfInShortRadius(int radius, WorldObject obj1, WorldObject obj2, boolean includeZAxis)
324 {
325- if (obj1 == null || obj2 == null)
326+ if (obj1 == null || obj2 == null || obj1.getInstanceWorld() != obj2.getInstanceWorld())
327 return false;
328
329@@ -143,5 +143,5 @@
330 public static boolean checkIfInRange(int range, WorldObject obj1, WorldObject obj2, boolean includeZAxis)
331 {
332- if (obj1 == null || obj2 == null)
333+ if (obj1 == null || obj2 == null || obj1.getInstanceWorld() != obj2.getInstanceWorld())
334 return false;
335
336Index: /swamp_gameserver/java/net/sf/l2j/custom/instancezone/Instance.java
337===================================================================
338--- /swamp_gameserver/java/net/sf/l2j/custom/instancezone/Instance.java (revision 6)
339+++ /swamp_gameserver/java/net/sf/l2j/custom/instancezone/Instance.java (revision 6)
340@@ -0,0 +1,434 @@
341+package net.sf.l2j.custom.instancezone;
342+
343+import java.sql.Connection;
344+import java.sql.PreparedStatement;
345+import java.util.Map;
346+import java.util.Set;
347+import java.util.concurrent.ConcurrentHashMap;
348+import java.util.concurrent.ScheduledFuture;
349+import java.util.concurrent.TimeUnit;
350+import java.util.logging.Logger;
351+
352+import net.sf.l2j.commons.concurrent.ThreadPool;
353+
354+import net.sf.l2j.Config;
355+import net.sf.l2j.L2DatabaseFactory;
356+import net.sf.l2j.gameserver.model.World;
357+import net.sf.l2j.gameserver.model.WorldObject;
358+import net.sf.l2j.gameserver.model.actor.Npc;
359+import net.sf.l2j.gameserver.model.actor.instance.Player;
360+import net.sf.l2j.gameserver.network.SystemMessageId;
361+import net.sf.l2j.gameserver.network.serverpackets.L2GameServerPacket;
362+import net.sf.l2j.gameserver.network.serverpackets.SystemMessage;
363+
364+public final class Instance
365+{
366+ private static final Logger LOG = Logger.getLogger(Instance.class.getName());
367+
368+ // Basic instance parameters
369+ private final int _id;
370+ private String _name;
371+ private long _endTime;
372+ private boolean _allowSummon;
373+ private boolean _isPvPInstance;
374+ private final int _ejectTime = Config.EJECT_DEAD_PLAYER_TIME;
375+
376+ // Advanced instance parameters
377+ private final Set<Npc> _npcs = ConcurrentHashMap.newKeySet();
378+ private final Set<Player> _players = ConcurrentHashMap.newKeySet();
379+ private final Map<Integer, Map<Integer, Long>> _playerInstanceTimes = new ConcurrentHashMap<>();
380+
381+ // Timers
382+ private final Map<Integer, ScheduledFuture<?>> _ejectDeadTasks = new ConcurrentHashMap<>();
383+ private ScheduledFuture<?> _cleanUpTask;
384+ private ScheduledFuture<?> _emptyDestroyTask;
385+
386+ public Instance(int id)
387+ {
388+ _id = id;
389+ InstanceManager.getInstance().register(this);
390+ }
391+
392+ public Instance(int id, String name)
393+ {
394+ _id = id;
395+ _name = name;
396+ }
397+
398+ /**
399+ * @return the ID of this instance.
400+ */
401+ public int getId()
402+ {
403+ return _id;
404+ }
405+
406+ /**
407+ * @return the name of this instance
408+ */
409+ public String getName()
410+ {
411+ return _name;
412+ }
413+
414+ /**
415+ * Add player to instance
416+ * @param player player instance
417+ */
418+ public void addPlayer(Player player)
419+ {
420+ _players.add(player);
421+ }
422+
423+ /**
424+ * Remove player from instance.
425+ * @param player player instance
426+ */
427+ public void removePlayer(Player player)
428+ {
429+ _players.remove(player);
430+ if (_players.isEmpty() && _endTime <= 0)
431+ destroy();
432+ }
433+
434+ /**
435+ * Get all players inside instance.
436+ * @return players within instance
437+ */
438+ public Set<Player> getPlayers()
439+ {
440+ return _players;
441+ }
442+
443+ /**
444+ * Get count of players inside instance.
445+ * @return players count inside instance
446+ */
447+ public int getPlayersCount()
448+ {
449+ return _players.size();
450+ }
451+
452+ /**
453+ * Get remaining time before instance will be destroyed.
454+ * @return remaining time in milliseconds if duration is not equal to -1, otherwise -1
455+ */
456+ public long getRemainingTime()
457+ {
458+ return (_endTime == -1) ? -1 : (_endTime - System.currentTimeMillis());
459+ }
460+
461+ /**
462+ * Get instance destroy time.
463+ * @return destroy time in milliseconds if duration is not equal to -1, otherwise -1
464+ */
465+ public long getEndTime()
466+ {
467+ return _endTime;
468+ }
469+
470+ /**
471+ * Remove all players from instance world.
472+ */
473+ private void removePlayers()
474+ {
475+ _players.forEach(this::ejectPlayer);
476+ _players.clear();
477+ }
478+
479+ /**
480+ * Check if player is inside instance.
481+ * @param player player to be checked
482+ * @return {@code true} if player is inside, otherwise {@code false}
483+ */
484+ public boolean containsPlayer(Player player)
485+ {
486+ return _players.contains(player);
487+ }
488+
489+ /**
490+ * Teleport player out of instance.
491+ * @param player player that should be moved out
492+ */
493+ public void ejectPlayer(Player player)
494+ {
495+ player.setInstanceId(0);
496+ player.teleToLocation(146319, 25774, -2013, 0);
497+ if (player.isDead())
498+ player.doRevive();
499+ }
500+
501+ public void addNpc(Npc npc)
502+ {
503+ _npcs.add(npc);
504+ }
505+
506+ public void removeNpc(Npc npc)
507+ {
508+ _npcs.remove(npc);
509+ }
510+
511+ /**
512+ * Despawn NPCs inside instance world.
513+ */
514+ public void removeNpcs()
515+ {
516+ _npcs.forEach(this::despawnAll);
517+ _npcs.clear();
518+ }
519+
520+ public void despawnAll(Npc npc)
521+ {
522+ npc.deleteMe();
523+ }
524+
525+ /**
526+ * Destroy current instance world.<br>
527+ * <b><font color=red>Use this method to destroy instance world properly.</font></b>
528+ */
529+ public synchronized void destroy()
530+ {
531+ if (_id <= 0)
532+ return;
533+
534+ if (_cleanUpTask != null)
535+ {
536+ _cleanUpTask.cancel(false);
537+ _cleanUpTask = null;
538+ }
539+
540+ if (_emptyDestroyTask != null)
541+ {
542+ _emptyDestroyTask.cancel(false);
543+ _emptyDestroyTask = null;
544+ }
545+
546+ _ejectDeadTasks.values().forEach(t -> t.cancel(true));
547+ _ejectDeadTasks.clear();
548+
549+ removeNpcs();
550+ removePlayers();
551+
552+ InstanceManager.getInstance().unregister(getId());
553+ }
554+
555+ /**
556+ * Allow summoning players (that are out of instance) to instance world by players inside.
557+ * @param allowSummon {@code true} means summon is allowed, {@code false} means summon is prohibited
558+ */
559+ public void setAllowSummon(boolean allowSummon)
560+ {
561+ _allowSummon = allowSummon;
562+ }
563+
564+ /**
565+ * Check if summoning players to instance world is allowed.
566+ * @return {@code true} when summon is allowed, otherwise {@code false}
567+ */
568+ public boolean isSummonAllowed()
569+ {
570+ return _allowSummon;
571+ }
572+
573+ /**
574+ * Check if instance world is PvP zone.
575+ * @return {@code true} when instance is PvP zone, otherwise {@code false}
576+ */
577+ public boolean isPvP()
578+ {
579+ return _isPvPInstance;
580+ }
581+
582+ /**
583+ * Sets PvP zone status of the instance
584+ * @param isPvP false by default
585+ */
586+ public void setPvPInstance(boolean isPvP)
587+ {
588+ _isPvPInstance = isPvP;
589+ }
590+
591+ /**
592+ * Set reenter penalty for players associated with current instance.<br>
593+ * @param playerObjId the player object id
594+ * @param id instance id
595+ * @param time penalty time in milliseconds
596+ */
597+ public void setReenterTime(int playerObjId, int id, long time)
598+ {
599+ if (!_playerInstanceTimes.containsKey(playerObjId))
600+ _playerInstanceTimes.put(playerObjId, new ConcurrentHashMap<>());
601+
602+ try (Connection con = L2DatabaseFactory.getInstance().getConnection();
603+ PreparedStatement ps = con.prepareStatement("INSERT IGNORE INTO character_instance_time (charId,instanceId,time) VALUES (?,?,?)"))
604+ {
605+ ps.setInt(1, playerObjId);
606+ ps.setInt(2, id);
607+ ps.setLong(3, time);
608+ ps.execute();
609+
610+ _playerInstanceTimes.get(playerObjId).put(id, time);
611+ }
612+ catch (Exception e)
613+ {
614+ LOG.warning("Instance: Could not insert character instance time data! " + e.getMessage());
615+ }
616+
617+ InstanceManager.getInstance().setReenterPenalty(playerObjId, id, time);
618+ final String msg = "Instant zone: " + InstanceManager.getInstance().getInstanceName(id) + " entry has been restricted. You can check the next possible entry time by using the command /instancezone.";
619+ final Player player = World.getInstance().getPlayer(playerObjId);
620+
621+ if (player != null && player.isOnline())
622+ player.sendMessage(msg);
623+ }
624+
625+ /**
626+ * Change instance duration.
627+ * @param minutes remaining time to destroy instance
628+ */
629+ public void setDuration(int minutes)
630+ {
631+ // Stop running tasks
632+ final long millis = TimeUnit.MINUTES.toMillis(minutes) + 1000;
633+ if (_cleanUpTask != null)
634+ {
635+ _cleanUpTask.cancel(true);
636+ _cleanUpTask = null;
637+ }
638+
639+ if (_emptyDestroyTask != null && millis < _emptyDestroyTask.getDelay(TimeUnit.MILLISECONDS))
640+ {
641+ _emptyDestroyTask.cancel(true);
642+ _emptyDestroyTask = null;
643+ }
644+
645+ // Set new cleanup task
646+ _endTime = System.currentTimeMillis() + millis;
647+ if (minutes < 1) // Destroy instance
648+ destroy();
649+ else
650+ {
651+ sendWorldDestroyMessage(minutes);
652+ if (minutes <= 5) // Message 1 minute before destroy
653+ _cleanUpTask = ThreadPool.schedule(this::cleanUp, millis - 60000);
654+ else // Message 5 minutes before destroy
655+ _cleanUpTask = ThreadPool.schedule(this::cleanUp, millis - (5 * 60000));
656+ }
657+ }
658+
659+ /**
660+ * Clean up instance.
661+ */
662+ private void cleanUp()
663+ {
664+ if (getRemainingTime() <= TimeUnit.MINUTES.toMillis(1))
665+ {
666+ sendWorldDestroyMessage(1);
667+ _cleanUpTask = ThreadPool.schedule(this::destroy, TimeUnit.MINUTES.toMillis(1));
668+ }
669+ else
670+ {
671+ sendWorldDestroyMessage(5);
672+ _cleanUpTask = ThreadPool.schedule(this::cleanUp, TimeUnit.MINUTES.toMillis(5));
673+ }
674+ }
675+
676+ /**
677+ * Show instance destroy messages to players inside instance world.
678+ * @param delay time in minutes
679+ */
680+ private void sendWorldDestroyMessage(int delay)
681+ {
682+ final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.THIS_INSTANT_ZONE_WILL_BE_TERMINATED_IN_S1_MINUTE_S_YOU_WILL_BE_FORCED_OUT_OF_THE_DUNGEON_WHEN_THE_TIME_EXPIRES);
683+ sm.addNumber(delay);
684+ broadcastPacket(sm);
685+ }
686+
687+ /**
688+ * Send packet to each player from instance world.
689+ * @param packets packets to be send
690+ */
691+ public void broadcastPacket(L2GameServerPacket... packets)
692+ {
693+ for (Player player : _players)
694+ {
695+ for (L2GameServerPacket packet : packets)
696+ player.sendPacket(packet);
697+ }
698+ }
699+
700+ // ---------------------------------------------
701+ // Listeners
702+ // ---------------------------------------------
703+
704+ /**
705+ * This method is called when player dead inside instance.
706+ * @param player
707+ */
708+ public void onDeath(Player player)
709+ {
710+ player.sendMessage("If you are not resurrected within " + _ejectTime + " minute(s), you will be expelled from the instant zone.");
711+
712+ // Start eject task
713+ _ejectDeadTasks.put(player.getObjectId(), ThreadPool.schedule(() ->
714+ {
715+ if (player.isDead())
716+ ejectPlayer(player);
717+
718+ }, TimeUnit.MINUTES.toMillis(_ejectTime)));
719+ }
720+
721+ /**
722+ * This method is called when player was resurrected inside instance.
723+ * @param player resurrected player
724+ */
725+ public void doRevive(Player player)
726+ {
727+ final ScheduledFuture<?> task = _ejectDeadTasks.remove(player.getObjectId());
728+ if (task != null)
729+ task.cancel(true);
730+ }
731+
732+ /**
733+ * This method is called when object enter or leave this instance.
734+ * @param object instance of object which enters/leaves instance
735+ * @param enter {@code true} when object enter, {@code false} when object leave
736+ */
737+ public void onInstanceChange(WorldObject object, boolean enter)
738+ {
739+ if (object instanceof Player)
740+ {
741+ final Player player = object.getActingPlayer();
742+ if (enter)
743+ addPlayer(player);
744+ else
745+ removePlayer(player);
746+ }
747+ else if (object instanceof Npc)
748+ {
749+ final Npc npc = (Npc) object;
750+ if (enter)
751+ addNpc(npc);
752+ else
753+ {
754+ if (npc.getSpawn() != null)
755+ npc.getSpawn().setRespawnState(false);
756+
757+ removeNpc(npc);
758+ }
759+ }
760+ }
761+
762+ /**
763+ * This method is called when player logout inside instance world.
764+ * @param player player who logout
765+ */
766+ public void onPlayerLogout(Player player)
767+ {
768+ removePlayer(player);
769+ if (Config.RESTORE_PLAYER_INSTANCE)
770+ player.getMemos().set("INSTANCE_RESTORE", getId());
771+ else
772+ ejectPlayer(player);
773+ }
774+}
775Index: /swamp_gameserver/java/net/sf/l2j/custom/instancezone/InstanceManager.java
776===================================================================
777--- /swamp_gameserver/java/net/sf/l2j/custom/instancezone/InstanceManager.java (revision 6)
778+++ /swamp_gameserver/java/net/sf/l2j/custom/instancezone/InstanceManager.java (revision 6)
779@@ -0,0 +1,301 @@
780+package net.sf.l2j.custom.instancezone;
781+
782+import java.io.File;
783+import java.sql.Connection;
784+import java.sql.PreparedStatement;
785+import java.sql.ResultSet;
786+import java.sql.Statement;
787+import java.util.ArrayList;
788+import java.util.Collection;
789+import java.util.Collections;
790+import java.util.HashMap;
791+import java.util.List;
792+import java.util.Map;
793+import java.util.Map.Entry;
794+import java.util.concurrent.ConcurrentHashMap;
795+import java.util.logging.Logger;
796+
797+import net.sf.l2j.commons.data.xml.XMLDocument;
798+
799+import net.sf.l2j.L2DatabaseFactory;
800+import net.sf.l2j.gameserver.model.actor.instance.Player;
801+import net.sf.l2j.gameserver.templates.StatsSet;
802+
803+import org.w3c.dom.Document;
804+import org.w3c.dom.Node;
805+
806+public final class InstanceManager extends XMLDocument
807+{
808+ private static final Logger LOG = Logger.getLogger(InstanceManager.class.getName());
809+
810+ private static final String DELETE_INSTANCE_TIME = "DELETE FROM character_instance_time WHERE charId=? AND instanceId=?";
811+
812+ private int _currentInstanceId;
813+
814+ private final Map<Integer, String> _instanceNames = new HashMap<>();
815+ private final Map<Integer, Instance> _instanceWorlds = new ConcurrentHashMap<>();
816+ private final Map<Integer, Map<Integer, Long>> _playerInstanceTimes = new ConcurrentHashMap<>();
817+
818+ protected InstanceManager()
819+ {
820+ load();
821+ }
822+
823+ @Override
824+ protected void load()
825+ {
826+ _instanceWorlds.put(0, new Instance(0, "Universe"));
827+ LOG.info("InstanceManager: Universe Instance created.");
828+
829+ loadDocument("./data/xml/instanceNames.xml");
830+ LOG.info("InstanceManager: Loaded " + _instanceNames.size() + " instance names.");
831+
832+ _playerInstanceTimes.clear();
833+ restoreInstanceTimes();
834+ }
835+
836+ @Override
837+ protected void parseDocument(Document doc, File file)
838+ {
839+ // StatsSet used to feed informations. Cleaned on every entry.
840+ final StatsSet set = new StatsSet();
841+
842+ // First element is never read.
843+ final Node n = doc.getFirstChild();
844+
845+ for (Node o = n.getFirstChild(); o != null; o = o.getNextSibling())
846+ {
847+ // Parse and feed instance names.
848+ if ("instance".equalsIgnoreCase(o.getNodeName()))
849+ {
850+ // Parse and feed content.
851+ parseAndFeed(o.getAttributes(), set);
852+
853+ // Feed the map with new data.
854+ _instanceNames.put(set.getInteger("id"), set.getString("name"));
855+
856+ // Clear the StatsSet.
857+ set.clear();
858+ }
859+ }
860+ }
861+
862+ /**
863+ * Get instance name from file "instancenames.xml"
864+ * @param templateId template ID of instance
865+ * @return name of instance if found, otherwise {@code null}
866+ */
867+ public String getInstanceName(int templateId)
868+ {
869+ return _instanceNames.get(templateId);
870+ }
871+
872+ /**
873+ * Create new instance.
874+ * @return newly created instance.
875+ */
876+ public Instance createInstance()
877+ {
878+ return new Instance(getNewInstanceId());
879+ }
880+
881+ /**
882+ * Get instance world with given ID.
883+ * @param instanceId ID of instance
884+ * @return instance itself if found, otherwise {@code null}
885+ */
886+ public Instance getInstance(int instanceId)
887+ {
888+ return _instanceWorlds.get(instanceId);
889+ }
890+
891+ /**
892+ * Get all active instances.
893+ * @return Collection of all instances
894+ */
895+ public Collection<Instance> getInstances()
896+ {
897+ return _instanceWorlds.values();
898+ }
899+
900+ /**
901+ * Get ID for newly created instance.
902+ * @return instance id
903+ */
904+ private synchronized int getNewInstanceId()
905+ {
906+ do
907+ {
908+ if (_currentInstanceId == 99)
909+ _currentInstanceId = 0;
910+
911+ _currentInstanceId++;
912+ }
913+ while (_instanceWorlds.containsKey(_currentInstanceId));
914+ return _currentInstanceId;
915+ }
916+
917+ /**
918+ * Register instance world.<br>
919+ * @param instance instance which should be registered
920+ */
921+ public void register(Instance instance)
922+ {
923+ _instanceWorlds.put(instance.getId(), instance);
924+ }
925+
926+ /**
927+ * Unregister instance world.<br>
928+ * <b><font color=red>To remove instance world properly use {@link Instance#destroy()}.</font></b>
929+ * @param instanceId ID of instance to unregister
930+ */
931+ public void unregister(int instanceId)
932+ {
933+ _instanceWorlds.remove(instanceId);
934+ }
935+
936+ /**
937+ * Set re-enter penalty for specified player.<br>
938+ * <font color=red><b>This method store penalty into memory only. Use {@link Instance#setReenterTime} to set instance penalty properly.</b></font>
939+ * @param objectId object ID of player
940+ * @param id instance template id
941+ * @param time penalty time
942+ */
943+ public void setReenterPenalty(int objectId, int id, long time)
944+ {
945+ _playerInstanceTimes.computeIfAbsent(objectId, k -> new ConcurrentHashMap<>()).put(id, time);
946+ }
947+
948+ /**
949+ * Restore instance reenter data for all players.
950+ */
951+ private void restoreInstanceTimes()
952+ {
953+ try (Connection con = L2DatabaseFactory.getInstance().getConnection();
954+ Statement ps = con.createStatement();
955+ ResultSet rs = ps.executeQuery("SELECT * FROM character_instance_time ORDER BY charId"))
956+ {
957+ while (rs.next())
958+ {
959+ // Check if instance penalty passed
960+ final long time = rs.getLong("time");
961+ if (time > System.currentTimeMillis())
962+ {
963+ // Load params
964+ final int charId = rs.getInt("charId");
965+ final int instanceId = rs.getInt("instanceId");
966+ // Set penalty
967+ setReenterPenalty(charId, instanceId, time);
968+ }
969+ }
970+ }
971+ catch (Exception e)
972+ {
973+ LOG.warning("Cannot restore players instance reenter data: " + e.getMessage());
974+ }
975+ }
976+
977+ /**
978+ * Get all instance re-enter times for specified player.<br>
979+ * This method also removes the penalties that have already expired.
980+ * @param player instance of player who wants to get re-enter data
981+ * @return map in form templateId, penaltyEndTime
982+ */
983+ public Map<Integer, Long> getAllInstanceTimes(Player player)
984+ {
985+ // When player don't have any instance penalty
986+ final Map<Integer, Long> instanceTimes = _playerInstanceTimes.get(player.getObjectId());
987+ if (instanceTimes == null || instanceTimes.isEmpty())
988+ return Collections.emptyMap();
989+
990+ // Find passed penalty
991+ final List<Integer> invalidPenalty = new ArrayList<>(instanceTimes.size());
992+ for (Entry<Integer, Long> entry : instanceTimes.entrySet())
993+ {
994+ if (entry.getValue() <= System.currentTimeMillis())
995+ invalidPenalty.add(entry.getKey());
996+ }
997+
998+ // Remove them
999+ if (!invalidPenalty.isEmpty())
1000+ {
1001+ try (Connection con = L2DatabaseFactory.getInstance().getConnection();
1002+ PreparedStatement ps = con.prepareStatement(DELETE_INSTANCE_TIME))
1003+ {
1004+ for (Integer id : invalidPenalty)
1005+ {
1006+ ps.setInt(1, player.getObjectId());
1007+ ps.setInt(2, id);
1008+ ps.addBatch();
1009+ }
1010+ ps.executeBatch();
1011+ invalidPenalty.forEach(instanceTimes::remove);
1012+ }
1013+ catch (Exception e)
1014+ {
1015+ LOG.warning("Cannot delete instance character reenter data: " + e.getMessage());
1016+ }
1017+ }
1018+ return instanceTimes;
1019+ }
1020+
1021+ public boolean canReenter(Player player, int templateId)
1022+ {
1023+ return System.currentTimeMillis() > getInstanceTime(player, templateId);
1024+ }
1025+
1026+ /**
1027+ * Get re-enter time to instance for player.<br>
1028+ * This method also removes penalty if expired.
1029+ * @param player player who wants to get re-enter time
1030+ * @param id template ID of instance
1031+ * @return penalty end time if penalty is found, otherwise -1
1032+ */
1033+ public long getInstanceTime(Player player, int id)
1034+ {
1035+ // Check if exists reenter data for player
1036+ final Map<Integer, Long> playerData = _playerInstanceTimes.get(player.getObjectId());
1037+ if (playerData == null || !playerData.containsKey(id))
1038+ return -1;
1039+
1040+ // If reenter time is higher then current, delete it
1041+ final long time = playerData.get(id);
1042+ if (time <= System.currentTimeMillis())
1043+ {
1044+ deleteInstanceTime(player, id);
1045+ return -1;
1046+ }
1047+ return time;
1048+ }
1049+
1050+ /**
1051+ * Remove re-enter penalty for specified instance from player.
1052+ * @param player player who wants to delete penalty
1053+ * @param id template id of instance world
1054+ */
1055+ public void deleteInstanceTime(Player player, int id)
1056+ {
1057+ try (Connection con = L2DatabaseFactory.getInstance().getConnection();
1058+ PreparedStatement ps = con.prepareStatement(DELETE_INSTANCE_TIME))
1059+ {
1060+ ps.setInt(1, player.getObjectId());
1061+ ps.setInt(2, id);
1062+ ps.execute();
1063+ _playerInstanceTimes.get(player.getObjectId()).remove(id);
1064+ }
1065+ catch (Exception e)
1066+ {
1067+ LOG.warning("Could not delete character instance reenter data: " + e.getMessage());
1068+ }
1069+ }
1070+
1071+ public static InstanceManager getInstance()
1072+ {
1073+ return SingletonHolder.instance;
1074+ }
1075+
1076+ private static class SingletonHolder
1077+ {
1078+ protected static final InstanceManager instance = new InstanceManager();
1079+ }
1080+}
1081Index: /swamp_gameserver/java/net/sf/l2j/gameserver/GameServer.java
1082===================================================================
1083--- /swamp_gameserver/java/net/sf/l2j/gameserver/GameServer.java (revision 5)
1084+++ /swamp_gameserver/java/net/sf/l2j/gameserver/GameServer.java (revision 6)
1085@@ -19,4 +19,5 @@
1086 import net.sf.l2j.Config;
1087 import net.sf.l2j.L2DatabaseFactory;
1088+import net.sf.l2j.custom.instancezone.InstanceManager;
1089 import net.sf.l2j.custom.offlineshop.OfflineTradersTable;
1090 import net.sf.l2j.gameserver.cache.CrestCache;
1091@@ -155,4 +156,5 @@
1092 StringUtil.printSection("World");
1093 World.getInstance();
1094+ InstanceManager.getInstance();
1095 MapRegionData.getInstance();
1096 AnnouncementData.getInstance();
1097@@ -284,4 +286,5 @@
1098
1099 StringUtil.printSection("Customs");
1100+
1101 if ((Config.OFFLINE_TRADE_ENABLE || Config.OFFLINE_CRAFT_ENABLE) && Config.RESTORE_OFFLINERS)
1102 OfflineTradersTable.restoreOfflineTraders();
1103Index: /swamp_gameserver/java/net/sf/l2j/gameserver/data/xml/MapRegionData.java
1104===================================================================
1105--- /swamp_gameserver/java/net/sf/l2j/gameserver/data/xml/MapRegionData.java (revision 5)
1106+++ /swamp_gameserver/java/net/sf/l2j/gameserver/data/xml/MapRegionData.java (revision 6)
1107@@ -5,4 +5,5 @@
1108 import net.sf.l2j.commons.data.xml.XMLDocument;
1109
1110+import net.sf.l2j.custom.instancezone.Instance;
1111 import net.sf.l2j.gameserver.instancemanager.CastleManager;
1112 import net.sf.l2j.gameserver.instancemanager.ClanHallManager;
1113@@ -285,4 +286,12 @@
1114 return arena.getSpawnLoc();
1115
1116+ // Checking if in an instance
1117+ final Instance inst = player.getInstanceWorld();
1118+ if (inst != null)
1119+ {
1120+ player.setInstanceId(0);
1121+ return new Location(146319, 25774, -2013);
1122+ }
1123+
1124 // Retrieve a random spawn location of the nearest town.
1125 return getClosestTown(player).getSpawnLoc();
1126Index: /swamp_gameserver/java/net/sf/l2j/gameserver/handler/AdminCommandHandler.java
1127===================================================================
1128--- /swamp_gameserver/java/net/sf/l2j/gameserver/handler/AdminCommandHandler.java (revision 5)
1129+++ /swamp_gameserver/java/net/sf/l2j/gameserver/handler/AdminCommandHandler.java (revision 6)
1130@@ -25,4 +25,5 @@
1131 import net.sf.l2j.gameserver.handler.admincommandhandlers.AdminHeal;
1132 import net.sf.l2j.gameserver.handler.admincommandhandlers.AdminHelpPage;
1133+import net.sf.l2j.gameserver.handler.admincommandhandlers.AdminInstance;
1134 import net.sf.l2j.gameserver.handler.admincommandhandlers.AdminInvul;
1135 import net.sf.l2j.gameserver.handler.admincommandhandlers.AdminKick;
1136@@ -81,4 +82,5 @@
1137 registerAdminCommandHandler(new AdminHeal());
1138 registerAdminCommandHandler(new AdminHelpPage());
1139+ registerAdminCommandHandler(new AdminInstance());
1140 registerAdminCommandHandler(new AdminInvul());
1141 registerAdminCommandHandler(new AdminKick());
1142Index: /swamp_gameserver/java/net/sf/l2j/gameserver/handler/UserCommandHandler.java
1143===================================================================
1144--- /swamp_gameserver/java/net/sf/l2j/gameserver/handler/UserCommandHandler.java (revision 5)
1145+++ /swamp_gameserver/java/net/sf/l2j/gameserver/handler/UserCommandHandler.java (revision 6)
1146@@ -11,4 +11,5 @@
1147 import net.sf.l2j.gameserver.handler.usercommandhandlers.DisMount;
1148 import net.sf.l2j.gameserver.handler.usercommandhandlers.Escape;
1149+import net.sf.l2j.gameserver.handler.usercommandhandlers.InstanceZone;
1150 import net.sf.l2j.gameserver.handler.usercommandhandlers.Loc;
1151 import net.sf.l2j.gameserver.handler.usercommandhandlers.Mount;
1152@@ -36,4 +37,5 @@
1153 registerUserCommandHandler(new DisMount());
1154 registerUserCommandHandler(new Escape());
1155+ registerUserCommandHandler(new InstanceZone());
1156 registerUserCommandHandler(new Loc());
1157 registerUserCommandHandler(new Mount());
1158Index: /swamp_gameserver/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminBan.java
1159===================================================================
1160--- /swamp_gameserver/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminBan.java (revision 5)
1161+++ /swamp_gameserver/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminBan.java (revision 6)
1162@@ -218,4 +218,5 @@
1163 if (targetPlayer != null)
1164 {
1165+ targetPlayer.setInstanceId(0);
1166 targetPlayer.setPunishLevel(Player.PunishLevel.JAIL, duration);
1167 activeChar.sendMessage(targetPlayer.getName() + " have been jailed for " + (duration > 0 ? duration + " minutes." : "ever !"));
1168Index: /swamp_gameserver/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminEditChar.java
1169===================================================================
1170--- /swamp_gameserver/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminEditChar.java (revision 5)
1171+++ /swamp_gameserver/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminEditChar.java (revision 6)
1172@@ -763,4 +763,5 @@
1173 html.replace("%ip%", ip);
1174 html.replace("%ai%", player.getAI().getIntention().name());
1175+ html.replace("%inst%", player.getInstanceId());
1176 activeChar.sendPacket(html);
1177 }
1178Index: /swamp_gameserver/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminInstance.java
1179===================================================================
1180--- /swamp_gameserver/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminInstance.java (revision 6)
1181+++ /swamp_gameserver/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminInstance.java (revision 6)
1182@@ -0,0 +1,279 @@
1183+package net.sf.l2j.gameserver.handler.admincommandhandlers;
1184+
1185+import java.util.ArrayList;
1186+import java.util.List;
1187+import java.util.Map;
1188+import java.util.StringTokenizer;
1189+
1190+import net.sf.l2j.commons.lang.StringUtil;
1191+import net.sf.l2j.commons.math.MathUtil;
1192+
1193+import net.sf.l2j.custom.instancezone.Instance;
1194+import net.sf.l2j.custom.instancezone.InstanceManager;
1195+import net.sf.l2j.gameserver.handler.IAdminCommandHandler;
1196+import net.sf.l2j.gameserver.model.World;
1197+import net.sf.l2j.gameserver.model.WorldObject;
1198+import net.sf.l2j.gameserver.model.actor.instance.Player;
1199+import net.sf.l2j.gameserver.network.serverpackets.ExShowScreenMessage;
1200+import net.sf.l2j.gameserver.network.serverpackets.ExShowScreenMessage.SMPOS;
1201+import net.sf.l2j.gameserver.network.serverpackets.NpcHtmlMessage;
1202+
1203+public class AdminInstance implements IAdminCommandHandler
1204+{
1205+ private static final int PAGE_LIMIT = 7;
1206+
1207+ private static final String[] ADMIN_COMMANDS =
1208+ {
1209+ "admin_instance",
1210+ "admin_instancezone",
1211+ "admin_instancepage",
1212+ "admin_instancereset",
1213+ "admin_instancecreate",
1214+ "admin_instanceplayers",
1215+ "admin_instancedestroy",
1216+ "admin_instanceteleport",
1217+ "admin_instancezone_clear"
1218+ };
1219+
1220+ @Override
1221+ public boolean useAdminCommand(String command, Player activeChar)
1222+ {
1223+ final StringTokenizer st = new StringTokenizer(command, " ");
1224+ final String actualCommand = st.nextToken();
1225+
1226+ switch (actualCommand.toLowerCase())
1227+ {
1228+ case "admin_instancezone":
1229+ {
1230+ if (st.hasMoreTokens())
1231+ {
1232+ Player player = null;
1233+ String playername = st.nextToken();
1234+
1235+ try
1236+ {
1237+ player = World.getInstance().getPlayer(playername);
1238+ }
1239+ catch (Exception e)
1240+ {
1241+ }
1242+
1243+ if (player != null)
1244+ display(player, activeChar);
1245+ else
1246+ {
1247+ activeChar.sendMessage("The player " + playername + " is not online");
1248+ activeChar.sendMessage("Usage: //instancezone [playername]");
1249+ return false;
1250+ }
1251+ }
1252+ else if (activeChar.getTarget() != null)
1253+ {
1254+ if (activeChar.getTarget() instanceof Player)
1255+ display((Player) activeChar.getTarget(), activeChar);
1256+ else
1257+ display(activeChar, activeChar);
1258+ }
1259+ else
1260+ display(activeChar, activeChar);
1261+ break;
1262+ }
1263+ case "admin_instancezone_clear":
1264+ {
1265+ try
1266+ {
1267+ final Player player = World.getInstance().getPlayer(st.nextToken());
1268+ final int instanceId = Integer.parseInt(st.nextToken());
1269+ final String name = InstanceManager.getInstance().getInstanceName(instanceId);
1270+
1271+ InstanceManager.getInstance().deleteInstanceTime(player, instanceId);
1272+ activeChar.sendMessage("Instance zone " + name + " cleared for player " + player.getName());
1273+ player.sendMessage("Admin cleared instance zone " + name + " for you");
1274+ display(activeChar, activeChar);
1275+ }
1276+ catch (Exception e)
1277+ {
1278+ activeChar.sendMessage("Failed clearing instance time: " + e.getCause());
1279+ activeChar.sendMessage("Usage: //instancezone_clear <playername> [instanceId]");
1280+ return false;
1281+ }
1282+ break;
1283+ }
1284+ case "admin_instancepage":
1285+ {
1286+ showInstanceDetails(activeChar, st.hasMoreTokens() ? Integer.parseInt(st.nextToken()) : 1);
1287+ break;
1288+ }
1289+ case "admin_instanceplayers":
1290+ {
1291+ listPlayers(activeChar, st.hasMoreTokens() ? Integer.parseInt(st.nextToken()) : 0);
1292+ break;
1293+ }
1294+ case "admin_instance":
1295+ {
1296+ showInstanceDetails(activeChar, 1);
1297+ break;
1298+ }
1299+ case "admin_instancecreate":
1300+ {
1301+ InstanceManager.getInstance().createInstance();
1302+ showInstanceDetails(activeChar, 1);
1303+ break;
1304+ }
1305+ case "admin_instancereset":
1306+ {
1307+ activeChar.setInstanceId(0);
1308+ activeChar.decayMe();
1309+ activeChar.spawnMe();
1310+ showInstanceDetails(activeChar, 1);
1311+ break;
1312+ }
1313+ case "admin_instanceteleport":
1314+ {
1315+ WorldObject target = activeChar.getTarget();
1316+ if (target == null)
1317+ target = activeChar;
1318+
1319+ final Instance instance = InstanceManager.getInstance().getInstance(Integer.parseInt(st.nextToken()));
1320+ if (instance != null)
1321+ {
1322+ target.setInstance(instance);
1323+ target.decayMe();
1324+ target.spawnMe();
1325+ showInstanceDetails(activeChar, 1);
1326+ }
1327+ break;
1328+ }
1329+ case "admin_instancedestroy":
1330+ {
1331+ try
1332+ {
1333+ int val = Integer.parseInt(st.nextToken());
1334+
1335+ if (val <= 0)
1336+ {
1337+ activeChar.sendMessage("You can not destroy this instance.");
1338+ showInstanceDetails(activeChar, 1);
1339+ return false;
1340+ }
1341+
1342+ final Instance instance = InstanceManager.getInstance().getInstance(val);
1343+ if (instance != null)
1344+ {
1345+ instance.getPlayers().forEach(player -> player.sendPacket(new ExShowScreenMessage("Your instance has been destroyed by GM.", 5000, SMPOS.TOP_CENTER, true)));
1346+ instance.destroy();
1347+ activeChar.sendMessage("Instance " + instance.getId() + " has been destroyed.");
1348+ showInstanceDetails(activeChar, 1);
1349+ }
1350+ else
1351+ activeChar.sendMessage("Instance " + val + " doesn't exists.");
1352+ }
1353+ catch (Exception e)
1354+ {
1355+ activeChar.sendMessage("Please insert instance number you wish to delete.");
1356+ return false;
1357+ }
1358+ break;
1359+ }
1360+ }
1361+ return true;
1362+ }
1363+
1364+ private static void showInstanceDetails(Player player, int page)
1365+ {
1366+ List<Instance> instance = new ArrayList<>(InstanceManager.getInstance().getInstances());
1367+
1368+ final NpcHtmlMessage html = new NpcHtmlMessage(0);
1369+ html.setFile("data/html/admin/instances.htm");
1370+ html.replace("%instanceId%", player.getInstanceId());
1371+ html.replace("%instCount%", InstanceManager.getInstance().getInstances().size());
1372+
1373+ if (instance.isEmpty())
1374+ {
1375+ html.replace("%details%", "<br>There is no active instance(s) at the moment.");
1376+ player.sendPacket(html);
1377+ return;
1378+ }
1379+
1380+ final int max = MathUtil.countPagesNumber(instance.size(), PAGE_LIMIT);
1381+ instance = instance.subList((page - 1) * PAGE_LIMIT, Math.min(page * PAGE_LIMIT, instance.size()));
1382+
1383+ final StringBuilder sb = new StringBuilder();
1384+
1385+ for (Instance inst : instance)
1386+ {
1387+ sb.append("<table><tr>");
1388+ StringUtil.append(sb, "<td width=\"50\" align=\"center\">", inst.getName() != null ? inst.getName() : ("ID " + inst.getId()), "</td>");
1389+ StringUtil.append(sb, "<td><button value=\"Teleport\" action=\"bypass admin_instanceteleport ", inst.getId(), "\" width=\"65\" height=\"19\" back=\"L2UI_ch3.smallbutton2_down\" fore=\"L2UI_ch3.smallbutton2\"></td>");
1390+ StringUtil.append(sb, "<td><button value=\"Destroy\" action=\"bypass admin_instancedestroy ", inst.getId(), "\" width=\"65\" height=\"19\" back=\"L2UI_ch3.smallbutton2_down\" fore=\"L2UI_ch3.smallbutton2\"></td>");
1391+ StringUtil.append(sb, "<td><button value=\"Players\" action=\"bypass admin_instanceplayers ", inst.getId(), "\" width=\"65\" height=\"19\" back=\"L2UI_ch3.smallbutton2_down\" fore=\"L2UI_ch3.smallbutton2\"></td>");
1392+ sb.append("</tr></table>");
1393+ sb.append("<img src=\"L2UI.SquareGray\" width=\"255\" height=\"1\">");
1394+ }
1395+
1396+ html.replace("%details%", sb.toString());
1397+
1398+ sb.setLength(0);
1399+
1400+ for (int i = 0; i < max; i++)
1401+ {
1402+ final int pagenr = i + 1;
1403+ if (page == pagenr)
1404+ StringUtil.append(sb, pagenr, " ");
1405+ else
1406+ StringUtil.append(sb, "<a action=\"bypass admin_instancepage ", pagenr, "\">", pagenr, "</a> ");
1407+ }
1408+
1409+ html.replace("%pages%", sb.toString());
1410+ player.sendPacket(html);
1411+ }
1412+
1413+ private static void listPlayers(Player activeChar, int id)
1414+ {
1415+ final NpcHtmlMessage html = new NpcHtmlMessage(0);
1416+ final StringBuilder sb = new StringBuilder();
1417+
1418+ for (Player player : InstanceManager.getInstance().getInstance(id).getPlayers())
1419+ StringUtil.append(sb, "<tr><td width=80><a action=\"bypass admin_character_info ", player.getName(), "\">", player.getName(), "</a></td><td width=110>", player.getTemplate().getClassName(), "</td><td width=40>", player.getLevel(), "</td></tr>");
1420+
1421+ html.setFile("data/html/admin/instance_players.htm");
1422+ html.replace("%instanceId%", id);
1423+ html.replace("%playersCount%", InstanceManager.getInstance().getInstance(id).getPlayersCount());
1424+ html.replace("%players%", sb.toString());
1425+ activeChar.sendPacket(html);
1426+ }
1427+
1428+ private static void display(Player player, Player activeChar)
1429+ {
1430+ Map<Integer, Long> instanceTimes = InstanceManager.getInstance().getAllInstanceTimes(player);
1431+
1432+ final StringBuilder sb = new StringBuilder(500 + (instanceTimes.size() * 200));
1433+ sb.append("<html><center><table width=260><tr><td width=40><button value=\"Main\" action=\"bypass -h admin_admin\" width=50 height=15 back=\"sek.cbui94\" fore=\"sek.cbui92\"></td><td width=180><center>Character Instances</center></td><td width=40><button value=\"Back\" action=\"bypass -h admin_instance\" width=50 height=15 back=\"sek.cbui94\" fore=\"sek.cbui92\"></td></tr></table><br><font color=\"LEVEL\">Instances for " + player.getName() + "</font><center><br><table><tr><td width=150>Name</td><td width=50>Time</td><td align=center width=70>Action</td></tr>");
1434+
1435+ for (int id : instanceTimes.keySet())
1436+ {
1437+ int hours = 0;
1438+ int minutes = 0;
1439+ long remainingTime = (instanceTimes.get(id) - System.currentTimeMillis()) / 1000;
1440+ if (remainingTime > 0)
1441+ {
1442+ hours = (int) (remainingTime / 3600);
1443+ minutes = (int) ((remainingTime % 3600) / 60);
1444+ }
1445+
1446+ StringUtil.append(sb, "<tr><td>", InstanceManager.getInstance().getInstanceName(id), "</td><td>", hours, ":", minutes, "</td><td><button value=\"Clear\" action=\"bypass -h admin_instancezone_clear ", player.getName(), " ", id, "\" width=65 height=19 back=\"L2UI_ch3.smallbutton2_down\" fore=\"L2UI_ch3.smallbutton2\"></td></tr>");
1447+ }
1448+
1449+ sb.append("</table></html>");
1450+
1451+ final NpcHtmlMessage ms = new NpcHtmlMessage(0);
1452+ ms.setHtml(sb.toString());
1453+ activeChar.sendPacket(ms);
1454+ }
1455+
1456+ @Override
1457+ public String[] getAdminCommandList()
1458+ {
1459+ return ADMIN_COMMANDS;
1460+ }
1461+}
1462Index: /swamp_gameserver/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminSpawn.java
1463===================================================================
1464--- /swamp_gameserver/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminSpawn.java (revision 5)
1465+++ /swamp_gameserver/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminSpawn.java (revision 6)
1466@@ -316,4 +316,10 @@
1467 else
1468 {
1469+ if (activeChar.isInInstance())
1470+ {
1471+ spawn.setInstanceId(activeChar.getInstanceId());
1472+ permanent = false;
1473+ }
1474+
1475 SpawnTable.getInstance().addNewSpawn(spawn, permanent);
1476 spawn.doSpawn(false);
1477Index: /swamp_gameserver/java/net/sf/l2j/gameserver/handler/chathandlers/ChatShout.java
1478===================================================================
1479--- /swamp_gameserver/java/net/sf/l2j/gameserver/handler/chathandlers/ChatShout.java (revision 5)
1480+++ /swamp_gameserver/java/net/sf/l2j/gameserver/handler/chathandlers/ChatShout.java (revision 6)
1481@@ -28,5 +28,5 @@
1482 for (Player player : World.getInstance().getPlayers())
1483 {
1484- if (!BlockList.isBlocked(player, activeChar) && region == MapRegionData.getInstance().getMapRegion(player.getX(), player.getY()))
1485+ if (!BlockList.isBlocked(player, activeChar) && region == MapRegionData.getInstance().getMapRegion(player.getX(), player.getY()) && player.getInstanceWorld() == activeChar.getInstanceWorld())
1486 player.sendPacket(cs);
1487 }
1488Index: /swamp_gameserver/java/net/sf/l2j/gameserver/handler/chathandlers/ChatTrade.java
1489===================================================================
1490--- /swamp_gameserver/java/net/sf/l2j/gameserver/handler/chathandlers/ChatTrade.java (revision 5)
1491+++ /swamp_gameserver/java/net/sf/l2j/gameserver/handler/chathandlers/ChatTrade.java (revision 6)
1492@@ -28,5 +28,5 @@
1493 for (Player player : World.getInstance().getPlayers())
1494 {
1495- if (!BlockList.isBlocked(player, activeChar) && region == MapRegionData.getInstance().getMapRegion(player.getX(), player.getY()))
1496+ if (!BlockList.isBlocked(player, activeChar) && region == MapRegionData.getInstance().getMapRegion(player.getX(), player.getY()) && player.getInstanceWorld() == activeChar.getInstanceWorld())
1497 player.sendPacket(cs);
1498 }
1499Index: /swamp_gameserver/java/net/sf/l2j/gameserver/handler/skillhandlers/Unlock.java
1500===================================================================
1501--- /swamp_gameserver/java/net/sf/l2j/gameserver/handler/skillhandlers/Unlock.java (revision 5)
1502+++ /swamp_gameserver/java/net/sf/l2j/gameserver/handler/skillhandlers/Unlock.java (revision 6)
1503@@ -30,5 +30,5 @@
1504 {
1505 final Door door = (Door) object;
1506- if (!door.isUnlockable() && skill.getSkillType() != L2SkillType.UNLOCK_SPECIAL)
1507+ if (!door.isUnlockable() && skill.getSkillType() != L2SkillType.UNLOCK_SPECIAL || activeChar.getInstanceWorld() != door.getInstanceWorld())
1508 {
1509 activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.UNABLE_TO_UNLOCK_DOOR));
1510@@ -44,5 +44,5 @@
1511 {
1512 final Chest chest = (Chest) object;
1513- if (chest.isDead() || chest.isInteracted())
1514+ if (chest.isDead() || chest.isInteracted() || activeChar.getInstanceWorld() != chest.getInstanceWorld())
1515 return;
1516
1517Index: /swamp_gameserver/java/net/sf/l2j/gameserver/handler/usercommandhandlers/InstanceZone.java
1518===================================================================
1519--- /swamp_gameserver/java/net/sf/l2j/gameserver/handler/usercommandhandlers/InstanceZone.java (revision 6)
1520+++ /swamp_gameserver/java/net/sf/l2j/gameserver/handler/usercommandhandlers/InstanceZone.java (revision 6)
1521@@ -0,0 +1,70 @@
1522+package net.sf.l2j.gameserver.handler.usercommandhandlers;
1523+
1524+import java.util.Map;
1525+
1526+import net.sf.l2j.custom.instancezone.Instance;
1527+import net.sf.l2j.custom.instancezone.InstanceManager;
1528+import net.sf.l2j.gameserver.handler.IUserCommandHandler;
1529+import net.sf.l2j.gameserver.model.actor.instance.Player;
1530+import net.sf.l2j.gameserver.network.SystemMessageId;
1531+import net.sf.l2j.gameserver.network.serverpackets.SystemMessage;
1532+
1533+public class InstanceZone implements IUserCommandHandler
1534+{
1535+ private static final int[] COMMAND_IDS =
1536+ {
1537+ 114
1538+ };
1539+
1540+ @Override
1541+ public boolean useUserCommand(int id, Player player)
1542+ {
1543+ final Instance world = player.getInstanceWorld();
1544+ if (world != null && world.getId() > 0)
1545+ {
1546+ final String sm = "Instant Zone currently in use: " + InstanceManager.getInstance().getInstanceName(world.getId());
1547+ player.sendMessage(sm);
1548+ }
1549+
1550+ final Map<Integer, Long> instanceTimes = InstanceManager.getInstance().getAllInstanceTimes(player);
1551+ boolean firstMessage = true;
1552+
1553+ if (instanceTimes != null)
1554+ {
1555+ for (int instanceId : instanceTimes.keySet())
1556+ {
1557+ long remainingTime = (instanceTimes.get(instanceId) - System.currentTimeMillis()) / 1000;
1558+ if (remainingTime > 60)
1559+ {
1560+ if (firstMessage)
1561+ {
1562+ firstMessage = false;
1563+ player.sendMessage("Instance zone time limit:");
1564+ }
1565+
1566+ int hours = (int) (remainingTime / 3600);
1567+ int minutes = (int) ((remainingTime % 3600) / 60);
1568+
1569+ final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.AVAILABLE_AFTER_S1_S2_HOURS_S3_MINUTES);
1570+ sm.addString(InstanceManager.getInstance().getInstanceName(instanceId));
1571+ sm.addNumber(hours);
1572+ sm.addNumber(minutes);
1573+ player.sendPacket(sm);
1574+ }
1575+ else
1576+ InstanceManager.getInstance().deleteInstanceTime(player, instanceId);
1577+ }
1578+ }
1579+
1580+ if (firstMessage)
1581+ player.sendMessage("There is no instance zone under a time limit.");
1582+
1583+ return true;
1584+ }
1585+
1586+ @Override
1587+ public int[] getUserCommandList()
1588+ {
1589+ return COMMAND_IDS;
1590+ }
1591+}
1592Index: /swamp_gameserver/java/net/sf/l2j/gameserver/model/L2Spawn.java
1593===================================================================
1594--- /swamp_gameserver/java/net/sf/l2j/gameserver/model/L2Spawn.java (revision 5)
1595+++ /swamp_gameserver/java/net/sf/l2j/gameserver/model/L2Spawn.java (revision 6)
1596@@ -8,8 +8,8 @@
1597
1598 import net.sf.l2j.Config;
1599+import net.sf.l2j.custom.instancezone.Instance;
1600 import net.sf.l2j.gameserver.geoengine.GeoEngine;
1601 import net.sf.l2j.gameserver.idfactory.IdFactory;
1602 import net.sf.l2j.gameserver.model.actor.Attackable;
1603-import net.sf.l2j.gameserver.model.actor.Creature;
1604 import net.sf.l2j.gameserver.model.actor.Npc;
1605 import net.sf.l2j.gameserver.model.actor.instance.Monster;
1606@@ -47,4 +47,6 @@
1607 private int _respawnMaxDelay;
1608
1609+ private int _instanceId;
1610+
1611 /**
1612 * Constructor of L2Spawn.<BR>
1613@@ -310,7 +312,4 @@
1614 Object tmp = _constructor.newInstance(parameters);
1615
1616- if (isSummonSpawn && tmp instanceof Creature)
1617- ((Creature) tmp).setShowSummonAnimation(isSummonSpawn);
1618-
1619 // Check if the Instance is a L2Npc
1620 if (!(tmp instanceof Npc))
1621@@ -320,4 +319,10 @@
1622 _npc = (Npc) tmp;
1623
1624+ // must be done before object is spawned into visible world
1625+ _npc.setInstanceId(_instanceId);
1626+
1627+ if (isSummonSpawn)
1628+ _npc.setShowSummonAnimation(isSummonSpawn);
1629+
1630 // assign L2Spawn to L2Npc
1631 _npc.setSpawn(this);
1632@@ -362,4 +367,9 @@
1633
1634 initializeAndSpawn();
1635+
1636+ // Register NPC back to instance world
1637+ final Instance instance = _npc.getInstanceWorld();
1638+ if (instance != null)
1639+ instance.addNpc(_npc);
1640 }
1641 }
1642@@ -403,5 +413,5 @@
1643 {
1644 // It can't be a Raid, a Raid minion nor a minion. Quest mobs and chests are disabled too.
1645- if (_npc instanceof Monster && !getTemplate().cantBeChampion() && _npc.getLevel() >= Config.CHAMP_MIN_LVL && _npc.getLevel() <= Config.CHAMP_MAX_LVL && !_npc.isRaid() && !((Monster) _npc).isRaidMinion() && !((Monster) _npc).isMinion())
1646+ if (_npc instanceof Monster && !getTemplate().cantBeChampion() && _npc.getLevel() >= Config.CHAMP_MIN_LVL && _npc.getLevel() <= Config.CHAMP_MAX_LVL && !_npc.isRaid() && !((Monster) _npc).isRaidMinion() && !((Monster) _npc).isMinion() && getInstanceId() == 0)
1647 ((Attackable) _npc).setChampion(Rnd.get(100) < Config.CHAMPION_FREQUENCY);
1648 }
1649@@ -419,3 +429,13 @@
1650 return "L2Spawn [id=" + _template.getNpcId() + ", loc=" + _loc.toString() + "]";
1651 }
1652+
1653+ public int getInstanceId()
1654+ {
1655+ return _instanceId;
1656+ }
1657+
1658+ public void setInstanceId(int instanceId)
1659+ {
1660+ _instanceId = instanceId;
1661+ }
1662 }
1663Index: /swamp_gameserver/java/net/sf/l2j/gameserver/model/MinionList.java
1664===================================================================
1665--- /swamp_gameserver/java/net/sf/l2j/gameserver/model/MinionList.java (revision 5)
1666+++ /swamp_gameserver/java/net/sf/l2j/gameserver/model/MinionList.java (revision 6)
1667@@ -217,4 +217,7 @@
1668 minion.setLeader(master);
1669
1670+ // move monster to masters instance
1671+ minion.setInstance(master.getInstanceWorld());
1672+
1673 // Init the position of the Minion and add it in the world as a visible object
1674 final int offset = (int) (100 + minion.getCollisionRadius() + master.getCollisionRadius());
1675Index: /swamp_gameserver/java/net/sf/l2j/gameserver/model/WorldObject.java
1676===================================================================
1677--- /swamp_gameserver/java/net/sf/l2j/gameserver/model/WorldObject.java (revision 5)
1678+++ /swamp_gameserver/java/net/sf/l2j/gameserver/model/WorldObject.java (revision 6)
1679@@ -8,4 +8,6 @@
1680 import net.sf.l2j.commons.math.MathUtil;
1681
1682+import net.sf.l2j.custom.instancezone.Instance;
1683+import net.sf.l2j.custom.instancezone.InstanceManager;
1684 import net.sf.l2j.gameserver.data.ItemTable;
1685 import net.sf.l2j.gameserver.data.NpcTable;
1686@@ -46,4 +48,6 @@
1687 private boolean _isVisible;
1688
1689+ private Instance _instance;
1690+
1691 public WorldObject(int objectId)
1692 {
1693@@ -383,5 +387,5 @@
1694 for (WorldObject obj : region.getObjects())
1695 {
1696- if (obj == this)
1697+ if (obj == this || obj.getInstanceWorld() != getInstanceWorld())
1698 continue;
1699
1700@@ -473,3 +477,66 @@
1701 return result;
1702 }
1703+
1704+ /**
1705+ * Gets the instance ID.
1706+ * @return the instance ID
1707+ */
1708+ public int getInstanceId()
1709+ {
1710+ final Instance instance = _instance;
1711+ return (instance != null) ? instance.getId() : 0;
1712+ }
1713+
1714+ /**
1715+ * Check if object is inside instance world.
1716+ * @return {@code true} when object is inside any instance world, otherwise {@code false}
1717+ */
1718+ public boolean isInInstance()
1719+ {
1720+ return _instance != null;
1721+ }
1722+
1723+ /**
1724+ * Get instance world where object is currently located.
1725+ * @return {@link Instance} if object is inside instance world, otherwise {@code null}
1726+ */
1727+ public Instance getInstanceWorld()
1728+ {
1729+ return _instance;
1730+ }
1731+
1732+ /**
1733+ * Sets instance for current object by instance ID.<br>
1734+ * @param id ID of instance world which should be set (0 means normal world)
1735+ */
1736+ public void setInstanceId(int id)
1737+ {
1738+ final Instance instance = InstanceManager.getInstance().getInstance(id);
1739+ if ((id != 0) && (instance == null))
1740+ return;
1741+
1742+ setInstance(instance);
1743+ }
1744+
1745+ /**
1746+ * Sets instance where current object belongs.
1747+ * @param newInstance new instance world for object
1748+ */
1749+ public synchronized void setInstance(Instance newInstance)
1750+ {
1751+ // Check if new and old instances are identical
1752+ if (_instance == newInstance)
1753+ return;
1754+
1755+ // Leave old instance
1756+ if (_instance != null)
1757+ _instance.onInstanceChange(this, false);
1758+
1759+ // Set new instance
1760+ _instance = newInstance;
1761+
1762+ // Enter into new instance
1763+ if (newInstance != null)
1764+ newInstance.onInstanceChange(this, true);
1765+ }
1766 }
1767Index: /swamp_gameserver/java/net/sf/l2j/gameserver/model/actor/Creature.java
1768===================================================================
1769--- /swamp_gameserver/java/net/sf/l2j/gameserver/model/actor/Creature.java (revision 5)
1770+++ /swamp_gameserver/java/net/sf/l2j/gameserver/model/actor/Creature.java (revision 6)
1771@@ -14,4 +14,5 @@
1772
1773 import net.sf.l2j.Config;
1774+import net.sf.l2j.custom.instancezone.Instance;
1775 import net.sf.l2j.gameserver.data.SkillTable.FrequentSkill;
1776 import net.sf.l2j.gameserver.data.xml.MapRegionData;
1777@@ -3980,4 +3981,8 @@
1778 return false;
1779
1780+ // Can't interact from another instance.
1781+ if (player.getInstanceWorld() != getInstanceWorld())
1782+ return false;
1783+
1784 return true;
1785 }
1786@@ -3986,4 +3991,8 @@
1787 {
1788 if (target == null)
1789+ return false;
1790+
1791+ final Instance instanceWorld = attacker.getInstanceWorld();
1792+ if (instanceWorld != null && instanceWorld.isPvP())
1793 return false;
1794
1795Index: /swamp_gameserver/java/net/sf/l2j/gameserver/model/actor/Npc.java
1796===================================================================
1797--- /swamp_gameserver/java/net/sf/l2j/gameserver/model/actor/Npc.java (revision 5)
1798+++ /swamp_gameserver/java/net/sf/l2j/gameserver/model/actor/Npc.java (revision 6)
1799@@ -12,4 +12,5 @@
1800
1801 import net.sf.l2j.Config;
1802+import net.sf.l2j.custom.instancezone.Instance;
1803 import net.sf.l2j.gameserver.cache.HtmCache;
1804 import net.sf.l2j.gameserver.data.ItemTable;
1805@@ -1323,4 +1324,9 @@
1806 if (_spawn != null)
1807 _spawn.doRespawn();
1808+
1809+ // Remove from instance world
1810+ final Instance instance = getInstanceWorld();
1811+ if (instance != null)
1812+ instance.removeNpc(this);
1813 }
1814
1815Index: /swamp_gameserver/java/net/sf/l2j/gameserver/model/actor/Playable.java
1816===================================================================
1817--- /swamp_gameserver/java/net/sf/l2j/gameserver/model/actor/Playable.java (revision 5)
1818+++ /swamp_gameserver/java/net/sf/l2j/gameserver/model/actor/Playable.java (revision 6)
1819@@ -2,4 +2,5 @@
1820
1821 import net.sf.l2j.Config;
1822+import net.sf.l2j.custom.instancezone.Instance;
1823 import net.sf.l2j.gameserver.model.L2Effect;
1824 import net.sf.l2j.gameserver.model.L2Skill;
1825@@ -138,4 +139,12 @@
1826 qs.getQuest().notifyDeath((killer == null ? this : killer), actingPlayer);
1827
1828+ // Notify instance
1829+ if (this instanceof Player)
1830+ {
1831+ final Instance instance = getInstanceWorld();
1832+ if (instance != null)
1833+ instance.onDeath(getActingPlayer());
1834+ }
1835+
1836 if (killer != null)
1837 {
1838Index: /swamp_gameserver/java/net/sf/l2j/gameserver/model/actor/Summon.java
1839===================================================================
1840--- /swamp_gameserver/java/net/sf/l2j/gameserver/model/actor/Summon.java (revision 5)
1841+++ /swamp_gameserver/java/net/sf/l2j/gameserver/model/actor/Summon.java (revision 6)
1842@@ -60,4 +60,5 @@
1843 addStatFuncs(skill.getStatFuncs(this));
1844
1845+ setInstance(owner.getInstanceWorld());
1846 _showSummonAnimation = true;
1847 _owner = owner;
1848Index: /swamp_gameserver/java/net/sf/l2j/gameserver/model/actor/instance/EffectPoint.java
1849===================================================================
1850--- /swamp_gameserver/java/net/sf/l2j/gameserver/model/actor/instance/EffectPoint.java (revision 5)
1851+++ /swamp_gameserver/java/net/sf/l2j/gameserver/model/actor/instance/EffectPoint.java (revision 6)
1852@@ -14,4 +14,6 @@
1853 super(objectId, template);
1854 _owner = owner == null ? null : owner.getActingPlayer();
1855+ if (owner != null)
1856+ setInstance(owner.getInstanceWorld());
1857 }
1858
1859Index: /swamp_gameserver/java/net/sf/l2j/gameserver/model/actor/instance/Player.java
1860===================================================================
1861--- /swamp_gameserver/java/net/sf/l2j/gameserver/model/actor/instance/Player.java (revision 5)
1862+++ /swamp_gameserver/java/net/sf/l2j/gameserver/model/actor/instance/Player.java (revision 6)
1863@@ -29,4 +29,5 @@
1864 import net.sf.l2j.Config;
1865 import net.sf.l2j.L2DatabaseFactory;
1866+import net.sf.l2j.custom.instancezone.Instance;
1867 import net.sf.l2j.gameserver.LoginServerThread;
1868 import net.sf.l2j.gameserver.communitybbs.BB.Forum;
1869@@ -65,5 +66,4 @@
1870 import net.sf.l2j.gameserver.model.L2Macro;
1871 import net.sf.l2j.gameserver.model.L2Radar;
1872-import net.sf.l2j.gameserver.model.Request;
1873 import net.sf.l2j.gameserver.model.L2ShortCut;
1874 import net.sf.l2j.gameserver.model.L2Skill;
1875@@ -71,4 +71,5 @@
1876 import net.sf.l2j.gameserver.model.MacroList;
1877 import net.sf.l2j.gameserver.model.PetDataEntry;
1878+import net.sf.l2j.gameserver.model.Request;
1879 import net.sf.l2j.gameserver.model.RewardInfo;
1880 import net.sf.l2j.gameserver.model.ShortCuts;
1881@@ -3883,4 +3884,8 @@
1882 return;
1883
1884+ final Instance instance = getInstanceWorld();
1885+ if (instance != null && instance.isPvP())
1886+ return;
1887+
1888 PvpFlagTaskManager.getInstance().add(this, Config.PVP_NORMAL_TIME);
1889
1890@@ -3896,4 +3901,8 @@
1891
1892 if (isInDuel() && player.getDuelId() == getDuelId())
1893+ return;
1894+
1895+ final Instance instance = getInstanceWorld();
1896+ if (instance != null && instance.isPvP())
1897 return;
1898
1899@@ -6228,4 +6237,9 @@
1900 return false;
1901
1902+ // Check if the attacker is inside instance
1903+ final Instance instanceWorld = attacker.getInstanceWorld();
1904+ if (instanceWorld != null && instanceWorld.isPvP())
1905+ return true;
1906+
1907 // Check if the attacker is a L2Playable
1908 if (attacker instanceof Playable)
1909@@ -6899,4 +6913,8 @@
1910 return true;
1911
1912+ final Instance instance = getInstanceWorld();
1913+ if (instance != null && instance.isPvP())
1914+ return true;
1915+
1916 if (skill.isDebuff() || skill.isOffensive())
1917 {
1918@@ -8285,4 +8303,9 @@
1919 startFeed(_mountNpcId);
1920
1921+ // Notify instance
1922+ final Instance instance = getInstanceWorld();
1923+ if (instance != null)
1924+ instance.doRevive(this);
1925+
1926 // Schedule a paralyzed task to wait for the animation to finish
1927 ThreadPool.schedule(() -> setIsParalyzed(false), (int) (2000 / getStat().getMovementSpeedMultiplier()));
1928@@ -8424,4 +8447,6 @@
1929 ((SummonAI) pet.getAI()).setStartFollowController(true);
1930 pet.setFollowStatus(true);
1931+ pet.setInstance(getInstanceWorld());
1932+ pet.updateAndBroadcastStatus(0);
1933 }
1934 }
1935@@ -8677,4 +8702,9 @@
1936 if (getVehicle() != null)
1937 getVehicle().oustPlayer(this, true, Location.DUMMY_LOC);
1938+
1939+ // remove player from instance
1940+ final Instance inst = getInstanceWorld();
1941+ if (inst != null)
1942+ inst.onPlayerLogout(this);
1943
1944 // Update inventory and remove them from the world
1945@@ -9828,4 +9858,11 @@
1946 return false;
1947
1948+ final Instance instance = summonerChar.getInstanceWorld();
1949+ if (instance != null && !instance.isSummonAllowed())
1950+ {
1951+ summonerChar.sendPacket(SystemMessageId.YOU_MAY_NOT_SUMMON_FROM_YOUR_CURRENT_LOCATION);
1952+ return false;
1953+ }
1954+
1955 return true;
1956 }
1957Index: /swamp_gameserver/java/net/sf/l2j/gameserver/model/item/instance/ItemInstance.java
1958===================================================================
1959--- /swamp_gameserver/java/net/sf/l2j/gameserver/model/item/instance/ItemInstance.java (revision 5)
1960+++ /swamp_gameserver/java/net/sf/l2j/gameserver/model/item/instance/ItemInstance.java (revision 6)
1961@@ -16,4 +16,5 @@
1962 import net.sf.l2j.Config;
1963 import net.sf.l2j.L2DatabaseFactory;
1964+import net.sf.l2j.custom.instancezone.Instance;
1965 import net.sf.l2j.gameserver.data.ItemTable;
1966 import net.sf.l2j.gameserver.geoengine.GeoEngine;
1967@@ -952,9 +953,13 @@
1968 if (_dropper != null)
1969 {
1970- Location dropDest = GeoEngine.getInstance().canMoveToTargetLoc(_dropper.getX(), _dropper.getY(), _dropper.getZ(), _x, _y, _z);
1971+ final Instance instance = _dropper.getInstanceWorld();
1972+ final Location dropDest = GeoEngine.getInstance().canMoveToTargetLoc(_dropper.getX(), _dropper.getY(), _dropper.getZ(), _x, _y, _z);
1973 _x = dropDest.getX();
1974 _y = dropDest.getY();
1975 _z = dropDest.getZ();
1976+ setInstance(instance); // Inherit instancezone when dropped in visible world
1977 }
1978+ else
1979+ setInstanceId(0);
1980
1981 _itm.setDropperObjectId(_dropper != null ? _dropper.getObjectId() : 0); // Set the dropper Id for the knownlist packets in sendInfo
1982Index: /swamp_gameserver/java/net/sf/l2j/gameserver/network/SystemMessageId.java
1983===================================================================
1984--- /swamp_gameserver/java/net/sf/l2j/gameserver/network/SystemMessageId.java (revision 5)
1985+++ /swamp_gameserver/java/net/sf/l2j/gameserver/network/SystemMessageId.java (revision 6)
1986@@ -11798,4 +11798,16 @@
1987 */
1988 public static final SystemMessageId PLEASE_WAIT_A_MOMENT;
1989+
1990+ /**
1991+ * ID: 2500<br>
1992+ * Message: This instant zone will be terminated in $s1 minute(s). You will be forced out of the dungeon when the time expires.
1993+ */
1994+ public static final SystemMessageId THIS_INSTANT_ZONE_WILL_BE_TERMINATED_IN_S1_MINUTE_S_YOU_WILL_BE_FORCED_OUT_OF_THE_DUNGEON_WHEN_THE_TIME_EXPIRES;
1995+
1996+ /**
1997+ * ID: 2501<br>
1998+ * Message: $s1 will be available for re-use after $s2 hour(s) $s3 minute(s).
1999+ */
2000+ public static final SystemMessageId AVAILABLE_AFTER_S1_S2_HOURS_S3_MINUTES;
2001
2002 /**
2003@@ -13767,4 +13779,6 @@
2004 CURRENTLY_LOGGING_IN = new SystemMessageId(2030);
2005 PLEASE_WAIT_A_MOMENT = new SystemMessageId(2031);
2006+ THIS_INSTANT_ZONE_WILL_BE_TERMINATED_IN_S1_MINUTE_S_YOU_WILL_BE_FORCED_OUT_OF_THE_DUNGEON_WHEN_THE_TIME_EXPIRES = new SystemMessageId(2500);
2007+ AVAILABLE_AFTER_S1_S2_HOURS_S3_MINUTES = new SystemMessageId(2501);
2008
2009 buildFastLookupTable();
2010Index: /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/Action.java
2011===================================================================
2012--- /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/Action.java (revision 5)
2013+++ /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/Action.java (revision 6)
2014@@ -51,4 +51,11 @@
2015 }
2016
2017+ // Players can't interact with objects in other instance
2018+ if (obj.getInstanceWorld() != activeChar.getInstanceWorld())
2019+ {
2020+ activeChar.sendPacket(ActionFailed.STATIC_PACKET);
2021+ return;
2022+ }
2023+
2024 switch (_actionId)
2025 {
2026Index: /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/AttackRequest.java
2027===================================================================
2028--- /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/AttackRequest.java (revision 5)
2029+++ /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/AttackRequest.java (revision 6)
2030@@ -50,4 +50,11 @@
2031 return;
2032
2033+ // Players can't attack objects in the other instances
2034+ if (target.getInstanceWorld() != activeChar.getInstanceWorld())
2035+ {
2036+ activeChar.sendPacket(ActionFailed.STATIC_PACKET);
2037+ return;
2038+ }
2039+
2040 if (activeChar.getTarget() != target)
2041 target.onAction(activeChar);
2042Index: /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/EnterWorld.java
2043===================================================================
2044--- /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/EnterWorld.java (revision 5)
2045+++ /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/EnterWorld.java (revision 6)
2046@@ -9,4 +9,6 @@
2047 import net.sf.l2j.Config;
2048 import net.sf.l2j.L2DatabaseFactory;
2049+import net.sf.l2j.custom.instancezone.Instance;
2050+import net.sf.l2j.custom.instancezone.InstanceManager;
2051 import net.sf.l2j.gameserver.communitybbs.Manager.MailBBSManager;
2052 import net.sf.l2j.gameserver.data.SkillTable.FrequentSkill;
2053@@ -82,4 +84,17 @@
2054 final int objectId = activeChar.getObjectId();
2055
2056+ // Restore to instanced area if enabled
2057+ if (Config.RESTORE_PLAYER_INSTANCE)
2058+ {
2059+ final int instanceId = activeChar.getMemos().getInteger("INSTANCE_RESTORE", 0);
2060+ final Instance instance = InstanceManager.getInstance().getInstance(instanceId);
2061+ if (instance != null)
2062+ activeChar.setInstance(instance);
2063+
2064+ activeChar.getMemos().remove("INSTANCE_RESTORE");
2065+ }
2066+ else
2067+ activeChar.setInstanceId(0);
2068+
2069 if (activeChar.isGM())
2070 {
2071Index: /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/MultiSellChoose.java
2072===================================================================
2073--- /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/MultiSellChoose.java (revision 5)
2074+++ /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/MultiSellChoose.java (revision 6)
2075@@ -65,5 +65,5 @@
2076
2077 final Npc npc = player.getCurrentFolk();
2078- if ((npc != null && !list.isNpcAllowed(npc.getNpcId())) || (npc == null && list.isNpcOnly()))
2079+ if ((npc != null && !list.isNpcAllowed(npc.getNpcId())) || (npc == null && list.isNpcOnly()) || (npc != null && npc.getInstanceWorld() != player.getInstanceWorld()))
2080 {
2081 player.setMultiSell(null);
2082Index: /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/RequestBuyItem.java
2083===================================================================
2084--- /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/RequestBuyItem.java (revision 5)
2085+++ /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/RequestBuyItem.java (revision 6)
2086@@ -74,5 +74,5 @@
2087 merchant = (Npc) target;
2088
2089- if (merchant == null || !buyList.isNpcAllowed(merchant.getNpcId()) || !merchant.canInteract(player))
2090+ if (merchant == null || !buyList.isNpcAllowed(merchant.getNpcId()) || !merchant.canInteract(player) || player.getInstanceWorld() != target.getInstanceWorld())
2091 return;
2092
2093Index: /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/RequestPrivateStoreBuy.java
2094===================================================================
2095--- /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/RequestPrivateStoreBuy.java (revision 5)
2096+++ /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/RequestPrivateStoreBuy.java (revision 6)
2097@@ -59,4 +59,7 @@
2098 return;
2099
2100+ if (player.getInstanceWorld() != storePlayer.getInstanceWorld())
2101+ return;
2102+
2103 if (player.isCursedWeaponEquipped())
2104 return;
2105Index: /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/RequestPrivateStoreSell.java
2106===================================================================
2107--- /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/RequestPrivateStoreSell.java (revision 5)
2108+++ /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/RequestPrivateStoreSell.java (revision 6)
2109@@ -58,4 +58,7 @@
2110 return;
2111
2112+ if (player.getInstanceWorld() != storePlayer.getInstanceWorld())
2113+ return;
2114+
2115 if (!player.isInsideRadius(storePlayer, 150, true, false))
2116 return;
2117Index: /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/RequestRecipeShopMakeItem.java
2118===================================================================
2119--- /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/RequestRecipeShopMakeItem.java (revision 5)
2120+++ /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/RequestRecipeShopMakeItem.java (revision 6)
2121@@ -42,4 +42,7 @@
2122 return;
2123
2124+ if (manufacturer.getInstanceWorld() != player.getInstanceWorld())
2125+ return;
2126+
2127 if (player.isInStoreMode())
2128 return;
2129Index: /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/RequestRestartPoint.java
2130===================================================================
2131--- /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/RequestRestartPoint.java (revision 5)
2132+++ /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/RequestRestartPoint.java (revision 6)
2133@@ -147,4 +147,5 @@
2134 player.doRevive();
2135
2136+ player.setInstanceId(0);
2137 player.teleToLocation(loc, 20);
2138 }
2139Index: /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/RequestSellItem.java
2140===================================================================
2141--- /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/RequestSellItem.java (revision 5)
2142+++ /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/RequestSellItem.java (revision 6)
2143@@ -57,5 +57,5 @@
2144
2145 final Npc merchant = (player.getTarget() instanceof Merchant || player.getTarget() instanceof MercenaryManagerNpc) ? (Npc) player.getTarget() : null;
2146- if (merchant == null || !merchant.canInteract(player))
2147+ if (merchant == null || !merchant.canInteract(player) || player.getInstanceWorld() != merchant.getInstanceWorld())
2148 return;
2149
2150Index: /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/TradeDone.java
2151===================================================================
2152--- /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/TradeDone.java (revision 5)
2153+++ /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/TradeDone.java (revision 6)
2154@@ -58,4 +58,10 @@
2155 }
2156
2157+ if (player.getInstanceWorld() != partner.getInstanceWorld())
2158+ {
2159+ player.cancelActiveTrade();
2160+ return;
2161+ }
2162+
2163 // Sender under enchant process, close it.
2164 if (owner.getActiveEnchantItem() != null)
2165Index: /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/TradeRequest.java
2166===================================================================
2167--- /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/TradeRequest.java (revision 5)
2168+++ /swamp_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/TradeRequest.java (revision 6)
2169@@ -36,5 +36,5 @@
2170
2171 final Player target = World.getInstance().getPlayer(_objectId);
2172- if (target == null || !player.getKnownType(Player.class).contains(target) || target.equals(player))
2173+ if (target == null || !player.getKnownType(Player.class).contains(target) || target.equals(player) || target.getInstanceWorld() != player.getInstanceWorld())
2174 {
2175 player.sendPacket(SystemMessageId.TARGET_IS_INCORRECT);