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