· 4 years ago · Aug 23, 2021, 03:20 AM
1diff --git a/java/net/sf/l2j/Config.java b/java/net/sf/l2j/Config.java
2index 4b7181f..68f70b7 100644
3--- a/java/net/sf/l2j/Config.java
4+++ b/java/net/sf/l2j/Config.java
5@@ -35,6 +35,7 @@
6 public static final String PLAYERS_FILE = "./config/players.properties";
7 public static final String SERVER_FILE = "./config/server.properties";
8 public static final String SIEGE_FILE = "./config/siege.properties";
9+ public static final String OFFLINEMOD = "./config/CustomMods/OfflineShop.ini";
10
11 // --------------------------------------------------
12 // Clans settings
13@@ -380,7 +381,15 @@
14 public static int ENCHANT_MAX_ARMOR;
15 public static int ENCHANT_SAFE_MAX;
16 public static int ENCHANT_SAFE_MAX_FULL;
17-
18+ /** Offline Shop */
19+ public static boolean OFFLINE_TRADE_ENABLE;
20+ public static boolean OFFLINE_CRAFT_ENABLE;
21+ public static boolean OFFLINE_MODE_IN_PEACE_ZONE;
22+ public static boolean OFFLINE_MODE_NO_DAMAGE;
23+ public static boolean RESTORE_OFFLINERS;
24+ public static int OFFLINE_MAX_DAYS;
25+ public static boolean OFFLINE_DISCONNECT_FINISHED;
26+ public static boolean OFFLINE_SET_SLEEP;
27 /** Augmentations */
28 public static int AUGMENTATION_NG_SKILL_CHANCE;
29 public static int AUGMENTATION_NG_GLOW_CHANCE;
30@@ -1088,6 +1097,19 @@
31 STORE_SKILL_COOLTIME = players.getProperty("StoreSkillCooltime", true);
32 }
33
34+ private static final void loadOff()
35+ {
36+ final ExProperties offlineshop = initProperties(Config.OFFLINEMOD);
37+ OFFLINE_TRADE_ENABLE = offlineshop.getProperty("OfflineTradeEnable", false);
38+ OFFLINE_CRAFT_ENABLE = offlineshop.getProperty("OfflineCraftEnable", false);
39+ OFFLINE_MODE_IN_PEACE_ZONE = offlineshop.getProperty("OfflineModeInPeaceZone", false);
40+ OFFLINE_MODE_NO_DAMAGE = offlineshop.getProperty("OfflineModeNoDamage", false);
41+ OFFLINE_SET_SLEEP = offlineshop.getProperty("OfflineSetSleepEffect", false);
42+ RESTORE_OFFLINERS = offlineshop.getProperty("RestoreOffliners", false);
43+ OFFLINE_MAX_DAYS = offlineshop.getProperty("OfflineMaxDays", 10);
44+ OFFLINE_DISCONNECT_FINISHED = offlineshop.getProperty("OfflineDisconnectFinished", true);
45+
46+ }
47 /**
48 * Loads siege settings.
49 */
50@@ -1307,6 +1329,9 @@
51 // siege settings
52 loadSieges();
53
54+ // OffShop settings
55+ loadOff();
56+
57 // server settings
58 loadServer();
59 }
60diff --git a/java/net/sf/l2j/gameserver/GameServer.java b/java/net/sf/l2j/gameserver/GameServer.java
61index 41a9f92..402a66a 100644
62--- a/java/net/sf/l2j/gameserver/GameServer.java
63+++ b/java/net/sf/l2j/gameserver/GameServer.java
64@@ -71,6 +71,7 @@
65 import net.sf.l2j.gameserver.data.xml.SummonItemData;
66 import net.sf.l2j.gameserver.data.xml.TeleportLocationData;
67 import net.sf.l2j.gameserver.data.xml.WalkerRouteData;
68+import net.sf.l2j.gameserver.enums.actors.OfflineStoresData;
69 import net.sf.l2j.gameserver.geoengine.GeoEngine;
70 import net.sf.l2j.gameserver.handler.AdminCommandHandler;
71 import net.sf.l2j.gameserver.handler.ChatHandler;
72@@ -243,6 +244,10 @@
73 Olympiad.getInstance();
74 HeroManager.getInstance();
75
76+ StringUtil.printSection("OfflineShop Started");
77+ if ((Config.OFFLINE_TRADE_ENABLE || Config.OFFLINE_CRAFT_ENABLE) && Config.RESTORE_OFFLINERS)
78+ OfflineStoresData.getInstance().restoreOfflineTraders();
79+
80 StringUtil.printSection("Four Sepulchers");
81 FourSepulchersManager.getInstance();
82
83diff --git a/java/net/sf/l2j/gameserver/LoginServerThread.java b/java/net/sf/l2j/gameserver/LoginServerThread.java
84index 477d0af..0eb4fd2 100644
85--- a/java/net/sf/l2j/gameserver/LoginServerThread.java
86+++ b/java/net/sf/l2j/gameserver/LoginServerThread.java
87@@ -295,6 +295,8 @@
88 public void addClient(String account, GameClient client)
89 {
90 final GameClient existingClient = _clients.putIfAbsent(account, client);
91+ if (client.isDetached())
92+ return;
93 if (existingClient == null)
94 {
95 try
96diff --git a/java/net/sf/l2j/gameserver/Shutdown.java b/java/net/sf/l2j/gameserver/Shutdown.java
97index 88a3dc4..f90b31e 100644
98--- a/java/net/sf/l2j/gameserver/Shutdown.java
99+++ b/java/net/sf/l2j/gameserver/Shutdown.java
100@@ -18,6 +18,7 @@
101 import net.sf.l2j.gameserver.data.manager.SevenSignsManager;
102 import net.sf.l2j.gameserver.data.manager.ZoneManager;
103 import net.sf.l2j.gameserver.data.sql.ServerMemoTable;
104+import net.sf.l2j.gameserver.enums.actors.OfflineStoresData;
105 import net.sf.l2j.gameserver.model.World;
106 import net.sf.l2j.gameserver.model.actor.Player;
107 import net.sf.l2j.gameserver.model.olympiad.Olympiad;
108@@ -77,7 +78,18 @@
109 if (this == SingletonHolder.INSTANCE)
110 {
111 StringUtil.printSection("Under " + MODE_TEXT[_shutdownMode] + " process");
112-
113+ try
114+ {
115+ if ((Config.OFFLINE_TRADE_ENABLE || Config.OFFLINE_CRAFT_ENABLE) && Config.RESTORE_OFFLINERS)
116+ {
117+ OfflineStoresData.getInstance().storeOffliners();
118+ LOGGER.info("Offline Traders Table: Offline shops stored.");
119+ }
120+ }
121+ catch (Throwable t)
122+ {
123+
124+ }
125 // disconnect players
126 try
127 {
128diff --git a/java/net/sf/l2j/gameserver/enums/actors/OfflineStoresData.java b/java/net/sf/l2j/gameserver/enums/actors/OfflineStoresData.java
129new file mode 100644
130index 0000000..47a48b3
131--- /dev/null
132+++ b/java/net/sf/l2j/gameserver/enums/actors/OfflineStoresData.java
133@@ -0,0 +1,276 @@
134+package net.sf.l2j.gameserver.enums.actors;
135+
136+import java.sql.Connection;
137+import java.sql.PreparedStatement;
138+import java.sql.ResultSet;
139+import java.sql.Statement;
140+import java.util.Calendar;
141+import java.util.logging.Level;
142+import java.util.logging.Logger;
143+
144+
145+import net.sf.l2j.Config;
146+import net.sf.l2j.L2DatabaseFactory;
147+import net.sf.l2j.gameserver.LoginServerThread;
148+import net.sf.l2j.gameserver.enums.ZoneId;
149+import net.sf.l2j.gameserver.model.World;
150+import net.sf.l2j.gameserver.model.actor.Player;
151+import net.sf.l2j.gameserver.model.craft.ManufactureItem;
152+import net.sf.l2j.gameserver.model.craft.ManufactureList;
153+import net.sf.l2j.gameserver.model.tradelist.TradeItem;
154+import net.sf.l2j.gameserver.network.GameClient;
155+import net.sf.l2j.gameserver.network.GameClient.GameClientState;
156+
157+public class OfflineStoresData
158+{
159+ private static final Logger LOGGER = Logger.getLogger(OfflineStoresData.class.getName());
160+
161+ // SQL DEFINITIONS
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`,`enchant`) 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 void storeOffliners()
170+ {
171+ try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement save_offline_status = con.prepareStatement(SAVE_OFFLINE_STATUS); PreparedStatement save_items = con.prepareStatement(SAVE_ITEMS))
172+ {
173+ try (Statement stm = con.createStatement())
174+ {
175+ stm.execute(CLEAR_OFFLINE_TABLE);
176+ stm.execute(CLEAR_OFFLINE_TABLE_ITEMS);
177+ }
178+ for (Player pc : World.getInstance().getPlayers())
179+ {
180+ try
181+ {
182+ if (pc.getStoreType() != StoreType.NONE && (pc.getClient() == null || pc.getClient().isDetached()))
183+ {
184+ save_offline_status.setInt(1, pc.getObjectId());
185+ save_offline_status.setLong(2, pc.getOfflineStartTime());
186+ save_offline_status.setInt(3, pc.getStoreType().getId());
187+ switch (pc.getStoreType())
188+ {
189+ case BUY:
190+ if (!Config.OFFLINE_TRADE_ENABLE)
191+ continue;
192+
193+ save_offline_status.setString(4, pc.getBuyList().getTitle());
194+ for (TradeItem i : pc.getBuyList().getItems())
195+ {
196+ save_items.setInt(1, pc.getObjectId());
197+ save_items.setInt(2, i.getItem().getItemId());
198+ save_items.setLong(3, i.getCount());
199+ save_items.setLong(4, i.getPrice());
200+ save_items.setLong(5, i.getEnchant());
201+ save_items.addBatch();
202+ }
203+ break;
204+ case SELL:
205+ case PACKAGE_SELL:
206+ if (!Config.OFFLINE_TRADE_ENABLE)
207+ continue;
208+
209+ save_offline_status.setString(4, pc.getSellList().getTitle());
210+ pc.getSellList().updateItems();
211+ for (TradeItem i : pc.getSellList().getItems())
212+ {
213+ save_items.setInt(1, pc.getObjectId());
214+ save_items.setInt(2, i.getObjectId());
215+ save_items.setLong(3, i.getCount());
216+ save_items.setLong(4, i.getPrice());
217+ save_items.setLong(5, i.getEnchant());
218+ save_items.addBatch();
219+ }
220+ break;
221+ case MANUFACTURE:
222+ if (!Config.OFFLINE_CRAFT_ENABLE)
223+ continue;
224+
225+ save_offline_status.setString(4, pc.getCreateList().getStoreName());
226+ for (ManufactureItem i : pc.getCreateList().getList())
227+ {
228+ save_items.setInt(1, pc.getObjectId());
229+ save_items.setInt(2, i.getId());
230+ save_items.setLong(3, 0L);
231+ save_items.setLong(4, i.getValue());
232+ save_items.setLong(5, 0L);
233+ save_items.addBatch();
234+ }
235+ break;
236+ }
237+ save_items.executeBatch();
238+ save_offline_status.execute();
239+ }
240+ }
241+ catch (Exception e)
242+ {
243+ LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Error while saving offline trader: " + pc.getObjectId() + " " + e, e);
244+ }
245+ }
246+
247+ LOGGER.info(getClass().getSimpleName() + ": Offline traders stored.");
248+ }
249+ catch (Exception e)
250+ {
251+ LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Error while saving offline traders: " + e, e);
252+ }
253+ }
254+
255+ public void restoreOfflineTraders()
256+ {
257+ LOGGER.info(getClass().getSimpleName() + ": Loading offline traders...");
258+ try (Connection con = L2DatabaseFactory.getInstance().getConnection(); Statement stm = con.createStatement(); ResultSet rs = stm.executeQuery(LOAD_OFFLINE_STATUS))
259+ {
260+ int nTraders = 0;
261+ while (rs.next())
262+ {
263+ final long time = rs.getLong("time");
264+ if (Config.OFFLINE_MAX_DAYS > 0)
265+ {
266+ final Calendar cal = Calendar.getInstance();
267+ cal.setTimeInMillis(time);
268+ cal.add(Calendar.DAY_OF_YEAR, Config.OFFLINE_MAX_DAYS);
269+ if (cal.getTimeInMillis() <= System.currentTimeMillis())
270+ continue;
271+ }
272+
273+ StoreType type = null;
274+ for (StoreType t : StoreType.values())
275+ {
276+ if (t.getId() == rs.getInt("type"))
277+ {
278+ type = t;
279+ break;
280+ }
281+ }
282+ if (type == null)
283+ {
284+ LOGGER.warning(getClass().getSimpleName() + ": PrivateStoreType with id " + rs.getInt("type") + " could not be found.");
285+ continue;
286+ }
287+ if (type == StoreType.NONE)
288+ continue;
289+
290+ final Player player = Player.restore(rs.getInt("charId"));
291+ if (player == null)
292+ continue;
293+
294+ try (PreparedStatement stm_items = con.prepareStatement(LOAD_OFFLINE_ITEMS))
295+ {
296+ player.isRunning();
297+ player.sitDown();
298+ player.setOnlineStatus(true, false);
299+
300+ World.getInstance().addPlayer(player);
301+
302+ final GameClient client = new GameClient(null);
303+ client.setDetached(true);
304+ player.setClient(client);
305+ client.setPlayer(player);
306+ client.setAccountName(player.getAccountNamePlayer());
307+ player.setOnlineStatus(true, true);
308+ client.setState(GameClientState.IN_GAME);
309+ player.setOfflineStartTime(time);
310+ player.spawnMe();
311+
312+ LoginServerThread.getInstance().addClient(player.getAccountName(), client);
313+
314+ stm_items.setInt(1, player.getObjectId());
315+ try (ResultSet items = stm_items.executeQuery())
316+ {
317+ switch (type)
318+ {
319+ case BUY:
320+ while (items.next())
321+ {
322+ player.getBuyList().addItemByItemId(items.getInt(2), items.getInt(3), items.getInt(4));
323+ }
324+
325+ player.getBuyList().setTitle(rs.getString("title"));
326+ break;
327+ case SELL:
328+ case PACKAGE_SELL:
329+ while (items.next())
330+ if (player.getSellList().addItem(items.getInt(2), items.getInt(3), items.getInt(4)) == null)
331+ throw new NullPointerException("NPE at SELL of offlineShop " + player.getObjectId() + " " + items.getInt(2) + " " + items.getInt(3) + " " + items.getInt(4));
332+
333+ player.getSellList().setTitle(rs.getString("title"));
334+ player.getSellList().setPackaged(type == StoreType.PACKAGE_SELL);
335+ break;
336+ case MANUFACTURE:
337+ final ManufactureList createList = new ManufactureList();
338+ createList.setStoreName(rs.getString("title"));
339+ while (items.next())
340+ createList.add(new ManufactureItem(items.getInt(2), items.getInt(4)));
341+ //player.setCreateList(createList);
342+ break;
343+ }
344+ }
345+
346+ if (Config.OFFLINE_SET_SLEEP)
347+ player.startAbnormalEffect(0x000080);
348+
349+ player.setStoreType(type);
350+ player.restoreEffects();
351+ player.broadcastUserInfo();
352+
353+ nTraders++;
354+ }
355+ catch (Exception e)
356+ {
357+ LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Error loading trader: " + player, e);
358+
359+ player.deleteMe();
360+ }
361+ }
362+
363+ LOGGER.info(getClass().getSimpleName() + ": Loaded: " + nTraders + " offline trader(s)");
364+
365+ try (Statement stm1 = con.createStatement())
366+ {
367+ stm1.execute(CLEAR_OFFLINE_TABLE);
368+ stm1.execute(CLEAR_OFFLINE_TABLE_ITEMS);
369+ }
370+ }
371+ catch (Exception e)
372+ {
373+ LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Error while loading offline traders: ", e);
374+ }
375+ }
376+
377+ public static boolean offlineMode(Player player)
378+ {
379+ if (player.isInOlympiadMode() || player.isFestivalParticipant() || player.isInJail() || player.getBoat() != null)
380+ return false;
381+
382+ boolean canSetShop = false;
383+ switch (player.getStoreType())
384+ {
385+ case SELL:
386+ case PACKAGE_SELL:
387+ case BUY:
388+ canSetShop = Config.OFFLINE_TRADE_ENABLE;
389+ break;
390+ case MANUFACTURE:
391+ canSetShop = Config.OFFLINE_CRAFT_ENABLE;
392+ break;
393+ }
394+ if (Config.OFFLINE_MODE_IN_PEACE_ZONE && !player.isInsideZone(ZoneId.PEACE))
395+ canSetShop = false;
396+
397+ return canSetShop;
398+ }
399+
400+ public static OfflineStoresData getInstance()
401+ {
402+ return SingletonHolder._instance;
403+ }
404+
405+ private static class SingletonHolder
406+ {
407+ protected static final OfflineStoresData _instance = new OfflineStoresData();
408+ }
409+}
410\ No newline at end of file
411diff --git a/java/net/sf/l2j/gameserver/enums/actors/StoreType.java b/java/net/sf/l2j/gameserver/enums/actors/StoreType.java
412index b7da1c6..dca591f 100644
413--- a/java/net/sf/l2j/gameserver/enums/actors/StoreType.java
414+++ b/java/net/sf/l2j/gameserver/enums/actors/StoreType.java
415@@ -21,4 +21,13 @@
416 {
417 return _id;
418 }
419+ public static StoreType findById(int id)
420+ {
421+ for (StoreType privateStoreType : values())
422+ {
423+ if (privateStoreType.getId() == id)
424+ return privateStoreType;
425+ }
426+ return null;
427+ }
428 }
429\ No newline at end of file
430diff --git a/java/net/sf/l2j/gameserver/model/actor/Player.java b/java/net/sf/l2j/gameserver/model/actor/Player.java
431index 9436ef0..dad3cc4 100644
432--- a/java/net/sf/l2j/gameserver/model/actor/Player.java
433+++ b/java/net/sf/l2j/gameserver/model/actor/Player.java
434@@ -2607,9 +2607,14 @@
435
436 public String getAccountName()
437 {
438+ if (getClient() == null)
439+ return getAccountNamePlayer();
440 return _accountName;
441 }
442-
443+ public String getAccountNamePlayer()
444+ {
445+ return _accountName;
446+ }
447 public Map<Integer, String> getAccountChars()
448 {
449 return _chars;
450@@ -4113,6 +4118,8 @@
451 public void setStoreType(StoreType type)
452 {
453 _storeType = type;
454+ if (Config.OFFLINE_DISCONNECT_FINISHED && type == StoreType.NONE && (getClient() == null || getClient().isDetached()))
455+ deleteMe();
456 }
457
458 /**
459@@ -4122,7 +4129,16 @@
460 {
461 return _storeType;
462 }
463+ private long _offlineShopStart;
464+ public long getOfflineStartTime()
465+ {
466+ return _offlineShopStart;
467+ }
468
469+ public void setOfflineStartTime(long time)
470+ {
471+ _offlineShopStart = time;
472+ }
473 /**
474 * @return true if the {@link Player} can use dwarven recipes.
475 */
476diff --git a/java/net/sf/l2j/gameserver/network/GameClient.java b/java/net/sf/l2j/gameserver/network/GameClient.java
477index 5218e60..823728e 100644
478--- a/java/net/sf/l2j/gameserver/network/GameClient.java
479+++ b/java/net/sf/l2j/gameserver/network/GameClient.java
480@@ -21,9 +21,12 @@
481 import net.sf.l2j.gameserver.LoginServerThread;
482 import net.sf.l2j.gameserver.data.sql.ClanTable;
483 import net.sf.l2j.gameserver.data.sql.PlayerInfoTable;
484+import net.sf.l2j.gameserver.enums.MessageType;
485+import net.sf.l2j.gameserver.enums.ZoneId;
486 import net.sf.l2j.gameserver.model.CharSelectSlot;
487 import net.sf.l2j.gameserver.model.World;
488 import net.sf.l2j.gameserver.model.actor.Player;
489+import net.sf.l2j.gameserver.model.olympiad.OlympiadManager;
490 import net.sf.l2j.gameserver.model.pledge.Clan;
491 import net.sf.l2j.gameserver.network.serverpackets.ActionFailed;
492 import net.sf.l2j.gameserver.network.serverpackets.L2GameServerPacket;
493@@ -208,6 +211,29 @@
494 if (getPlayer() != null && !isDetached())
495 {
496 setDetached(true);
497+ if (offlineMode(getPlayer()))
498+ {
499+ if (getPlayer().getParty() != null)
500+ getPlayer().getParty().removePartyMember(getPlayer(), MessageType.EXPELLED);
501+ OlympiadManager.getInstance().unRegisterNoble(getPlayer());
502+
503+ // If the Character has Pet, unsummon it
504+ if (getPlayer().hasPet())
505+ {
506+ getPlayer().getSummon().unSummon(getPlayer());
507+ // Dead pet wasn't unsummoned, broadcast npcinfo changes (pet will be without owner name - means owner offline)
508+ if (getPlayer().getSummon() != null)
509+ getPlayer().getSummon().updateAndBroadcastStatus(0);
510+ }
511+
512+ if (Config.OFFLINE_SET_SLEEP)
513+ getPlayer().startAbnormalEffect(0x000080);
514+
515+ if (getPlayer().getOfflineStartTime() == 0)
516+ getPlayer().setOfflineStartTime(System.currentTimeMillis());
517+
518+ return;
519+ }
520 fast = !getPlayer().isInCombat() && !getPlayer().isLocked();
521 }
522 cleanMe(fast);
523@@ -218,6 +244,44 @@
524 }
525 }
526
527+ /**
528+ * @param player the player to be check.
529+ * @return {@code true} if the player is allowed to remain as off-line shop.
530+ */
531+ public static boolean offlineMode(Player player)
532+ {
533+ if (player.isInOlympiadMode() || player.isFestivalParticipant() || player.isInJail() || player.getBoat() != null)
534+ return false;
535+
536+ boolean canSetShop = false;
537+ switch (player.getStoreType())
538+ {
539+ case SELL:
540+ case PACKAGE_SELL:
541+ case BUY:
542+ {
543+ canSetShop = Config.OFFLINE_TRADE_ENABLE;
544+ break;
545+ }
546+ case MANUFACTURE:
547+ {
548+ canSetShop = Config.OFFLINE_TRADE_ENABLE;
549+ break;
550+ }
551+ default:
552+ {
553+ canSetShop = Config.OFFLINE_CRAFT_ENABLE && player.isCrafting();
554+ break;
555+ }
556+ }
557+
558+ if (Config.OFFLINE_MODE_IN_PEACE_ZONE && !player.isInsideZone(ZoneId.PEACE))
559+ canSetShop = false;
560+
561+ return canSetShop;
562+ }
563+
564+
565 @Override
566 protected void onForcedDisconnection()
567 {
568@@ -580,6 +644,8 @@
569
570 public void close(L2GameServerPacket gsp)
571 {
572+ if (getConnection() == null)
573+ return;
574 getConnection().close(gsp);
575 }
576
577
578Criar Tabela SQL
579
580+character_offline_trade_items
581+CREATE TABLE IF NOT EXISTS `character_offline_trade_items` (
582+ `charId` int(10) NOT NULL DEFAULT 0,
583+ `item` int(10) NOT NULL DEFAULT 0,
584+ `count` int(20) NOT NULL DEFAULT 0,
585+ `price` int(20) NOT NULL DEFAULT 0,
586+ `enchant` int(20) NOT NULL DEFAULT 0
587+);
588
589+character_offline_trade
590+CREATE TABLE IF NOT EXISTS `character_offline_trade` (
591+ `charId` int(11) NOT NULL,
592+ `time` bigint(20) unsigned NOT NULL DEFAULT 0,
593+ `type` tinyint(4) NOT NULL DEFAULT 0,
594+ `title` varchar(100) DEFAULT NULL,
595+ PRIMARY KEY (`charId`)
596+);
597
598TOOLS
599database_installer.bat
600%mysqlPath% -h %gshost% -u %gsuser% --password=%gspass% -D %gsdb% < ../sql/character_memo.sql
601+%mysqlPath% -h %gshost% -u %gsuser% --password=%gspass% -D %gsdb% < ../sql/character_offline_trade.sql
602+%mysqlPath% -h %gshost% -u %gsuser% --password=%gspass% -D %gsdb% < ../sql/character_offline_trade_items.sql
603%mysqlPath% -h %gshost% -u %gsuser% --password=%gspass% -D %gsdb% < ../sql/character_quests.sql
604
605database_installer.sh
606$MYG < ../sql/character_memo.sql &> /dev/null
607+$MYG < ../sql/character_offline_trade.sql &> /dev/null
608+$MYG < ../sql/character_offline_trade_items.sql &> /dev/null
609$MYG < ../sql/character_quests.sql &> /dev/null
610
611full_instal.sql
612DROP TABLE IF EXISTS character_memo;
613+DROP TABLE IF EXISTS character_offline_trade;
614+DROP TABLE IF EXISTS character_offline_trade_items;
615DROP TABLE IF EXISTS character_quests;
616
617
618Index: DataPack
619
620+# =================================================================
621+# Offline trade/craft
622+# =================================================================
623+
624+# Option to enable or disable offline trade feature.
625+# Enable -> true, Disable -> false
626+OfflineTradeEnable = True
627+
628+# Option to enable or disable offline craft feature.
629+# Enable -> true, Disable -> false
630+OfflineCraftEnable = True
631+
632+# If set to True, off-line shops will be possible only in peace zones.
633+# Default: False
634+OfflineModeInPeaceZone = True
635+
636+# If set to True, players in off-line shop mode will not take any damage, thus they cannot be killed.
637+# Default: False
638+OfflineModeNoDamage = False
639+
640+# Restore offline traders/crafters after restart/shutdown.
641+# Default: False
642+RestoreOffliners = True
643+
644+# Do not restore offline characters, after OfflineMaxDays days spent from first restore.
645+# Require server restart to disconnect expired shops.
646+# 0 = disabled (always restore).
647+# Default: 7
648+OfflineMaxDays = 7
649+
650+# Disconnect shop after finished selling, buying.
651+# Default: True
652+OfflineDisconnectFinished = True
653+
654+#Offline Effect Sleep
655+OfflineSetSleepEffect = True
656