View difference between Paste ID: hCYpWN6z and 4XqP11Qq
SHOW: | | - or go back to the newest paste.
1
### Eclipse Workspace Patch 1.0
2
#P aCis_gameserver
3
diff --git config/players.properties
4
+++ config/players.properties
5
@@ -255,7 +255,46 @@
6
 #=============================================================
7
 
8
 # Maximum number of buffs. Remember that Divine Inspiration will give 4 additional buff slots on top of the number specified. Default: 20
9
 MaxBuffsAmount = 20
10
 
11
 # Store buffs/debuffs on user logout. Default: True
12
 StoreSkillCooltime = True
13
14
+# =================================================================
15
+#                       Offline trade/craft
16
+# =================================================================
17
+
18
+# Option to enable or disable offline trade feature.
19
+# Enable -> true, Disable -> false
20
+OfflineTradeEnable = True
21
+
22
+# Option to enable or disable offline craft feature.
23
+# Enable -> true, Disable -> false
24
+OfflineCraftEnable = True
25
+
26
+# If set to True, off-line shops will be possible only in peace zones.
27
+# Default: False
28
+OfflineModeInPeaceZone = True
29
+
30
+# If set to True, players in off-line shop mode will not take any damage, thus they cannot be killed.
31
+# Default: False
32
+OfflineModeNoDamage = False
33
+
34
+# Restore offline traders/crafters after restart/shutdown.
35
+# Default: False
36
+RestoreOffliners = True
37
+
38
+# Do not restore offline characters, after OfflineMaxDays days spent from first restore.
39
+# Require server restart to disconnect expired shops.
40
+# 0 = disabled (always restore).
41
+# Default: 7
42
+OfflineMaxDays = 7
43
+
44
+# Disconnect shop after finished selling, buying.
45
+# Default: True
46
+OfflineDisconnectFinished = True
47
+
48
+#Offline Effect Sleep
49
+OfflineSetSleepEffect = True
50
+
51
+
52
diff --git java/net/sf/l2j/Config.java
53
+++ java/net/sf/l2j/Config.java
54
@@ -341,12 +341,23 @@
55
 	public static int MAX_PVTSTORE_SLOTS_DWARF;
56
 	public static int MAX_PVTSTORE_SLOTS_OTHER;
57
 	public static boolean DEEPBLUE_DROP_RULES;
58
 	public static boolean ALLOW_DELEVEL;
59
 	public static int DEATH_PENALTY_CHANCE;
60
 	
61
+	/** Offline Shop */
62
+	public static boolean OFFLINE_TRADE_ENABLE;
63
+	public static boolean OFFLINE_CRAFT_ENABLE;
64
+	public static boolean OFFLINE_MODE_IN_PEACE_ZONE;
65
+	public static boolean OFFLINE_MODE_NO_DAMAGE;
66
+	public static boolean RESTORE_OFFLINERS;
67
+	public static int OFFLINE_MAX_DAYS;
68
+	public static boolean OFFLINE_DISCONNECT_FINISHED;
69
+	public static boolean OFFLINE_SET_SLEEP;
70
+	
71
 	/** Inventory & WH */
72
 	public static int INVENTORY_MAXIMUM_NO_DWARF;
73
 	public static int INVENTORY_MAXIMUM_DWARF;
74
 	public static int INVENTORY_MAXIMUM_PET;
75
 	public static int MAX_ITEM_IN_PACKET;
76
 	public static double WEIGHT_LIMIT;
77
@@ -970,12 +981,21 @@
78
 		MAX_PVTSTORE_SLOTS_DWARF = players.getProperty("MaxPvtStoreSlotsDwarf", 5);
79
 		MAX_PVTSTORE_SLOTS_OTHER = players.getProperty("MaxPvtStoreSlotsOther", 4);
80
 		DEEPBLUE_DROP_RULES = players.getProperty("UseDeepBlueDropRules", true);
81
 		ALLOW_DELEVEL = players.getProperty("AllowDelevel", true);
82
 		DEATH_PENALTY_CHANCE = players.getProperty("DeathPenaltyChance", 20);
83
 		
84
+		OFFLINE_TRADE_ENABLE = players.getProperty("OfflineTradeEnable", false);
85
+		OFFLINE_CRAFT_ENABLE = players.getProperty("OfflineCraftEnable", false);
86
+		OFFLINE_MODE_IN_PEACE_ZONE = players.getProperty("OfflineModeInPeaceZone", false);
87
+		OFFLINE_MODE_NO_DAMAGE = players.getProperty("OfflineModeNoDamage", false);
88
+		OFFLINE_SET_SLEEP = players.getProperty("OfflineSetSleepEffect", false);
89
+		RESTORE_OFFLINERS = players.getProperty("RestoreOffliners", false);
90
+		OFFLINE_MAX_DAYS = players.getProperty("OfflineMaxDays", 10);
91
+		OFFLINE_DISCONNECT_FINISHED = players.getProperty("OfflineDisconnectFinished", true);
92
93
 		INVENTORY_MAXIMUM_NO_DWARF = players.getProperty("MaximumSlotsForNoDwarf", 80);
94
 		INVENTORY_MAXIMUM_DWARF = players.getProperty("MaximumSlotsForDwarf", 100);
95
 		INVENTORY_MAXIMUM_PET = players.getProperty("MaximumSlotsForPet", 12);
96
 		MAX_ITEM_IN_PACKET = Math.max(INVENTORY_MAXIMUM_NO_DWARF, INVENTORY_MAXIMUM_DWARF);
97
 		WEIGHT_LIMIT = players.getProperty("WeightLimit", 1.);
98
 		WAREHOUSE_SLOTS_NO_DWARF = players.getProperty("MaximumWarehouseSlotsForNoDwarf", 100);
99
diff --git java/net/sf/l2j/gameserver/GameServer.java
100
+++ java/net/sf/l2j/gameserver/GameServer.java
101
@@ -70,12 +70,13 @@
102
 import net.sf.l2j.gameserver.data.xml.SoulCrystalData;
103
 import net.sf.l2j.gameserver.data.xml.SpellbookData;
104
 import net.sf.l2j.gameserver.data.xml.StaticObjectData;
105
 import net.sf.l2j.gameserver.data.xml.SummonItemData;
106
 import net.sf.l2j.gameserver.data.xml.TeleportData;
107
 import net.sf.l2j.gameserver.data.xml.WalkerRouteData;
108
+import net.sf.l2j.gameserver.enums.actors.OfflineStoresData;
109
 import net.sf.l2j.gameserver.geoengine.GeoEngine;
110
 import net.sf.l2j.gameserver.handler.AdminCommandHandler;
111
 import net.sf.l2j.gameserver.handler.ChatHandler;
112
 import net.sf.l2j.gameserver.handler.ItemHandler;
113
 import net.sf.l2j.gameserver.handler.SkillHandler;
114
 import net.sf.l2j.gameserver.handler.TargetHandler;
115
@@ -235,12 +236,23 @@
116
 		TeleportData.getInstance();
117
 		
118
 		StringUtil.printSection("Olympiads & Heroes");
119
 		OlympiadGameManager.getInstance();
120
 		Olympiad.getInstance();
121
 		HeroManager.getInstance();
122
123
+		StringUtil.printSection("OfflineShop Started");
124
+		if ((Config.OFFLINE_TRADE_ENABLE || Config.OFFLINE_CRAFT_ENABLE) && Config.RESTORE_OFFLINERS)
125
+		{
126
+			OfflineStoresData.getInstance().restoreOfflineTraders();
127
+		}
128
+		else
129
+		{
130
+			LOGGER.info("Offline Shop is disabled.");
131
+		}
132
 		
133
 		StringUtil.printSection("Four Sepulchers");
134
 		FourSepulchersManager.getInstance();
135
 		
136
 		StringUtil.printSection("Quests & Scripts");
137
 		ScriptData.getInstance();
138
diff --git java/net/sf/l2j/gameserver/LoginServerThread.java
139
+++ java/net/sf/l2j/gameserver/LoginServerThread.java
140
@@ -292,12 +292,14 @@
141
 		}
142
 	}
143
 	
144
 	public void addClient(String account, GameClient client)
145
 	{
146
 		final GameClient existingClient = _clients.putIfAbsent(account, client);
147
+		if (client.isDetached())
148
+			return;
149
 		if (existingClient == null)
150
 		{
151
 			try
152
 			{
153
 				sendPacket(new PlayerAuthRequest(client.getAccountName(), client.getSessionId()));
154
 			}
155
diff --git java/net/sf/l2j/gameserver/Shutdown.java
156
+++ java/net/sf/l2j/gameserver/Shutdown.java
157
@@ -15,12 +15,13 @@
158
 import net.sf.l2j.gameserver.data.manager.GrandBossManager;
159
 import net.sf.l2j.gameserver.data.manager.HeroManager;
160
 import net.sf.l2j.gameserver.data.manager.PetitionManager;
161
 import net.sf.l2j.gameserver.data.manager.RaidBossManager;
162
 import net.sf.l2j.gameserver.data.manager.SevenSignsManager;
163
 import net.sf.l2j.gameserver.data.manager.ZoneManager;
164
+import net.sf.l2j.gameserver.enums.actors.OfflineStoresData;
165
 import net.sf.l2j.gameserver.model.World;
166
 import net.sf.l2j.gameserver.model.actor.Player;
167
 import net.sf.l2j.gameserver.model.olympiad.Olympiad;
168
 import net.sf.l2j.gameserver.network.GameClient;
169
 import net.sf.l2j.gameserver.network.SystemMessageId;
170
 import net.sf.l2j.gameserver.network.gameserverpackets.ServerStatus;
171
@@ -74,13 +75,24 @@
172
 	@Override
173
 	public void run()
174
 	{
175
 		if (this == SingletonHolder.INSTANCE)
176
 		{
177
 			StringUtil.printSection("Under " + MODE_TEXT[_shutdownMode] + " process");
178
179
+			try
180
+			{
181
+				if ((Config.OFFLINE_TRADE_ENABLE || Config.OFFLINE_CRAFT_ENABLE) && Config.RESTORE_OFFLINERS)
182
+				{
183
+					OfflineStoresData.getInstance().storeOffliners();
184
+					LOGGER.info("Offline Traders Table: Offline shops stored.");
185
+				}
186
+			}
187
+			catch (Throwable t)
188
+			{
189
+				
190
+			}
191
 			// disconnect players
192
 			try
193
 			{
194
 				disconnectAllPlayers();
195
 				LOGGER.info("All players have been disconnected.");
196
 			}
197
diff --git java/net/sf/l2j/gameserver/enums/actors/OfflineStoresData.java
198
+++ java/net/sf/l2j/gameserver/enums/actors/OfflineStoresData.java
199
@@ -0,0 +1,274 @@
200
+package net.sf.l2j.gameserver.enums.actors;
201
+
202
+import java.sql.Connection;
203
+import java.sql.PreparedStatement;
204
+import java.sql.ResultSet;
205
+import java.sql.Statement;
206
+import java.util.Calendar;
207
+import java.util.logging.Level;
208
+import java.util.logging.Logger;
209
+
210
+import net.sf.l2j.commons.pool.ConnectionPool;
211
+
212
+import net.sf.l2j.Config;
213
+import net.sf.l2j.gameserver.LoginServerThread;
214
+import net.sf.l2j.gameserver.enums.ZoneId;
215
+import net.sf.l2j.gameserver.model.World;
216
+import net.sf.l2j.gameserver.model.actor.Player;
217
+import net.sf.l2j.gameserver.model.craft.ManufactureItem;
218
+import net.sf.l2j.gameserver.model.craft.ManufactureList;
219
+import net.sf.l2j.gameserver.model.trade.TradeItem;
220
+import net.sf.l2j.gameserver.network.GameClient;
221
+import net.sf.l2j.gameserver.network.GameClient.GameClientState;
222
+
223
+public class OfflineStoresData
224
+{
225
+	private static final Logger LOGGER = Logger.getLogger(OfflineStoresData.class.getName());
226
+
227
+	// SQL DEFINITIONS
228
+	private static final String SAVE_OFFLINE_STATUS = "INSERT INTO character_offline_trade (`charId`,`time`,`type`,`title`) VALUES (?,?,?,?)";
229
+	private static final String SAVE_ITEMS = "INSERT INTO character_offline_trade_items (`charId`,`item`,`count`,`price`,`enchant`) VALUES (?,?,?,?,?)";
230
+	private static final String CLEAR_OFFLINE_TABLE = "DELETE FROM character_offline_trade";
231
+	private static final String CLEAR_OFFLINE_TABLE_ITEMS = "DELETE FROM character_offline_trade_items";
232
+	private static final String LOAD_OFFLINE_STATUS = "SELECT * FROM character_offline_trade";
233
+	private static final String LOAD_OFFLINE_ITEMS = "SELECT * FROM character_offline_trade_items WHERE charId = ?";
234
+
235
+	public void storeOffliners()
236
+	{
237
+		try (Connection con = ConnectionPool.getConnection(); PreparedStatement save_offline_status = con.prepareStatement(SAVE_OFFLINE_STATUS); PreparedStatement save_items = con.prepareStatement(SAVE_ITEMS))
238
+		{
239
+			try (Statement stm = con.createStatement())
240
+			{
241
+				stm.execute(CLEAR_OFFLINE_TABLE);
242
+				stm.execute(CLEAR_OFFLINE_TABLE_ITEMS);
243
+			}
244
+			for (Player pc : World.getInstance().getPlayers())
245
+			{
246
+				try
247
+				{
248
+					if (pc.getOperateType() != OperateType.NONE && (pc.getClient() == null || pc.getClient().isDetached()))
249
+					{
250
+						save_offline_status.setInt(1, pc.getObjectId());
251
+						save_offline_status.setLong(2, pc.getOfflineStartTime());
252
+						save_offline_status.setInt(3, pc.getOperateType().getId());
253
+						switch (pc.getOperateType())
254
+						{
255
+							case BUY:
256
+								if (!Config.OFFLINE_TRADE_ENABLE)
257
+									continue;
258
+
259
+								save_offline_status.setString(4, pc.getBuyList().getTitle());
260
+								for (TradeItem i : pc.getBuyList())
261
+								{
262
+									save_items.setInt(1, pc.getObjectId());
263
+									save_items.setInt(2, i.getItem().getItemId());
264
+									save_items.setLong(3, i.getCount());
265
+									save_items.setLong(4, i.getPrice());
266
+									save_items.setLong(5, i.getEnchant());
267
+									save_items.addBatch();
268
+								}
269
+								break;
270
+							case SELL:
271
+							case PACKAGE_SELL:
272
+								if (!Config.OFFLINE_TRADE_ENABLE)
273
+									continue;
274
+
275
+								save_offline_status.setString(4, pc.getSellList().getTitle());
276
+								pc.getSellList().updateItems();
277
+								for (TradeItem i : pc.getSellList())
278
+								{
279
+									save_items.setInt(1, pc.getObjectId());
280
+									save_items.setInt(2, i.getObjectId());
281
+									save_items.setLong(3, i.getCount());
282
+									save_items.setLong(4, i.getPrice());
283
+									save_items.setLong(5, i.getEnchant());
284
+									save_items.addBatch();
285
+								}
286
+								break;
287
+							case MANUFACTURE:
288
+								if (!Config.OFFLINE_CRAFT_ENABLE)
289
+									continue;
290
+
291
+								save_offline_status.setString(4, pc.getManufactureList().getStoreName());
292
+								for (final ManufactureItem i : pc.getManufactureList())
293
+								{
294
+									save_items.setInt(1, pc.getObjectId());
295
+			                        save_items.setInt(2, i.getId());
296
+			                        save_items.setLong(3, 0L);
297
+			                        save_items.setLong(4, i.getValue());
298
+			                        save_items.setLong(5, 0L);
299
+									save_items.addBatch();
300
+								}
301
+								break;
302
+						}
303
+						save_items.executeBatch();
304
+						save_offline_status.execute();
305
+					}
306
+				}
307
+				catch (Exception e)
308
+				{
309
+					LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Error while saving offline trader: " + pc.getObjectId() + " " + e, e);
310
+				}
311
+			}
312
+
313
+			LOGGER.info(getClass().getSimpleName() + ": Offline traders stored.");
314
+		}
315
+		catch (Exception e)
316
+		{
317
+			LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Error while saving offline traders: " + e, e);
318
+		}
319
+	}
320
+
321
+	public void restoreOfflineTraders()
322
+	{
323
+		LOGGER.info(getClass().getSimpleName() + ": Loading offline traders...");
324
+		try (Connection con = ConnectionPool.getConnection(); Statement stm = con.createStatement(); ResultSet rs = stm.executeQuery(LOAD_OFFLINE_STATUS))
325
+		{
326
+			int nTraders = 0;
327
+			while (rs.next())
328
+			{
329
+				final long time = rs.getLong("time");
330
+				if (Config.OFFLINE_MAX_DAYS > 0)
331
+				{
332
+					final Calendar cal = Calendar.getInstance();
333
+					cal.setTimeInMillis(time);
334
+					cal.add(Calendar.DAY_OF_YEAR, Config.OFFLINE_MAX_DAYS);
335
+					if (cal.getTimeInMillis() <= System.currentTimeMillis())
336
+						continue;
337
+				}
338
+
339
+				OperateType type = null;
340
+				for (OperateType t : OperateType.values())
341
+				{
342
+					if (t.getId() == rs.getInt("type"))
343
+					{
344
+						type = t;
345
+						break;
346
+					}
347
+				}
348
+				if (type == null)
349
+				{
350
+					LOGGER.warning(getClass().getSimpleName() + ": PrivateStoreType with id " + rs.getInt("type") + " could not be found.");
351
+					continue;
352
+				}
353
+				if (type == OperateType.NONE)
354
+					continue;
355
+
356
+				final Player player = Player.restore(rs.getInt("charId"));
357
+				if (player == null)
358
+					continue;
359
+
360
+				try (PreparedStatement stm_items = con.prepareStatement(LOAD_OFFLINE_ITEMS))
361
+				{
362
+					player.isRunning();
363
+					player.sitDown();
364
+					player.setOnlineStatus(true, false);
365
+
366
+					World.getInstance().addPlayer(player);
367
+
368
+					final GameClient client = new GameClient(null);
369
+					client.setDetached(true);
370
+					player.setClient(client);
371
+					client.setPlayer(player);
372
+					client.setAccountName(player.getAccountNamePlayer());
373
+					player.setOnlineStatus(true, true);
374
+					client.setState(GameClientState.IN_GAME);
375
+					player.setOfflineStartTime(time);
376
+					player.spawnMe();
377
+
378
+					LoginServerThread.getInstance().addClient(player.getAccountName(), client);
379
+
380
+					stm_items.setInt(1, player.getObjectId());
381
+					try (ResultSet items = stm_items.executeQuery())
382
+					{
383
+						switch (type)
384
+						{
385
+							case BUY:
386
+								while (items.next())
387
+								{
388
+								player.getBuyList().addItemByItemId(items.getInt(2), items.getInt(3), items.getInt(4), items.getInt(5));
389
+								}
390
+								
391
+								player.getBuyList().setTitle(rs.getString("title"));
392
+								break;
393
+							case SELL:
394
+							case PACKAGE_SELL:
395
+								while (items.next())
396
+									if (player.getSellList().addItem(items.getInt(2), items.getInt(3), items.getInt(4)) == null)
397
+										throw new NullPointerException("NPE at SELL of offlineShop " + player.getObjectId() + " " + items.getInt(2) + " " + items.getInt(3) + " " + items.getInt(4));
398
+								
399
+								player.getSellList().setTitle(rs.getString("title"));
400
+								player.getSellList().setPackaged(type == OperateType.PACKAGE_SELL);
401
+								break;
402
+		                      case MANUFACTURE:
403
+		                        while (items.next())
404
+		                        player.getManufactureList().add(new ManufactureItem(items.getInt(2), items.getInt(4))); 
405
+		                        player.getManufactureList().setStoreName(rs.getString("title"));
406
+		                        break;
407
+						}
408
+					}
409
+
410
+					if (Config.OFFLINE_SET_SLEEP)
411
+						player.startAbnormalEffect(0x000080);
412
+
413
+					player.setOperateType(type);
414
+					player.restoreEffects();
415
+					player.broadcastUserInfo();
416
+
417
+					nTraders++;
418
+				}
419
+				catch (Exception e)
420
+				{
421
+					LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Error loading trader: " + player, e);
422
+
423
+					player.deleteMe();
424
+				}
425
+			}
426
+
427
+			LOGGER.info(getClass().getSimpleName() + ": Loaded: " + nTraders + " offline trader(s)");
428
+
429
+			try (Statement stm1 = con.createStatement())
430
+			{
431
+				stm1.execute(CLEAR_OFFLINE_TABLE);
432
+				stm1.execute(CLEAR_OFFLINE_TABLE_ITEMS);
433
+			}
434
+		}
435
+		catch (Exception e)
436
+		{
437
+			LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Error while loading offline traders: ", e);
438
+		}
439
+	}
440
+
441
+	public static boolean offlineMode(Player player)
442
+	{
443
+		if (player.isInOlympiadMode() || player.isFestivalParticipant() || player.isInJail() || player.getBoat() != null)
444
+			return false;
445
+
446
+		boolean canSetShop = false;
447
+		switch (player.getOperateType())
448
+		{
449
+			case SELL:
450
+			case PACKAGE_SELL:
451
+			case BUY:
452
+				canSetShop = Config.OFFLINE_TRADE_ENABLE;
453
+				break;
454
+			case MANUFACTURE:
455
+				canSetShop = Config.OFFLINE_CRAFT_ENABLE;
456
+				break;
457
+		}
458
+		if (Config.OFFLINE_MODE_IN_PEACE_ZONE && !player.isInsideZone(ZoneId.PEACE))
459
+			canSetShop = false;
460
+
461
+		return canSetShop;
462
+	}
463
+
464
+	public static OfflineStoresData getInstance()
465
+	{
466
+		return SingletonHolder._instance;
467
+	}
468
+
469
+	private static class SingletonHolder
470
+	{
471
+		protected static final OfflineStoresData _instance = new OfflineStoresData();
472
+	}
473
+}
474
\ No newline at end of file
475
diff --git java/net/sf/l2j/gameserver/enums/actors/OperateType.java
476
+++ java/net/sf/l2j/gameserver/enums/actors/OperateType.java
477
@@ -15,12 +15,20 @@
478
 	private int _id;
479
 	
480
 	private OperateType(int id)
481
 	{
482
 		_id = id;
483
 	}
484
+	public static OperateType findById(int id)
485
+	{
486
+		for (OperateType privateStoreType : values())
487
+		{
488
+			if (privateStoreType.getId() == id)
489
+				return privateStoreType;
490
+		}
491
+		return null;
492
+	}
493
 	public int getId()
494
 	{
495
 		return _id;
496
 	}
497
 }
498
\ No newline at end of file
499
diff --git java/net/sf/l2j/gameserver/model/actor/Player.java
500
+++ java/net/sf/l2j/gameserver/model/actor/Player.java
501
@@ -2240,15 +2240,20 @@
502
 	{
503
 		_client = client;
504
 	}
505
 	
506
 	public String getAccountName()
507
 	{
508
+		if (getClient() == null)
509
+			return getAccountNamePlayer();
510
 		return _accountName;
511
 	}
512
513
+	public String getAccountNamePlayer()
514
+	{
515
+		return _accountName;
516
+	}
517
 	public Map<Integer, String> getAccountChars()
518
 	{
519
 		return _chars;
520
 	}
521
 	
522
 	/**
523
@@ -3275,14 +3280,26 @@
524
 	 * Set the {@link OperateType} of this {@link Player}.
525
 	 * @param type : The new {@link OperateType} state to set.
526
 	 */
527
 	public void setOperateType(OperateType type)
528
 	{
529
 		_operateType = type;
530
+		if (Config.OFFLINE_DISCONNECT_FINISHED && type == OperateType.NONE && (getClient() == null || getClient().isDetached()))
531
+			deleteMe();
532
 	}
533
 	
534
+	private long _offlineShopStart;
535
+	public long getOfflineStartTime()
536
+	{
537
+		return _offlineShopStart;
538
+	}
539
+	public void setOfflineStartTime(long time)
540
+	{
541
+		_offlineShopStart = time;
542
+	}
543
+		
544
 	/**
545
 	 * @return True if this {@link Player} is set on any store mode, or false otherwise.
546
 	 */
547
 	public boolean isInStoreMode()
548
 	{
549
 		return _operateType == OperateType.BUY || _operateType == OperateType.SELL || _operateType == OperateType.PACKAGE_SELL || _operateType == OperateType.MANUFACTURE;
550
diff --git java/net/sf/l2j/gameserver/network/GameClient.java
551
+++ java/net/sf/l2j/gameserver/network/GameClient.java
552
@@ -19,15 +19,18 @@
553
 
554
 import net.sf.l2j.Config;
555
 import net.sf.l2j.gameserver.LoginServerThread;
556
 import net.sf.l2j.gameserver.data.sql.ClanTable;
557
 import net.sf.l2j.gameserver.data.sql.PlayerInfoTable;
558
 import net.sf.l2j.gameserver.enums.FloodProtector;
559
+import net.sf.l2j.gameserver.enums.MessageType;
560
+import net.sf.l2j.gameserver.enums.ZoneId;
561
 import net.sf.l2j.gameserver.model.CharSelectSlot;
562
 import net.sf.l2j.gameserver.model.World;
563
 import net.sf.l2j.gameserver.model.actor.Player;
564
+import net.sf.l2j.gameserver.model.olympiad.OlympiadManager;
565
 import net.sf.l2j.gameserver.model.pledge.Clan;
566
 import net.sf.l2j.gameserver.network.serverpackets.ActionFailed;
567
 import net.sf.l2j.gameserver.network.serverpackets.L2GameServerPacket;
568
 import net.sf.l2j.gameserver.network.serverpackets.ServerClose;
569
 
570
 /**
571
@@ -206,22 +209,82 @@
572
 			ThreadPool.execute(() ->
573
 			{
574
 				boolean fast = true;
575
 				if (getPlayer() != null && !isDetached())
576
 				{
577
 					setDetached(true);
578
+					if (offlineMode(getPlayer()))
579
+					{
580
+						if (getPlayer().getParty() != null)
581
+							getPlayer().getParty().removePartyMember(getPlayer(), MessageType.EXPELLED);
582
+						OlympiadManager.getInstance().unRegisterNoble(getPlayer());
583
+						
584
+						// If the Character has Pet, unsummon it
585
+						if (getPlayer().hasPet())
586
+						{
587
+							getPlayer().getSummon().unSummon(getPlayer());
588
+							// Dead pet wasn't unsummoned, broadcast npcinfo changes (pet will be without owner name - means owner offline)
589
+							if (getPlayer().getSummon() != null)
590
+								getPlayer().getSummon().updateAndBroadcastStatus(0);
591
+						}
592
+						
593
+						if (Config.OFFLINE_SET_SLEEP)
594
+							getPlayer().startAbnormalEffect(0x000080);
595
+						
596
+						if (getPlayer().getOfflineStartTime() == 0)
597
+							getPlayer().setOfflineStartTime(System.currentTimeMillis());
598
+						
599
+						return;
600
+					}
601
 					fast = !getPlayer().isInCombat() && !getPlayer().isLocked();
602
 				}
603
 				cleanMe(fast);
604
 			});
605
 		}
606
 		catch (RejectedExecutionException e)
607
 		{
608
 		}
609
 	}
610
 	
611
+	/**
612
+	 * @param player
613
+	 * @return
614
+	 */
615
+	public static boolean offlineMode(Player player)
616
+	{
617
+		if (player.isInOlympiadMode() || player.isFestivalParticipant() || player.isInJail() || player.getBoat() != null)
618
+			return false;
619
+		
620
+		boolean canSetShop = false;
621
+		switch (player.getOperateType())
622
+		{
623
+			case SELL:
624
+			case PACKAGE_SELL:
625
+			case BUY:
626
+			{
627
+				canSetShop = Config.OFFLINE_TRADE_ENABLE;
628
+				break;
629
+			}
630
+			case MANUFACTURE:
631
+			{
632
+				canSetShop = Config.OFFLINE_TRADE_ENABLE;
633
+				break;
634
+			}
635
+			default:
636
+			{
637
+				canSetShop = Config.OFFLINE_CRAFT_ENABLE && player.isCrafting();
638
+				break;
639
+			}
640
+		}
641
+		
642
+		if (Config.OFFLINE_MODE_IN_PEACE_ZONE && !player.isInsideZone(ZoneId.PEACE))
643
+			canSetShop = false;
644
+		
645
+		return canSetShop;
646
+	}
647
+
648
 	@Override
649
 	protected void onForcedDisconnection()
650
 	{
651
 		LOGGER.debug("{} disconnected abnormally.", toString());
652
 	}
653
 	
654
@@ -578,12 +641,14 @@
655
 	{
656
 		_slots = list;
657
 	}
658
 	
659
 	public void close(L2GameServerPacket gsp)
660
 	{
661
+		if (getConnection() == null)
662
+			return;
663
 		getConnection().close(gsp);
664
 	}
665
 	
666
 	/**
667
 	 * @param slot : The slot to test.
668
 	 * @return the objectId of the character associated to that slot, or -1 if not found.
669