· 6 years ago · Mar 07, 2019, 05:02 PM
1### Eclipse Workspace Patch 1.0
2#P aCis_gameserver
3Index: java/net/sf/l2j/gameserver/model/actor/status/PlayerStatus.java
4===================================================================
5--- java/net/sf/l2j/gameserver/model/actor/status/PlayerStatus.java (revision 1002)
6+++ java/net/sf/l2j/gameserver/model/actor/status/PlayerStatus.java (working copy)
7@@ -10,6 +10,7 @@
8 import net.sf.l2j.gameserver.model.actor.Summon;
9 import net.sf.l2j.gameserver.model.actor.ai.CtrlIntention;
10 import net.sf.l2j.gameserver.model.actor.instance.Player;
11+import net.sf.l2j.gameserver.model.actor.instance.Player.StoreType;
12 import net.sf.l2j.gameserver.model.actor.instance.Servitor;
13 import net.sf.l2j.gameserver.model.actor.stat.PlayerStat;
14 import net.sf.l2j.gameserver.model.entity.Duel.DuelState;
15@@ -55,6 +56,10 @@
16 if (getActiveChar().isDead())
17 return;
18
19+ // If OFFLINE_MODE_NO_DAMAGE is enabled and player is offline and he is in store/craft mode, no damage is taken.
20+ if (Config.OFFLINE_MODE_NO_DAMAGE && (getActiveChar().getClient() != null) && getActiveChar().getClient().isDetached() && ((Config.OFFLINE_TRADE_ENABLE && ((getActiveChar().getStoreType() == StoreType.SELL) || (getActiveChar().getStoreType() == StoreType.BUY))) || (Config.OFFLINE_CRAFT_ENABLE && (getActiveChar().isCrafting() || (getActiveChar().getStoreType() == StoreType.MANUFACTURE)))))
21+ return;
22+
23 // invul handling
24 if (getActiveChar().isInvul())
25 {
26Index: java/net/sf/l2j/Config.java
27===================================================================
28--- java/net/sf/l2j/Config.java (revision 1002)
29+++ java/net/sf/l2j/Config.java (working copy)
30@@ -36,8 +36,24 @@
31 public static final String PLAYERS_FILE = "./config/players.properties";
32 public static final String SERVER_FILE = "./config/server.properties";
33 public static final String SIEGE_FILE = "./config/siege.properties";
34+ public static final String CUSTOM_FILE = "./config/custom.properties";
35
36 // --------------------------------------------------
37+ // Custom settings
38+ // --------------------------------------------------
39+
40+ /** Offline Shop */
41+ public static boolean OFFLINE_TRADE_ENABLE;
42+ public static boolean OFFLINE_CRAFT_ENABLE;
43+ public static boolean OFFLINE_MODE_IN_PEACE_ZONE;
44+ public static boolean OFFLINE_MODE_NO_DAMAGE;
45+ public static boolean RESTORE_OFFLINERS;
46+ public static int OFFLINE_MAX_DAYS;
47+ public static boolean OFFLINE_DISCONNECT_FINISHED;
48+ public static boolean OFFLINE_SET_NAME_COLOR;
49+ public static int OFFLINE_NAME_COLOR;
50+
51+ // --------------------------------------------------
52 // Clans settings
53 // --------------------------------------------------
54
55@@ -883,6 +899,23 @@
56 }
57
58 /**
59+ * Loads custom settings
60+ */
61+ private static final void loadCustoms()
62+ {
63+ final ExProperties custom = initProperties(CUSTOM_FILE);
64+ OFFLINE_TRADE_ENABLE = custom.getProperty("OfflineTradeEnable", false);
65+ OFFLINE_CRAFT_ENABLE = custom.getProperty("OfflineCraftEnable", false);
66+ OFFLINE_MODE_IN_PEACE_ZONE = custom.getProperty("OfflineModeInPeaceZone", false);
67+ OFFLINE_MODE_NO_DAMAGE = custom.getProperty("OfflineModeNoDamage", false);
68+ OFFLINE_SET_NAME_COLOR = custom.getProperty("OfflineSetNameColor", false);
69+ OFFLINE_NAME_COLOR = Integer.decode("0x" + custom.getProperty("OfflineNameColor", "808080"));
70+ RESTORE_OFFLINERS = custom.getProperty("RestoreOffliners", false);
71+ OFFLINE_MAX_DAYS = custom.getProperty("OfflineMaxDays", 10);
72+ OFFLINE_DISCONNECT_FINISHED = custom.getProperty("OfflineDisconnectFinished", true);
73+ }
74+
75+ /**
76 * Loads hex ID settings.
77 */
78 private static final void loadHexID()
79@@ -1347,6 +1380,9 @@
80 // players settings
81 loadPlayers();
82
83+ // custom settings
84+ loadCustoms();
85+
86 // siege settings
87 loadSieges();
88
89Index: java/net/sf/l2j/gameserver/GameServer.java
90===================================================================
91--- java/net/sf/l2j/gameserver/GameServer.java (revision 1002)
92+++ java/net/sf/l2j/gameserver/GameServer.java (working copy)
93@@ -37,6 +37,7 @@
94 import net.sf.l2j.gameserver.data.manager.ZoneManager;
95 import net.sf.l2j.gameserver.data.sql.BookmarkTable;
96 import net.sf.l2j.gameserver.data.sql.ClanTable;
97+import net.sf.l2j.gameserver.data.sql.OfflineTradersTable;
98 import net.sf.l2j.gameserver.data.sql.PlayerInfoTable;
99 import net.sf.l2j.gameserver.data.sql.ServerMemoTable;
100 import net.sf.l2j.gameserver.data.xml.AdminData;
101@@ -278,6 +279,10 @@
102 LOGGER.info("Loaded {} skill handlers.", SkillHandler.getInstance().size());
103 LOGGER.info("Loaded {} user command handlers.", UserCommandHandler.getInstance().size());
104
105+ StringUtil.printSection("Customs");
106+ if ((Config.OFFLINE_TRADE_ENABLE || Config.OFFLINE_CRAFT_ENABLE) && Config.RESTORE_OFFLINERS)
107+ OfflineTradersTable.restoreOfflineTraders();
108+
109 StringUtil.printSection("System");
110 Runtime.getRuntime().addShutdownHook(Shutdown.getInstance());
111 ForumsBBSManager.getInstance();
112Index: java/net/sf/l2j/gameserver/network/serverpackets/CharInfo.java
113===================================================================
114--- java/net/sf/l2j/gameserver/network/serverpackets/CharInfo.java (revision 1002)
115+++ java/net/sf/l2j/gameserver/network/serverpackets/CharInfo.java (working copy)
116@@ -192,7 +192,10 @@
117 writeD(0);
118 }
119
120- writeD(_activeChar.getAppearance().getNameColor());
121+ if ((Config.OFFLINE_TRADE_ENABLE || Config.OFFLINE_CRAFT_ENABLE) && Config.OFFLINE_SET_NAME_COLOR && (_activeChar.getClient() == null || _activeChar.getClient().isDetached()))
122+ writeD(Config.OFFLINE_NAME_COLOR);
123+ else
124+ writeD(_activeChar.getAppearance().getNameColor());
125
126 writeD(0x00); // isRunning() as in UserInfo?
127
128Index: java/net/sf/l2j/gameserver/data/sql/OfflineTradersTable.java
129===================================================================
130--- java/net/sf/l2j/gameserver/data/sql/OfflineTradersTable.java (nonexistent)
131+++ java/net/sf/l2j/gameserver/data/sql/OfflineTradersTable.java (working copy)
132@@ -0,0 +1,276 @@
133+package net.sf.l2j.gameserver.data.sql;
134+
135+import java.sql.Connection;
136+import java.sql.PreparedStatement;
137+import java.sql.ResultSet;
138+import java.sql.SQLException;
139+import java.sql.Statement;
140+import java.util.Calendar;
141+import java.util.logging.Level;
142+import java.util.logging.Logger;
143+
144+import net.sf.l2j.Config;
145+import net.sf.l2j.L2DatabaseFactory;
146+import net.sf.l2j.gameserver.LoginServerThread;
147+import net.sf.l2j.gameserver.model.World;
148+import net.sf.l2j.gameserver.model.actor.instance.Player;
149+import net.sf.l2j.gameserver.model.actor.instance.Player.StoreType;
150+import net.sf.l2j.gameserver.model.craft.ManufactureItem;
151+import net.sf.l2j.gameserver.model.craft.ManufactureList;
152+import net.sf.l2j.gameserver.model.tradelist.TradeItem;
153+import net.sf.l2j.gameserver.model.zone.ZoneId;
154+import net.sf.l2j.gameserver.network.L2GameClient;
155+import net.sf.l2j.gameserver.network.L2GameClient.GameClientState;
156+import net.sf.l2j.gameserver.skills.AbnormalEffect;
157+
158+public final class OfflineTradersTable
159+{
160+ private static final Logger LOGGER = Logger.getLogger(OfflineTradersTable.class.getName());
161+
162+ private static final String SAVE_OFFLINE_STATUS = "INSERT INTO character_offline_trade (charId, time, type, title) VALUES (?, ?, ?, ?)";
163+ private static final String SAVE_ITEMS = "INSERT INTO character_offline_trade_items (charId, item, count, price) VALUES (?, ?, ?, ?)";
164+ private static final String CLEAR_OFFLINE_TABLE = "DELETE FROM character_offline_trade";
165+ private static final String CLEAR_OFFLINE_TABLE_ITEMS = "DELETE FROM character_offline_trade_items";
166+ private static final String LOAD_OFFLINE_STATUS = "SELECT * FROM character_offline_trade";
167+ private static final String LOAD_OFFLINE_ITEMS = "SELECT * FROM character_offline_trade_items WHERE charId=?";
168+
169+ public static void storeOffliners()
170+ {
171+ try (final Connection con = L2DatabaseFactory.getInstance().getConnection();
172+ final PreparedStatement save_offline_status = con.prepareStatement(SAVE_OFFLINE_STATUS);
173+ final PreparedStatement save_items = con.prepareStatement(SAVE_ITEMS))
174+ {
175+ try (final Statement stm = con.createStatement())
176+ {
177+ stm.execute(CLEAR_OFFLINE_TABLE);
178+ stm.execute(CLEAR_OFFLINE_TABLE_ITEMS);
179+ }
180+
181+ for (final Player pc : World.getInstance().getPlayers())
182+ {
183+ try
184+ {
185+ if (pc.getStoreType() != StoreType.NONE && (pc.getClient() == null || pc.getClient().isDetached()))
186+ {
187+ save_offline_status.setInt(1, pc.getObjectId());
188+ save_offline_status.setLong(2, pc.getOfflineStartTime());
189+ save_offline_status.setInt(3, pc.getStoreType().getId());
190+ switch (pc.getStoreType())
191+ {
192+ case BUY:
193+ {
194+ if (!Config.OFFLINE_TRADE_ENABLE)
195+ continue;
196+
197+ save_offline_status.setString(4, pc.getBuyList().getTitle());
198+ for (final TradeItem i : pc.getBuyList().getItems())
199+ {
200+ save_items.setInt(1, pc.getObjectId());
201+ save_items.setInt(2, i.getItem().getItemId());
202+ save_items.setLong(3, i.getCount());
203+ save_items.setLong(4, i.getPrice());
204+ save_items.addBatch();
205+ }
206+ break;
207+ }
208+ case SELL:
209+ case PACKAGE_SELL:
210+ {
211+ if (!Config.OFFLINE_TRADE_ENABLE)
212+ continue;
213+
214+ save_offline_status.setString(4, pc.getSellList().getTitle());
215+ for (final TradeItem i : pc.getSellList().getItems())
216+ {
217+ save_items.setInt(1, pc.getObjectId());
218+ save_items.setInt(2, i.getObjectId());
219+ save_items.setLong(3, i.getCount());
220+ save_items.setLong(4, i.getPrice());
221+ save_items.addBatch();
222+ }
223+ break;
224+ }
225+ case MANUFACTURE:
226+ {
227+ if (!Config.OFFLINE_CRAFT_ENABLE)
228+ continue;
229+
230+ save_offline_status.setString(4, pc.getCreateList().getStoreName());
231+ for (final ManufactureItem i : pc.getCreateList().getList())
232+ {
233+ save_items.setInt(1, pc.getObjectId());
234+ save_items.setInt(2, i.getId());
235+ save_items.setLong(3, 0);
236+ save_items.setLong(4, i.getValue());
237+ save_items.addBatch();
238+ }
239+ break;
240+ }
241+ }
242+ save_items.executeBatch();
243+ save_offline_status.execute();
244+ }
245+ }
246+ catch (final Exception e)
247+ {
248+ LOGGER.log(Level.WARNING, OfflineTradersTable.class.getSimpleName() + ": error while saving offline trader " + pc.getObjectId() + ".", e);
249+ }
250+ }
251+
252+ LOGGER.info(OfflineTradersTable.class.getSimpleName() + ": offline traders stored.");
253+ }
254+ catch (final SQLException e)
255+ {
256+ LOGGER.log(Level.WARNING, OfflineTradersTable.class.getSimpleName() + ": error while saving offline traders.", e);
257+ }
258+ }
259+
260+ public static void restoreOfflineTraders()
261+ {
262+ LOGGER.info(OfflineTradersTable.class.getSimpleName() + ": loading offline traders...");
263+
264+ try (final Connection con = L2DatabaseFactory.getInstance().getConnection();
265+ final Statement stm = con.createStatement();
266+ final ResultSet rs = stm.executeQuery(LOAD_OFFLINE_STATUS))
267+ {
268+ int nTraders = 0;
269+ while (rs.next())
270+ {
271+ final long time = rs.getLong("time");
272+ if (Config.OFFLINE_MAX_DAYS > 0)
273+ {
274+ final Calendar cal = Calendar.getInstance();
275+ cal.setTimeInMillis(time);
276+ cal.add(Calendar.DAY_OF_YEAR, Config.OFFLINE_MAX_DAYS);
277+ if (cal.getTimeInMillis() <= System.currentTimeMillis())
278+ continue;
279+ }
280+
281+ StoreType type = null;
282+ for (final StoreType t : StoreType.values())
283+ {
284+ if (t.getId() == rs.getInt("type"))
285+ {
286+ type = t;
287+ break;
288+ }
289+ }
290+
291+ if (type == null)
292+ {
293+ LOGGER.warning(OfflineTradersTable.class.getSimpleName() + ": PrivateStoreType with id " + rs.getInt("type") + " could not be found.");
294+ continue;
295+ }
296+
297+ if (type == StoreType.NONE)
298+ continue;
299+
300+ final Player player = Player.restore(rs.getInt("charId"));
301+ if (player == null)
302+ continue;
303+
304+ try (final PreparedStatement stm_items = con.prepareStatement(LOAD_OFFLINE_ITEMS))
305+ {
306+ player.setRunning();
307+ player.sitDown();
308+ player.setOnlineStatus(true, false);
309+
310+ World.getInstance().addPlayer(player);
311+
312+ final L2GameClient client = new L2GameClient(null);
313+ client.setDetached(true);
314+ player.setClient(client);
315+ client.setActiveChar(player);
316+ client.setAccountName(player.getAccountNamePlayer());
317+ player.setOnlineStatus(true, true);
318+ client.setState(GameClientState.IN_GAME);
319+ player.setOfflineStartTime(time);
320+ player.spawnMe();
321+
322+ LoginServerThread.getInstance().addClient(player.getAccountName(), client);
323+
324+ stm_items.setInt(1, player.getObjectId());
325+ try (final ResultSet items = stm_items.executeQuery())
326+ {
327+ switch (type)
328+ {
329+ case BUY:
330+ {
331+ while (items.next())
332+ if (player.getBuyList().addItemByItemId(items.getInt(2), items.getInt(3), items.getInt(4)) == null)
333+ throw new NullPointerException("NPE at BUY of offlineShop " + player.getObjectId() + " " + items.getInt(2) + " " + items.getInt(3) + " " + items.getInt(4));
334+
335+ player.getBuyList().setTitle(rs.getString("title"));
336+ break;
337+ }
338+ case SELL:
339+ case PACKAGE_SELL:
340+ {
341+ while (items.next())
342+ if (player.getSellList().addItem(items.getInt(2), items.getInt(3), items.getInt(4)) == null)
343+ throw new NullPointerException("NPE at SELL of offlineShop " + player.getObjectId() + " " + items.getInt(2) + " " + items.getInt(3) + " " + items.getInt(4));
344+
345+ player.getSellList().setTitle(rs.getString("title"));
346+ player.getSellList().setPackaged(type == StoreType.PACKAGE_SELL);
347+ break;
348+ }
349+ case MANUFACTURE:
350+ {
351+ final ManufactureList createList = new ManufactureList();
352+ createList.setStoreName(rs.getString("title"));
353+ while (items.next())
354+ createList.add(new ManufactureItem(items.getInt(2), items.getInt(4)));
355+
356+ player.setCreateList(createList);
357+ break;
358+ }
359+ }
360+ }
361+
362+ player.setStoreType(type);
363+ player.startAbnormalEffect(AbnormalEffect.SLEEP);
364+ player.broadcastUserInfo();
365+
366+ nTraders++;
367+ }
368+ catch (final Exception e)
369+ {
370+ LOGGER.log(Level.WARNING, OfflineTradersTable.class.getSimpleName() + ": error loading trader " + player.getObjectId() + ".", e);
371+ player.logout(false);
372+ }
373+ }
374+
375+ LOGGER.info(OfflineTradersTable.class.getSimpleName() + ": loaded " + nTraders + " offline traders.");
376+
377+ try (final Statement stm1 = con.createStatement())
378+ {
379+ stm1.execute(CLEAR_OFFLINE_TABLE);
380+ stm1.execute(CLEAR_OFFLINE_TABLE_ITEMS);
381+ }
382+ }
383+ catch (final SQLException e)
384+ {
385+ LOGGER.log(Level.WARNING, OfflineTradersTable.class.getSimpleName() + ": error while loading offline traders.", e);
386+ }
387+ }
388+
389+ public static boolean offlineMode(final Player player)
390+ {
391+ if (player.isInOlympiadMode() || player.isFestivalParticipant() || player.isInJail() || player.getBoat() != null)
392+ return false;
393+
394+ if (Config.OFFLINE_MODE_IN_PEACE_ZONE && !player.isInsideZone(ZoneId.PEACE))
395+ return false;
396+
397+ switch (player.getStoreType())
398+ {
399+ case SELL:
400+ case PACKAGE_SELL:
401+ case BUY:
402+ return Config.OFFLINE_TRADE_ENABLE;
403+ case MANUFACTURE:
404+ return Config.OFFLINE_CRAFT_ENABLE;
405+ }
406+ return false;
407+ }
408+}
409\ No newline at end of file
410Index: java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminEditChar.java
411===================================================================
412--- java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminEditChar.java (revision 1002)
413+++ java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminEditChar.java (working copy)
414@@ -710,6 +710,19 @@
415 else
416 activeChar.setTarget(player);
417
418+ final L2GameClient client = activeChar.getClient();
419+ if (client == null)
420+ {
421+ activeChar.sendMessage("Client is null.");
422+ return;
423+ }
424+
425+ if (client.isDetached())
426+ {
427+ activeChar.sendMessage("Client is detached.");
428+ return;
429+ }
430+
431 gatherCharacterInfo(activeChar, player, "charinfo.htm");
432 }
433
434Index: java/net/sf/l2j/gameserver/LoginServerThread.java
435===================================================================
436--- java/net/sf/l2j/gameserver/LoginServerThread.java (revision 1002)
437+++ java/net/sf/l2j/gameserver/LoginServerThread.java (working copy)
438@@ -295,6 +295,9 @@
439 final L2GameClient existingClient = _clients.putIfAbsent(account, client);
440 if (existingClient == null)
441 {
442+ if (client.isDetached())
443+ return;
444+
445 try
446 {
447 sendPacket(new PlayerAuthRequest(client.getAccountName(), client.getSessionId()));
448Index: java/net/sf/l2j/gameserver/model/actor/instance/Player.java
449===================================================================
450--- java/net/sf/l2j/gameserver/model/actor/instance/Player.java (revision 1002)
451+++ java/net/sf/l2j/gameserver/model/actor/instance/Player.java (working copy)
452@@ -2738,9 +2738,17 @@
453
454 public String getAccountName()
455 {
456+ if (getClient() == null)
457+ return getAccountNamePlayer();
458+
459 return _client.getAccountName();
460 }
461
462+ public String getAccountNamePlayer()
463+ {
464+ return _accountName;
465+ }
466+
467 public Map<Integer, String> getAccountChars()
468 {
469 return _chars;
470@@ -4236,6 +4244,9 @@
471 public void setStoreType(StoreType type)
472 {
473 _storeType = type;
474+
475+ if (Config.OFFLINE_DISCONNECT_FINISHED && type == StoreType.NONE && (getClient() == null || getClient().isDetached()))
476+ deleteMe();
477 }
478
479 /**
480@@ -10127,4 +10138,17 @@
481 }
482 }
483 }
484+
485+ /** Offline Shop */
486+ private long _offlineShopStart;
487+
488+ public long getOfflineStartTime()
489+ {
490+ return _offlineShopStart;
491+ }
492+
493+ public void setOfflineStartTime(long time)
494+ {
495+ _offlineShopStart = time;
496+ }
497 }
498\ No newline at end of file
499Index: java/net/sf/l2j/gameserver/network/L2GameClient.java
500===================================================================
501--- java/net/sf/l2j/gameserver/network/L2GameClient.java (revision 1002)
502+++ java/net/sf/l2j/gameserver/network/L2GameClient.java (working copy)
503@@ -22,14 +22,18 @@
504 import net.sf.l2j.L2DatabaseFactory;
505 import net.sf.l2j.gameserver.LoginServerThread;
506 import net.sf.l2j.gameserver.data.sql.ClanTable;
507+import net.sf.l2j.gameserver.data.sql.OfflineTradersTable;
508 import net.sf.l2j.gameserver.data.sql.PlayerInfoTable;
509 import net.sf.l2j.gameserver.model.CharSelectSlot;
510 import net.sf.l2j.gameserver.model.World;
511 import net.sf.l2j.gameserver.model.actor.instance.Player;
512+import net.sf.l2j.gameserver.model.group.Party.MessageType;
513+import net.sf.l2j.gameserver.model.olympiad.OlympiadManager;
514 import net.sf.l2j.gameserver.model.pledge.Clan;
515 import net.sf.l2j.gameserver.network.serverpackets.ActionFailed;
516 import net.sf.l2j.gameserver.network.serverpackets.L2GameServerPacket;
517 import net.sf.l2j.gameserver.network.serverpackets.ServerClose;
518+import net.sf.l2j.gameserver.skills.AbnormalEffect;
519
520 /**
521 * Represents a client connected on Game Server
522@@ -447,6 +451,9 @@
523
524 public void close(L2GameServerPacket gsp)
525 {
526+ if (getConnection() == null)
527+ return;
528+
529 getConnection().close(gsp);
530 }
531
532@@ -543,6 +550,30 @@
533 if (getActiveChar() != null && !isDetached())
534 {
535 setDetached(true);
536+ if (OfflineTradersTable.offlineMode(getActiveChar()))
537+ {
538+ if (getActiveChar().getParty() != null)
539+ getActiveChar().getParty().removePartyMember(getActiveChar(), MessageType.DISCONNECTED);
540+
541+ OlympiadManager.getInstance().unRegisterNoble(getActiveChar());
542+
543+ if (getActiveChar().getPet() != null)
544+ {
545+ getActiveChar().getPet().doRevive();
546+ getActiveChar().getPet().unSummon(getActiveChar());
547+ }
548+
549+ if (Config.OFFLINE_SET_NAME_COLOR)
550+ {
551+ getActiveChar().broadcastUserInfo();
552+ getActiveChar().startAbnormalEffect(AbnormalEffect.SLEEP);
553+ }
554+
555+ if (getActiveChar().getOfflineStartTime() == 0)
556+ getActiveChar().setOfflineStartTime(System.currentTimeMillis());
557+
558+ return;
559+ }
560 fast = !getActiveChar().isInCombat() && !getActiveChar().isLocked();
561 }
562 cleanMe(fast);
563Index: java/net/sf/l2j/gameserver/Shutdown.java
564===================================================================
565--- java/net/sf/l2j/gameserver/Shutdown.java (revision 1002)
566+++ java/net/sf/l2j/gameserver/Shutdown.java (working copy)
567@@ -11,6 +11,7 @@
568 import net.sf.l2j.gameserver.data.manager.CoupleManager;
569 import net.sf.l2j.gameserver.data.manager.FishingChampionshipManager;
570 import net.sf.l2j.gameserver.data.manager.ZoneManager;
571+import net.sf.l2j.gameserver.data.sql.OfflineTradersTable;
572 import net.sf.l2j.gameserver.data.sql.ServerMemoTable;
573 import net.sf.l2j.gameserver.instancemanager.FourSepulchersManager;
574 import net.sf.l2j.gameserver.instancemanager.GrandBossManager;
575@@ -79,6 +80,16 @@
576 {
577 StringUtil.printSection("Under " + MODE_TEXT[_shutdownMode] + " process");
578
579+ // offline shop
580+ try
581+ {
582+ if ((Config.OFFLINE_TRADE_ENABLE || Config.OFFLINE_CRAFT_ENABLE) && Config.RESTORE_OFFLINERS)
583+ OfflineTradersTable.storeOffliners();
584+ }
585+ catch (Throwable t)
586+ {
587+ }
588+
589 // disconnect players
590 try
591 {
592Index: config/custom.properties
593===================================================================
594--- config/custom.properties (nonexistent)
595+++ config/custom.properties (working copy)
596@@ -0,0 +1,39 @@
597+# =================================================================
598+# Offline trade/craft
599+# =================================================================
600+
601+# Option to enable or disable offline trade feature.
602+# Default: False
603+OfflineTradeEnable = False
604+
605+# Option to enable or disable offline craft feature.
606+# Default: False
607+OfflineCraftEnable = False
608+
609+# If set to True, offline shops will be possible only in peace zones.
610+# Default: False
611+OfflineModeInPeaceZone = True
612+
613+# If set to True, players in offline shop mode will not take any damage, thus they cannot be killed.
614+# Default: False
615+OfflineModeNoDamage = False
616+
617+# If set to True, name color will be changed when entering offline mode.
618+OfflineSetNameColor = True
619+
620+# Color of the name in offline mode.
621+OfflineNameColor = 808080
622+
623+# Restore offline traders/crafters after restart/shutdown.
624+# Default: False
625+RestoreOffliners = True
626+
627+# Do not restore offline characters, after OfflineMaxDays days spent from first restore.
628+# Require server restart to disconnect expired shops.
629+# 0 = disabled (always restore).
630+# Default: 7
631+OfflineMaxDays = 7
632+
633+# Disconnect shop after finished selling, buying.
634+# Default: True
635+OfflineDisconnectFinished = True
636#P aCis_datapack
637Index: sql/character_offline_trade_items.sql
638===================================================================
639--- sql/character_offline_trade_items.sql (nonexistent)
640+++ sql/character_offline_trade_items.sql (working copy)
641@@ -0,0 +1,8 @@
642+CREATE TABLE IF NOT EXISTS `character_offline_trade_items` (
643+ `charId` int(10) unsigned NOT NULL,
644+ `item` int(10) unsigned NOT NULL DEFAULT '0',
645+ `count` bigint(20) unsigned NOT NULL DEFAULT '0',
646+ `price` bigint(20) unsigned NOT NULL DEFAULT '0',
647+ KEY `charId` (`charId`),
648+ KEY `item` (`item`)
649+);
650Index: sql/character_offline_trade.sql
651===================================================================
652--- sql/character_offline_trade.sql (nonexistent)
653+++ sql/character_offline_trade.sql (working copy)
654@@ -0,0 +1,7 @@
655+CREATE TABLE IF NOT EXISTS `character_offline_trade` (
656+ `charId` int(10) unsigned NOT NULL,
657+ `time` bigint(13) unsigned NOT NULL DEFAULT '0',
658+ `type` tinyint(4) NOT NULL DEFAULT '0',
659+ `title` varchar(50) DEFAULT NULL,
660+ PRIMARY KEY (`charId`)
661+);
662Index: tools/database_installer.sh
663===================================================================
664--- tools/database_installer.sh (revision 1001)
665+++ tools/database_installer.sh (working copy)
666@@ -91,6 +91,8 @@
667 $MYG < ../sql/character_macroses.sql &> /dev/null
668 $MYG < ../sql/character_mail.sql &> /dev/null
669 $MYG < ../sql/character_memo.sql &> /dev/null
670+$MYG < ../sql/character_offline_trade_items.sql &> /dev/null
671+$MYG < ../sql/character_offline_trade.sql &> /dev/null
672 $MYG < ../sql/character_quests.sql &> /dev/null
673 $MYG < ../sql/character_raid_points.sql &> /dev/null
674 $MYG < ../sql/character_recipebook.sql &> /dev/null
675Index: tools/database_installer.bat
676===================================================================
677--- tools/database_installer.bat (revision 1001)
678+++ tools/database_installer.bat (working copy)
679@@ -71,6 +71,8 @@
680 %mysqlPath% -h %gshost% -u %gsuser% --password=%gspass% -D %gsdb% < ../sql/character_macroses.sql
681 %mysqlPath% -h %gshost% -u %gsuser% --password=%gspass% -D %gsdb% < ../sql/character_mail.sql
682 %mysqlPath% -h %gshost% -u %gsuser% --password=%gspass% -D %gsdb% < ../sql/character_memo.sql
683+%mysqlPath% -h %gshost% -u %gsuser% --password=%gspass% -D %gsdb% < ../sql/character_offline_trade_items.sql
684+%mysqlPath% -h %gshost% -u %gsuser% --password=%gspass% -D %gsdb% < ../sql/character_offline_trade.sql
685 %mysqlPath% -h %gshost% -u %gsuser% --password=%gspass% -D %gsdb% < ../sql/character_quests.sql
686 %mysqlPath% -h %gshost% -u %gsuser% --password=%gspass% -D %gsdb% < ../sql/character_raid_points.sql
687 %mysqlPath% -h %gshost% -u %gsuser% --password=%gspass% -D %gsdb% < ../sql/character_recipebook.sql
688Index: tools/full_install.sql
689===================================================================
690--- tools/full_install.sql (revision 1001)
691+++ tools/full_install.sql (working copy)
692@@ -15,6 +15,8 @@
693 DROP TABLE IF EXISTS character_macroses;
694 DROP TABLE IF EXISTS character_mail;
695 DROP TABLE IF EXISTS character_memo;
696+DROP TABLE IF EXISTS character_offline_trade_items;
697+DROP TABLE IF EXISTS character_offline_trade;
698 DROP TABLE IF EXISTS character_quests;
699 DROP TABLE IF EXISTS character_raid_points;
700 DROP TABLE IF EXISTS character_recipebook;