· 8 years ago · Jan 15, 2018, 07:04 PM
1### Eclipse Workspace Patch 1.0
2#P aCis_gameserver
3Index: config/offlineshop.properties
4===================================================================
5--- config/offlineshop.properties (revision 0)
6+++ config/offlineshop.properties (working copy)
7@@ -0,0 +1,38 @@
8+# ---------------------------------------------------------------------------
9+# Offline trade/craft
10+# ---------------------------------------------------------------------------
11+
12+# Option to enable or disable offline trade feature.
13+# Enable -> true, Disable -> false
14+OfflineTradeEnable = False
15+
16+# Option to enable or disable offline craft feature.
17+# Enable -> true, Disable -> false
18+OfflineCraftEnable = False
19+
20+# If set to True, off-line shops will be possible only peace zones.
21+# Default: False
22+OfflineModeInPeaceZone = False
23+
24+# If set to True, players in off-line shop mode wont take any damage, thus they cannot be killed.
25+# Default: False
26+OfflineModeNoDamage = False
27+
28+# If set to True, name color will be changed then entering offline mode
29+OfflineSetNameColor = False
30+
31+# Color of the name in offline mode (if OfflineSetNameColor = True)
32+OfflineNameColor = 808080
33+
34+#Restore offline traders/crafters after restart/shutdown. Default: false.
35+RestoreOffliners = False
36+
37+#Do not restore offline characters, after OfflineMaxDays days spent from first restore.
38+#Require server restart to disconnect expired shops.
39+#0 = disabled (always restore).
40+#Default: 10
41+OfflineMaxDays = 10
42+
43+#Disconnect shop after finished selling, buying.
44+#Default: True
45+OfflineDisconnectFinished = True
46\ No newline at end of file
47Index: java/net/sf/l2j/gameserver/GameServer.java
48===================================================================
49--- java/net/sf/l2j/gameserver/GameServer.java (revision 371)
50+++ java/net/sf/l2j/gameserver/GameServer.java (working copy)
51@@ -56,6 +56,7 @@
52import net.sf.l2j.gameserver.datatables.MultisellData;
53import net.sf.l2j.gameserver.datatables.NpcTable;
54import net.sf.l2j.gameserver.datatables.NpcWalkerRoutesTable;
55+import net.sf.l2j.gameserver.datatables.OfflineTradersTable;
56import net.sf.l2j.gameserver.datatables.PetDataTable;
57import net.sf.l2j.gameserver.datatables.RecipeTable;
58import net.sf.l2j.gameserver.datatables.SkillTable;
59@@ -182,6 +183,9 @@
60MovieMakerManager.getInstance();
61PetitionManager.getInstance();
62
63+ if ((Config.OFFLINE_TRADE_ENABLE || Config.OFFLINE_CRAFT_ENABLE) && Config.RESTORE_OFFLINERS)
64+ OfflineTradersTable.getInstance().restoreOfflineTraders();
65+
66StringUtil.printSection("Characters");
67CharTemplateTable.getInstance();
68CharNameTable.getInstance();
69Index: java/net/sf/l2j/Config.java
70===================================================================
71--- java/net/sf/l2j/Config.java (revision 371)
72+++ java/net/sf/l2j/Config.java (working copy)
73@@ -47,6 +47,7 @@
74public static final String GEOENGINE_FILE = "./config/geoengine.properties";
75public static final String HEXID_FILE = "./config/hexid.txt";
76public static final String LOGIN_CONFIGURATION_FILE = "./config/loginserver.properties";
77public static final String NPCS_FILE = "./config/npcs.properties";
78public static final String PLAYERS_FILE = "./config/players.properties";
79public static final String SERVER_FILE = "./config/server.properties";
80+ public static final String OFFLINE_FILE = "./config/offlineshop.properties";
81+
82+ // --------------------------------------------------
83+ // Offline Shop && Craft
84+ // --------------------------------------------------
85+ public static boolean OFFLINE_TRADE_ENABLE;
86+ public static boolean OFFLINE_CRAFT_ENABLE;
87+ public static boolean OFFLINE_MODE_IN_PEACE_ZONE;
88+ public static boolean OFFLINE_MODE_NO_DAMAGE;
89+ public static boolean RESTORE_OFFLINERS;
90+ public static int OFFLINE_MAX_DAYS;
91+ public static boolean OFFLINE_DISCONNECT_FINISHED;
92+ public static boolean OFFLINE_SET_NAME_COLOR;
93+ public static int OFFLINE_NAME_COLOR;
94+
95+ // --------------------------------------------------
96// NPCs / Monsters
97// --------------------------------------------------
98
99@@ -867,6 +881,18 @@
100SERVER_ID = Integer.parseInt(hexid.getProperty("ServerID"));
101HEX_ID = new BigInteger(hexid.getProperty("HexID"), 16).toByteArray();
102
103+ /**
104+ * Loads Offline settings.
105+ */
106+ private static final void loadOffline()
107+ {
108+ final ExProperties offline = initProperties(OFFLINE_FILE);
109+ OFFLINE_TRADE_ENABLE = offline.getProperty("OfflineTradeEnable", false);
110+ OFFLINE_CRAFT_ENABLE = offline.getProperty("OfflineCraftEnable", false);
111+ OFFLINE_MODE_IN_PEACE_ZONE = offline.getProperty("OfflineModeInPeaceZone", false);
112+ OFFLINE_MODE_NO_DAMAGE = offline.getProperty("OfflineModeNoDamage", false);
113+ OFFLINE_SET_NAME_COLOR = offline.getProperty("OfflineSetNameColor", false);
114+ OFFLINE_NAME_COLOR = Integer.decode("0x" + offline.getProperty("OfflineNameColor", 808080));
115+ RESTORE_OFFLINERS = offline.getProperty("RestoreOffliners", false);
116+ OFFLINE_MAX_DAYS = offline.getProperty("OfflineMaxDays", 10);
117+ OFFLINE_DISCONNECT_FINISHED = offline.getProperty("OfflineDisconnectFinished", true);
118+ }
119+
120
121// server settings
122loadServer();
123+
124+ // Offline settings
125+ loadOffline();
126
127// NPCs / Monsters
128ExProperties npcs = load(NPCS_FILE);
129CHAMPION_FREQUENCY = npcs.getProperty("ChampionFrequency", 0);
130Index: java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminEditChar.java
131===================================================================
132--- java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminEditChar.java (revision 371)
133+++ java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminEditChar.java (working copy)
134@@ -628,7 +628,13 @@
135final String clientInfo = player.getClient().toString();
136final String account = clientInfo.substring(clientInfo.indexOf("Account: ") + 9, clientInfo.indexOf(" - IP: "));
137final String ip = clientInfo.substring(clientInfo.indexOf(" - IP: ") + 7, clientInfo.lastIndexOf("]"));
138+ final L2GameClient client = player.getClient();
139
140+ if (client == null)
141+ activeChar.sendMessage("Client is null.");
142+ else if (client.isDetached())
143+ activeChar.sendMessage("Client is detached.");
144+
145final NpcHtmlMessage html = new NpcHtmlMessage(0);
146html.setFile("data/html/admin/" + filename);
147html.replace("%name%", player.getName());
148Index: java/net/sf/l2j/gameserver/model/actor/instance/L2PcInstance.java
149===================================================================
150--- java/net/sf/l2j/gameserver/model/actor/instance/L2PcInstance.java (revision 371)
151+++ java/net/sf/l2j/gameserver/model/actor/instance/L2PcInstance.java (working copy)
152@@ -271,6 +271,16 @@
153{
154return _id;
155}
156+
157+ public static StoreType findById(int id)
158+ {
159+ for (StoreType StoreType : values())
160+ {
161+ if (StoreType.getId() == id)
162+ return StoreType;
163+ }
164+ return null;
165+ }
166}
167
168public enum PunishLevel
169@@ -415,6 +425,7 @@
170private int _teleMode;
171private boolean _isCrystallizing;
172private boolean _isCrafting;
173+ private long _offlineShopStart = 0;
174
175private final Map _dwarvenRecipeBook = new HashMap<>();
176private final Map _commonRecipeBook = new HashMap<>();
177@@ -704,9 +715,16 @@
178
179public String getAccountName()
180{
181+ if (getClient() == null)
182+ return getAccountNamePlayer();
183return getClient().getAccountName();
184}
185
186+ public String getAccountNamePlayer()
187+ {
188+ return _accountName;
189+ }
190+
191public Map getAccountChars()
192{
193return _chars;
194@@ -4642,6 +4660,9 @@
195public void setStoreType(StoreType type)
196{
197_storeType = type;
198+
199+ if (Config.OFFLINE_DISCONNECT_FINISHED && (type == StoreType.NONE) && ((getClient() == null) || getClient().isDetached()))
200+ deleteMe();
201}
202
203/**
204@@ -10169,6 +10190,16 @@
205return _isInSiege;
206}
207
208+ public long getOfflineStartTime()
209+ {
210+ return _offlineShopStart;
211+ }
212+
213+ public void setOfflineStartTime(long time)
214+ {
215+ _offlineShopStart = time;
216+ }
217+
218/**
219* Remove player from BossZones (used on char logout/exit)
220*/
221Index: java/net/sf/l2j/gameserver/model/actor/status/PcStatus.java
222===================================================================
223--- java/net/sf/l2j/gameserver/model/actor/status/PcStatus.java (revision 371)
224+++ java/net/sf/l2j/gameserver/model/actor/status/PcStatus.java (working copy)
225@@ -22,6 +22,7 @@
226import net.sf.l2j.gameserver.model.actor.L2Playable;
227import net.sf.l2j.gameserver.model.actor.L2Summon;
228import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
229+import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance.StoreType;
230import net.sf.l2j.gameserver.model.actor.instance.L2SummonInstance;
231import net.sf.l2j.gameserver.model.actor.stat.PcStat;
232import net.sf.l2j.gameserver.model.entity.Duel.DuelState;
233@@ -68,6 +69,10 @@
234if (getActiveChar().isDead())
235return;
236+
237+ // If OFFLINE_MODE_NO_DAMAGE is enabled and player is offline and he is in store/craft mode, no damage is taken.
238+ 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)))))
239+ return;
240+
241// invul handling
242if (getActiveChar().isInvul())
243{
244Index: java/net/sf/l2j/gameserver/network/L2GameClient.java
245===================================================================
246--- java/net/sf/l2j/gameserver/network/L2GameClient.java(revision 371)
247+++ java/net/sf/l2j/gameserver/network/L2GameClient.java(working copy)
248@@ -41,6 +41,8 @@
249import net.sf.l2j.gameserver.model.L2Clan;
250import net.sf.l2j.gameserver.model.L2World;
251import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
252+import net.sf.l2j.gameserver.model.olympiad.OlympiadManager;
253+import net.sf.l2j.gameserver.model.zone.ZoneId;
254import net.sf.l2j.gameserver.network.serverpackets.ActionFailed;
255import net.sf.l2j.gameserver.network.serverpackets.L2GameServerPacket;
256import net.sf.l2j.gameserver.network.serverpackets.ServerClose;
257@@ -445,6 +447,9 @@
258
259public void close(L2GameServerPacket gsp)
260{
261+ if (getConnection() == null)
262+ return;
263+
264getConnection().close(gsp);
265}
266
267@@ -528,9 +533,6 @@
268
269protected class DisconnectTask implements Runnable
270{
271- /**
272-* @see java.lang.Runnable#run()
273-*/
274@Override
275public void run()
276{
277@@ -541,6 +543,31 @@
278if (getActiveChar() != null && !isDetached())
279{
280setDetached(true);
281+ if (offlineMode(getActiveChar()))
282+ {
283+ getActiveChar().leaveParty();
284+ OlympiadManager.getInstance().unRegisterNoble(getActiveChar());
285+
286+ // If the L2PcInstance has Pet, unsummon it
287+ if (getActiveChar().hasPet())
288+ {
289+ getActiveChar().getPet().unSummon(getActiveChar());
290+ // Dead pet wasn't unsummoned, broadcast npcinfo changes (pet will be without owner name - means owner offline)
291+ if (getActiveChar().getPet() != null)
292+ getActiveChar().getPet().broadcastNpcInfo(0);
293+ }
294+
295+ if (Config.OFFLINE_SET_NAME_COLOR)
296+ {
297+ getActiveChar().getAppearance().setNameColor(Config.OFFLINE_NAME_COLOR);
298+ getActiveChar().updateAndBroadcastStatus();
299+ }
300+
301+ if (getActiveChar().getOfflineStartTime() == 0)
302+ getActiveChar().setOfflineStartTime(System.currentTimeMillis());
303+
304+ return;
305+ }
306fast = !getActiveChar().isInCombat() && !getActiveChar().isLocked();
307}
308cleanMe(fast);
309@@ -552,6 +579,43 @@
310}
311}
312+
313+ /**
314+ * @param player the player to be check.
315+ * @return {@code true} if the player is allowed to remain as off-line shop.
316+ */
317+ protected static boolean offlineMode(L2PcInstance player)
318+ {
319+ if (player.isInOlympiadMode() || player.isFestivalParticipant() || player.isInJail() || player.getVehicle() != null)
320+ return false;
321+
322+ boolean canSetShop = false;
323+ switch (player.getStoreType())
324+ {
325+ case SELL:
326+ case PACKAGE_SELL:
327+ case BUY:
328+ {
329+ canSetShop = Config.OFFLINE_TRADE_ENABLE;
330+ break;
331+ }
332+ case MANUFACTURE:
333+ {
334+ canSetShop = Config.OFFLINE_TRADE_ENABLE;
335+ break;
336+ }
337+ default:
338+ {
339+ canSetShop = Config.OFFLINE_CRAFT_ENABLE && player.isCrafting();
340+ break;
341+ }
342+ }
343+
344+ if (Config.OFFLINE_MODE_IN_PEACE_ZONE && !player.isInsideZone(ZoneId.PEACE))
345+ canSetShop = false;
346+
347+ return canSetShop;
348+ }
349+
350public void cleanMe(boolean fast)
351{
352try
353Index: java/net/sf/l2j/gameserver/datatables/OfflineTradersTable.java
354===================================================================
355--- java/net/sf/l2j/gameserver/datatables/OfflineTradersTable.java (revision 0)
356+++ java/net/sf/l2j/gameserver/datatables/OfflineTradersTable.java (working copy)
357@@ -0,0 +1,270 @@
358+/*
359+ * Copyright © 2004-2015 L2J Server
360+ *
361+ * This file is part of L2J Server.
362+ *
363+ * L2J Server is free software: you can redistribute it and/or modify
364+ * it under the terms of the GNU General Public License as published by
365+ * the Free Software Foundation, either version 3 of the License, or
366+ * (at your option) any later version.
367+ *
368+ * L2J Server is distributed in the hope that it will be useful,
369+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
370+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
371+ * General Public License for more details.
372+ *
373+ * You should have received a copy of the GNU General Public License
374+ * along with this program. If not, see .
375+ */
376+package net.sf.l2j.gameserver.datatables;
377+
378+import java.sql.Connection;
379+import java.sql.PreparedStatement;
380+import java.sql.ResultSet;
381+import java.sql.Statement;
382+import java.util.Calendar;
383+import java.util.logging.Level;
384+import java.util.logging.Logger;
385+
386+import net.sf.l2j.Config;
387+import net.sf.l2j.L2DatabaseFactory;
388+import net.sf.l2j.gameserver.LoginServerThread;
389+import net.sf.l2j.gameserver.model.L2ManufactureItem;
390+import net.sf.l2j.gameserver.model.L2ManufactureList;
391+import net.sf.l2j.gameserver.model.World;
392+import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
393+import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance.StoreType;
394+import net.sf.l2j.gameserver.model.tradelist.TradeItem;
395+import net.sf.l2j.gameserver.network.L2GameClient;
396+import net.sf.l2j.gameserver.network.L2GameClient.GameClientState;
397+
398+public class OfflineTradersTable
399+{
400+ private static Logger LOGGER = Logger.getLogger(OfflineTradersTable.class.getName());
401+
402+ // SQL DEFINITIONS
403+ private static final String SAVE_OFFLINE_STATUS = "INSERT INTO character_offline_trade (`charId`,`time`,`type`,`title`) VALUES (?,?,?,?)";
404+ private static final String SAVE_ITEMS = "INSERT INTO character_offline_trade_items (`charId`,`item`,`count`,`price`) VALUES (?,?,?,?)";
405+ private static final String CLEAR_OFFLINE_TABLE = "DELETE FROM character_offline_trade";
406+ private static final String CLEAR_OFFLINE_TABLE_ITEMS = "DELETE FROM character_offline_trade_items";
407+ private static final String LOAD_OFFLINE_STATUS = "SELECT * FROM character_offline_trade";
408+ private static final String LOAD_OFFLINE_ITEMS = "SELECT * FROM character_offline_trade_items WHERE charId = ?";
409+
410+ public void storeOffliners()
411+ {
412+ try (Connection con = L2DatabaseFactory.getInstance().getConnection();
413+ PreparedStatement stm1 = con.prepareStatement(CLEAR_OFFLINE_TABLE);
414+ PreparedStatement stm2 = con.prepareStatement(CLEAR_OFFLINE_TABLE_ITEMS);
415+ PreparedStatement stm3 = con.prepareStatement(SAVE_OFFLINE_STATUS);
416+ PreparedStatement stm_items = con.prepareStatement(SAVE_ITEMS))
417+ {
418+ stm1.execute();
419+ stm2.execute();
420+ con.setAutoCommit(false); // avoid halfway done
421+
422+ for (L2PcInstance pc : World.getInstance().getPlayers())
423+ {
424+ try
425+ {
426+ if ((pc.getStoreType() != StoreType.NONE) && ((pc.getClient() == null) || pc.getClient().isDetached()))
427+ {
428+ stm3.setInt(1, pc.getObjectId()); // Char Id
429+ stm3.setLong(2, pc.getOfflineStartTime());
430+ stm3.setInt(3, pc.getStoreType().getId()); // store type
431+ String title = null;
432+
433+ switch (pc.getStoreType())
434+ {
435+ case BUY:
436+ if (!Config.OFFLINE_TRADE_ENABLE)
437+ continue;
438+
439+ title = pc.getBuyList().getTitle();
440+ for (TradeItem i : pc.getBuyList().getItems())
441+ {
442+ stm_items.setInt(1, pc.getObjectId());
443+ stm_items.setInt(2, i.getItem().getItemId());
444+ stm_items.setLong(3, i.getCount());
445+ stm_items.setLong(4, i.getPrice());
446+ stm_items.executeUpdate();
447+ stm_items.clearParameters();
448+ }
449+ break;
450+ case SELL:
451+ case PACKAGE_SELL:
452+ if (!Config.OFFLINE_TRADE_ENABLE)
453+ continue;
454+
455+ title = pc.getSellList().getTitle();
456+ for (TradeItem i : pc.getSellList().getItems())
457+ {
458+ stm_items.setInt(1, pc.getObjectId());
459+ stm_items.setInt(2, i.getObjectId());
460+ stm_items.setLong(3, i.getCount());
461+ stm_items.setLong(4, i.getPrice());
462+ stm_items.executeUpdate();
463+ stm_items.clearParameters();
464+ }
465+ break;
466+ case MANUFACTURE:
467+ if (!Config.OFFLINE_CRAFT_ENABLE)
468+ continue;
469+
470+ title = pc.getCreateList().getStoreName();
471+ for (L2ManufactureItem i : pc.getCreateList().getList())
472+ {
473+ stm_items.setInt(1, pc.getObjectId());
474+ stm_items.setInt(2, i.getRecipeId());
475+ stm_items.setLong(3, 0);
476+ stm_items.setLong(4, i.getCost());
477+ stm_items.executeUpdate();
478+ stm_items.clearParameters();
479+ }
480+ }
481+ stm3.setString(4, title);
482+ stm3.executeUpdate();
483+ stm3.clearParameters();
484+ con.commit(); // flush
485+ }
486+ }
487+ catch (Exception e)
488+ {
489+ LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Error while saving offline trader: " + pc.getObjectId() + " " + e, e);
490+ }
491+ }
492+ LOGGER.info(getClass().getSimpleName() + ": Offline traders stored.");
493+ }
494+ catch (Exception e)
495+ {
496+ LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Error while saving offline traders: " + e, e);
497+ }
498+ }
499+
500+ public void restoreOfflineTraders()
501+ {
502+ LOGGER.info(getClass().getSimpleName() + ": Loading offline traders...");
503+ int nTraders = 0;
504+ try (Connection con = L2DatabaseFactory.getInstance().getConnection();
505+ Statement stm = con.createStatement();
506+ ResultSet rs = stm.executeQuery(LOAD_OFFLINE_STATUS))
507+ {
508+ while (rs.next())
509+ {
510+ long time = rs.getLong("time");
511+ if (Config.OFFLINE_MAX_DAYS > 0)
512+ {
513+ Calendar cal = Calendar.getInstance();
514+ cal.setTimeInMillis(time);
515+ cal.add(Calendar.DAY_OF_YEAR, Config.OFFLINE_MAX_DAYS);
516+ if (cal.getTimeInMillis() <= System.currentTimeMillis())
517+ continue;
518+ }
519+
520+ StoreType type = StoreType.findById(rs.getInt("type"));
521+ if (type == null)
522+ {
523+ LOGGER.warning(getClass().getSimpleName() + ": StoreType with id " + rs.getInt("type") + " could not be found.");
524+ continue;
525+ }
526+
527+ if (type == StoreType.NONE)
528+ continue;
529+
530+ L2PcInstance player = null;
531+
532+ try
533+ {
534+ L2GameClient client = new L2GameClient(null);
535+ client.setDetached(true);
536+ player = L2PcInstance.restore(rs.getInt("charId"));
537+ client.setActiveChar(player);
538+ player.setOnlineStatus(true, false);
539+ client.setAccountName(player.getAccountNamePlayer());
540+ client.setState(GameClientState.IN_GAME);
541+ player.setClient(client);
542+ player.setOfflineStartTime(time);
543+ player.spawnMe(player.getX(), player.getY(), player.getZ());
544+ LoginServerThread.getInstance().addGameServerLogin(player.getAccountName(), client);
545+ try (PreparedStatement stm_items = con.prepareStatement(LOAD_OFFLINE_ITEMS))
546+ {
547+ stm_items.setInt(1, player.getObjectId());
548+ try (ResultSet items = stm_items.executeQuery())
549+ {
550+ switch (type)
551+ {
552+ case BUY:
553+ while (items.next())
554+ {
555+ if (player.getBuyList().addItemByItemId(items.getInt(2), items.getInt(3), items.getInt(4)) == null)
556+ throw new NullPointerException();
557+ }
558+ player.getBuyList().setTitle(rs.getString("title"));
559+ break;
560+ case SELL:
561+ case PACKAGE_SELL:
562+ while (items.next())
563+ {
564+ if (player.getSellList().addItem(items.getInt(2), items.getInt(3), items.getInt(4)) == null)
565+ throw new NullPointerException();
566+ }
567+ player.getSellList().setTitle(rs.getString("title"));
568+ player.getSellList().setPackaged(type == StoreType.PACKAGE_SELL);
569+ break;
570+ case MANUFACTURE:
571+ L2ManufactureList createList = new L2ManufactureList();
572+ while (items.next())
573+ {
574+ createList.add(new L2ManufactureItem(items.getInt(2), items.getInt(4)));
575+ }
576+
577+ createList.setStoreName(rs.getString("title"));
578+ player.setCreateList(createList);
579+ break;
580+ }
581+ }
582+ }
583+ player.sitDown();
584+ if (Config.OFFLINE_SET_NAME_COLOR)
585+ player.getAppearance().setNameColor(Config.OFFLINE_NAME_COLOR);
586+ player.setStoreType(type);
587+ player.setOnlineStatus(true, true);
588+ player.restoreEffects();
589+ player.broadcastUserInfo();
590+ nTraders++;
591+ }
592+ catch (Exception e)
593+ {
594+ LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Error loading trader: " + player, e);
595+ if (player != null)
596+ player.deleteMe();
597+ }
598+ }
599+
600+ LOGGER.info(getClass().getSimpleName() + ": Loaded: " + nTraders + " offline trader(s)");
601+
602+ try (Statement stm1 = con.createStatement())
603+ {
604+ stm1.execute(CLEAR_OFFLINE_TABLE);
605+ stm1.execute(CLEAR_OFFLINE_TABLE_ITEMS);
606+ }
607+ }
608+ catch (Exception e)
609+ {
610+ LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Error while loading offline traders: ", e);
611+ }
612+ }
613+
614+ /**
615+* Gets the single instance of OfflineTradersTable.
616+* @return single instance of OfflineTradersTable
617+*/
618+ public static OfflineTradersTable getInstance()
619+ {
620+ return SingletonHolder._instance;
621+ }
622+
623+ private static class SingletonHolder
624+ {
625+ protected static final OfflineTradersTable _instance = new OfflineTradersTable();
626+ }
627+}
628Index: java/net/sf/l2j/gameserver/Shutdown.java
629===================================================================
630--- java/net/sf/l2j/gameserver/Shutdown.java(revision 371)
631+++ java/net/sf/l2j/gameserver/Shutdown.java(working copy)
632@@ -21,6 +21,7 @@
633import net.sf.l2j.L2DatabaseFactory;
634import net.sf.l2j.commons.lang.StringUtil;
635import net.sf.l2j.gameserver.datatables.BufferTable;
636+import net.sf.l2j.gameserver.datatables.OfflineTradersTable;
637import net.sf.l2j.gameserver.instancemanager.CastleManorManager;
638import net.sf.l2j.gameserver.instancemanager.FishingChampionshipManager;
639import net.sf.l2j.gameserver.instancemanager.FourSepulchersManager;
640@@ -118,6 +119,19 @@
641{
642StringUtil.printSection("Under " + MODE_TEXT[_shutdownMode] + " process");
643
644+ try
645+ {
646+ if ((Config.OFFLINE_TRADE_ENABLE || Config.OFFLINE_CRAFT_ENABLE) && Config.RESTORE_OFFLINERS)
647+ {
648+ OfflineTradersTable.getInstance().storeOffliners();
649+ _log.info("Offline Traders Table: Offline shops stored.");
650+ }
651+ }
652+ catch (Throwable t)
653+ {
654+ _log.log(Level.WARNING, "Error saving offline shops.", t);
655+ }
656+
657// disconnect players
658try
659{
660
661CREATE TABLE IF NOT EXISTS `character_offline_trade` (
662`charId` int(10) unsigned NOT NULL,
663`time` bigint(13) unsigned NOT NULL DEFAULT '0',
664`type` tinyint(4) NOT NULL DEFAULT '0',
665`title` varchar(50) DEFAULT NULL,
666PRIMARY KEY (`charId`)
667);
668CREATE TABLE IF NOT EXISTS `character_offline_trade_items` (
669`charId` int(10) unsigned NOT NULL,
670`item` int(10) unsigned NOT NULL DEFAULT '0', -- itemId(for buy) & ObjectId(for sell)
671`count` bigint(20) unsigned NOT NULL DEFAULT '0',
672`price` bigint(20) unsigned NOT NULL DEFAULT '0'
673);