Player.java 43.5 KB
Newer Older
Melledy's avatar
Melledy committed
1
package emu.grasscutter.game.player;
Melledy's avatar
Melledy committed
2
3

import dev.morphia.annotations.*;
4
import emu.grasscutter.GameConstants;
5
import emu.grasscutter.Grasscutter;
6
import emu.grasscutter.data.GameData;
Melledy's avatar
Melledy committed
7
import emu.grasscutter.data.excels.PlayerLevelData;
AnimeGitB's avatar
AnimeGitB committed
8
import emu.grasscutter.data.excels.WeatherData;
Melledy's avatar
Melledy committed
9
import emu.grasscutter.database.DatabaseHelper;
Melledy's avatar
Melledy committed
10
11
import emu.grasscutter.game.Account;
import emu.grasscutter.game.CoopRequest;
Melledy's avatar
Melledy committed
12
import emu.grasscutter.game.ability.AbilityManager;
Akka's avatar
Akka committed
13
import emu.grasscutter.game.activity.ActivityManager;
14
import emu.grasscutter.game.avatar.Avatar;
Kengxxiao's avatar
Kengxxiao committed
15
import emu.grasscutter.game.avatar.AvatarStorage;
16
import emu.grasscutter.game.battlepass.BattlePassManager;
17
18
import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.game.entity.EntityVehicle;
19
import emu.grasscutter.game.home.GameHome;
20
import emu.grasscutter.game.entity.EntityGadget;
Melledy's avatar
Melledy committed
21
import emu.grasscutter.game.entity.EntityItem;
22
import emu.grasscutter.game.entity.GameEntity;
Kinesis's avatar
Kinesis committed
23
import emu.grasscutter.game.expedition.ExpeditionInfo;
Melledy's avatar
Melledy committed
24
25
26
import emu.grasscutter.game.friends.FriendsList;
import emu.grasscutter.game.friends.PlayerProfile;
import emu.grasscutter.game.gacha.PlayerGachaInfo;
27
import emu.grasscutter.game.inventory.GameItem;
Melledy's avatar
Melledy committed
28
import emu.grasscutter.game.inventory.Inventory;
Melledy's avatar
Melledy committed
29
import emu.grasscutter.game.mail.Mail;
30
import emu.grasscutter.game.mail.MailHandler;
31
import emu.grasscutter.game.managers.CookingManager;
Akka's avatar
Akka committed
32
import emu.grasscutter.game.managers.FurnitureManager;
ImmuState's avatar
ImmuState committed
33
import emu.grasscutter.game.managers.ResinManager;
Melledy's avatar
Melledy committed
34
35
36
37
38
39
import emu.grasscutter.game.managers.deforestation.DeforestationManager;
import emu.grasscutter.game.managers.energy.EnergyManager;
import emu.grasscutter.game.managers.forging.ActiveForgeData;
import emu.grasscutter.game.managers.forging.ForgingManager;
import emu.grasscutter.game.managers.mapmark.*;
import emu.grasscutter.game.managers.stamina.StaminaManager;
40
import emu.grasscutter.game.managers.SotSManager;
Melledy's avatar
Melledy committed
41
import emu.grasscutter.game.props.ActionReason;
AnimeGitB's avatar
AnimeGitB committed
42
import emu.grasscutter.game.props.ClimateType;
Melledy's avatar
Melledy committed
43
import emu.grasscutter.game.props.PlayerProperty;
44
import emu.grasscutter.game.props.WatcherTriggerType;
Melledy's avatar
Melledy committed
45
import emu.grasscutter.game.quest.QuestManager;
Kengxxiao's avatar
Kengxxiao committed
46
import emu.grasscutter.game.shop.ShopLimit;
47
import emu.grasscutter.game.tower.TowerData;
Akka's avatar
Akka committed
48
import emu.grasscutter.game.tower.TowerManager;
Melledy's avatar
Melledy committed
49
50
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.game.world.World;
51
import emu.grasscutter.net.packet.BasePacket;
52
import emu.grasscutter.net.proto.*;
Melledy's avatar
Melledy committed
53
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
54
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
Melledy's avatar
Melledy committed
55
import emu.grasscutter.net.proto.CombatInvokeEntryOuterClass.CombatInvokeEntry;
ImmuState's avatar
ImmuState committed
56
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
Melledy's avatar
Melledy committed
57
58
59
60
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
import emu.grasscutter.net.proto.MpSettingTypeOuterClass.MpSettingType;
import emu.grasscutter.net.proto.OnlinePlayerInfoOuterClass.OnlinePlayerInfo;
import emu.grasscutter.net.proto.PlayerLocationInfoOuterClass.PlayerLocationInfo;
Melledy's avatar
Melledy committed
61
import emu.grasscutter.net.proto.ProfilePictureOuterClass.ProfilePicture;
Melledy's avatar
Melledy committed
62
import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail;
63
import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType;
64
65
import emu.grasscutter.server.event.player.PlayerJoinEvent;
import emu.grasscutter.server.event.player.PlayerQuitEvent;
Melledy's avatar
Melledy committed
66
67
import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.server.game.GameSession;
68
import emu.grasscutter.server.game.GameSession.SessionState;
69
import emu.grasscutter.server.packet.send.*;
Yazawazi's avatar
utils    
Yazawazi committed
70
import emu.grasscutter.utils.DateHelper;
Kengxxiao's avatar
Kengxxiao committed
71
import emu.grasscutter.utils.Position;
72
import emu.grasscutter.utils.MessageHandler;
Kinesis's avatar
Kinesis committed
73
import emu.grasscutter.utils.Utils;
Melledy's avatar
Melledy committed
74
75
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
AnimeGitB's avatar
AnimeGitB committed
76
import lombok.Getter;
Melledy's avatar
Melledy committed
77

78
79
import static emu.grasscutter.config.Configuration.*;

80
import java.time.DayOfWeek;
81
82
83
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
Asnxthaony's avatar
Asnxthaony committed
84
import java.util.*;
85
import java.util.concurrent.LinkedBlockingQueue;
Asnxthaony's avatar
Asnxthaony committed
86

KingRainbow44's avatar
KingRainbow44 committed
87
@Entity(value = "players", useDiscriminator = false)
88
public class Player {
Melledy's avatar
Melledy committed
89
90
	@Id private int id;
	@Indexed(options = @IndexOptions(unique = true)) private String accountId;
Melledy's avatar
Melledy committed
91
92
93
	private transient Account account;
	private transient GameSession session;
	
Melledy's avatar
Melledy committed
94
95
96
97
	private String nickname;
	private String signature;
	private int headImage;
	private int nameCardId = 210001;
98
	private Position position;
Melledy's avatar
Melledy committed
99
	private Position rotation;
Miyucchi's avatar
Miyucchi committed
100
	private PlayerBirthday birthday;
101
	private PlayerCodex codex;
Melledy's avatar
Melledy committed
102
103
	private boolean showAvatars;
	private List<Integer> showAvatarList;
Melledy's avatar
Melledy committed
104
	private Map<Integer, Integer> properties;
Melledy's avatar
Melledy committed
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
	private int currentRealmId;
	private int widgetId;
	private int sceneId;
    private int regionId;
    private int mainCharacterId;
    private boolean godmode;
    private boolean stamina;
    
    @Getter private Set<Integer> nameCardList;
    @Getter private Set<Integer> flyCloakList;
    @Getter private Set<Integer> costumeList;
    @Getter private Set<Integer> rewardedLevels;
    @Getter private Set<Integer> realmList;
    @Getter private Set<Integer> unlockedForgingBlueprints;
    @Getter private Set<Integer> unlockedCombines;
    @Getter private Set<Integer> unlockedFurniture;
    @Getter private Set<Integer> unlockedFurnitureSuite;
    @Getter private Map<Long, ExpeditionInfo> expeditionInfo;
    @Getter private Map<Integer, Integer> unlockedRecipies;
    @Getter private List<ActiveForgeData> activeForges;
125

Melledy's avatar
Melledy committed
126
127
128
	@Transient private long nextGuid = 0;
	@Transient private int peerId;
	@Transient private World world;
129
	@Transient private Scene scene;
AnimeGitB's avatar
AnimeGitB committed
130
131
	@Transient @Getter private int weatherId = 0;
	@Transient @Getter private ClimateType climate = ClimateType.CLIMATE_SUNNY;
Melledy's avatar
Melledy committed
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
	
	// Player managers go here
	@Getter private transient AvatarStorage avatars;
	@Getter private transient Inventory inventory;
	@Getter private transient FriendsList friendsList;
	@Getter private transient MailHandler mailHandler;
	@Getter private transient MessageHandler messageHandler;
	@Getter private transient AbilityManager abilityManager;
	@Getter private transient QuestManager questManager;
	@Getter private transient TowerManager towerManager;
	@Getter private transient SotSManager sotsManager;
    @Getter private transient MapMarksManager mapMarksManager;
    @Getter private transient StaminaManager staminaManager;
    @Getter private transient EnergyManager energyManager;
    @Getter private transient ResinManager resinManager;
    @Getter private transient ForgingManager forgingManager;
    @Getter private transient DeforestationManager deforestationManager;
    @Getter private transient FurnitureManager furnitureManager;
    @Getter private transient BattlePassManager battlePassManager;
    @Getter private transient CookingManager cookingManager;
    @Getter private transient ActivityManager activityManager;

    // Manager data (Save-able to the database)
    private PlayerProfile playerProfile;
    private TeamManager teamManager;
157
	private TowerData towerData;
Melledy's avatar
Melledy committed
158
	private PlayerGachaInfo gachaInfo;
Melledy's avatar
Melledy committed
159
	private PlayerOpenStateManager openStateManager;
160
	private PlayerCollectionRecords collectionRecordStore;
Kengxxiao's avatar
Kengxxiao committed
161
	private ArrayList<ShopLimit> shopLimit;
Melledy's avatar
Melledy committed
162
163
	
	@Getter private transient GameHome home;
AnimeGitB's avatar
AnimeGitB committed
164

Yazawazi's avatar
Yazawazi committed
165
166
167
168
169
	private boolean moonCard;
	private Date moonCardStartTime;
	private int moonCardDuration;
	private Set<Date> moonCardGetTimes;

Melledy's avatar
Melledy committed
170
171
172
173
	@Transient private boolean paused;
	@Transient private int enterSceneToken;
	@Transient private SceneLoadState sceneState;
	@Transient private boolean hasSentAvatarDataNotify;
Melledy's avatar
Melledy committed
174
	@Transient private long nextSendPlayerLocTime = 0;
Asnxthaony's avatar
Asnxthaony committed
175

Melledy's avatar
Melledy committed
176
177
178
179
180
	private transient final Int2ObjectMap<CoopRequest> coopRequests;
	private transient final Queue<AttackResult> attackResults;
	@Getter private transient final InvokeHandler<CombatInvokeEntry> combatInvokeHandler;
	@Getter private transient final InvokeHandler<AbilityInvokeEntry> abilityInvokeHandler;
	@Getter private transient final InvokeHandler<AbilityInvokeEntry> clientAbilityInitFinishHandler;
181

182
	private long springLastUsed;
gentlespoon's avatar
gentlespoon committed
183
	private HashMap<String, MapMark> mapMarks;
ImmuState's avatar
ImmuState committed
184
	private int nextResinRefresh;
185
	private int lastDailyReset;
186

Asnxthaony's avatar
Asnxthaony committed
187
188
	@Deprecated
	@SuppressWarnings({"rawtypes", "unchecked"}) // Morphia only!
189
	public Player() {
Melledy's avatar
Melledy committed
190
191
192
		this.inventory = new Inventory(this);
		this.avatars = new AvatarStorage(this);
		this.friendsList = new FriendsList(this);
193
		this.mailHandler = new MailHandler(this);
194
		this.towerManager = new TowerManager(this);
Melledy's avatar
Melledy committed
195
		this.abilityManager = new AbilityManager(this);
196
		this.deforestationManager = new DeforestationManager(this);
Melledy's avatar
Melledy committed
197
		this.questManager = new QuestManager(this);
198
199
200
201
		this.position = new Position(GameConstants.START_POSITION);
		this.rotation = new Position(0, 307, 0);
		this.sceneId = 3;
        this.regionId = 1;
Melledy's avatar
Melledy committed
202
203
204
205
206
207
208
		this.properties = new HashMap<>();
		for (PlayerProperty prop : PlayerProperty.values()) {
			if (prop.getId() < 10000) {
				continue;
			}
			this.properties.put(prop.getId(), 0);
		}
Asnxthaony's avatar
Asnxthaony committed
209

210
211
212
213
		this.gachaInfo = new PlayerGachaInfo();
		this.nameCardList = new HashSet<>();
		this.flyCloakList = new HashSet<>();
		this.costumeList = new HashSet<>();
214
		this.towerData = new TowerData();
215
		this.collectionRecordStore = new PlayerCollectionRecords();
216
		this.unlockedForgingBlueprints = new HashSet<>();
ImmuState's avatar
ImmuState committed
217
		this.unlockedCombines = new HashSet<>();
Akka's avatar
Akka committed
218
219
		this.unlockedFurniture = new HashSet<>();
		this.unlockedFurnitureSuite = new HashSet<>();
220
		this.activeForges = new ArrayList<>();
221
		this.unlockedRecipies = new HashMap<>();
Melledy's avatar
Melledy committed
222
		this.sceneState = SceneLoadState.NONE;
Asnxthaony's avatar
Asnxthaony committed
223

224
		this.attackResults = new LinkedBlockingQueue<>();
Melledy's avatar
Melledy committed
225
226
227
		this.coopRequests = new Int2ObjectOpenHashMap<>();
		this.combatInvokeHandler = new InvokeHandler(PacketCombatInvocationsNotify.class);
		this.abilityInvokeHandler = new InvokeHandler(PacketAbilityInvocationsNotify.class);
228
		this.clientAbilityInitFinishHandler = new InvokeHandler(PacketClientAbilityInitFinishNotify.class);
Miyucchi's avatar
Miyucchi committed
229
230

		this.birthday = new PlayerBirthday();
231
		this.rewardedLevels = new HashSet<>();
Yazawazi's avatar
Yazawazi committed
232
		this.moonCardGetTimes = new HashSet<>();
233
		this.codex = new PlayerCodex(this);
akatatsu27's avatar
akatatsu27 committed
234
        this.openStateManager = new PlayerOpenStateManager(this);
Kengxxiao's avatar
Kengxxiao committed
235
		this.shopLimit = new ArrayList<>();
Kinesis's avatar
Kinesis committed
236
		this.expeditionInfo = new HashMap<>();
237
		this.messageHandler = null;
gentlespoon's avatar
gentlespoon committed
238
		this.mapMarksManager = new MapMarksManager(this);
239
		this.staminaManager = new StaminaManager(this);
240
		this.sotsManager = new SotSManager(this);
241
		this.energyManager = new EnergyManager(this);
ImmuState's avatar
ImmuState committed
242
		this.resinManager = new ResinManager(this);
243
		this.forgingManager = new ForgingManager(this);
Akka's avatar
Akka committed
244
		this.furnitureManager = new FurnitureManager(this);
245
		this.cookingManager = new CookingManager(this);
Melledy's avatar
Melledy committed
246
	}
Asnxthaony's avatar
Asnxthaony committed
247

Melledy's avatar
Melledy committed
248
	// On player creation
249
	public Player(GameSession session) {
Melledy's avatar
Melledy committed
250
251
252
253
254
255
256
		this();
		this.account = session.getAccount();
		this.accountId = this.getAccount().getId();
		this.session = session;
		this.nickname = "Traveler";
		this.signature = "";
		this.teamManager = new TeamManager(this);
Miyucchi's avatar
Miyucchi committed
257
		this.birthday = new PlayerBirthday();
258
		this.codex = new PlayerCodex(this);
AnimeGitB's avatar
AnimeGitB committed
259
260
261
262
263
264
265
266
		this.setProperty(PlayerProperty.PROP_PLAYER_LEVEL, 1, false);
		this.setProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE, 1, false);
		this.setProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT, 50, false);
		this.setProperty(PlayerProperty.PROP_IS_FLYABLE, 1, false);
		this.setProperty(PlayerProperty.PROP_IS_TRANSFERABLE, 1, false);
		this.setProperty(PlayerProperty.PROP_MAX_STAMINA, 24000, false);
		this.setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, 24000, false);
		this.setProperty(PlayerProperty.PROP_PLAYER_RESIN, 160, false);
Melledy's avatar
Melledy committed
267
268
		this.getFlyCloakList().add(140001);
		this.getNameCardList().add(210001);
269
		this.messageHandler = null;
gentlespoon's avatar
gentlespoon committed
270
		this.mapMarksManager = new MapMarksManager(this);
271
		this.staminaManager = new StaminaManager(this);
272
		this.sotsManager = new SotSManager(this);
273
		this.energyManager = new EnergyManager(this);
ImmuState's avatar
ImmuState committed
274
		this.resinManager = new ResinManager(this);
275
		this.deforestationManager = new DeforestationManager(this);
276
		this.forgingManager = new ForgingManager(this);
Akka's avatar
Akka committed
277
		this.furnitureManager = new FurnitureManager(this);
278
		this.cookingManager = new CookingManager(this);
Melledy's avatar
Melledy committed
279
280
	}

281
	public int getUid() {
Melledy's avatar
Melledy committed
282
283
284
		return id;
	}

285
	public void setUid(int id) {
Melledy's avatar
Melledy committed
286
287
		this.id = id;
	}
Asnxthaony's avatar
Asnxthaony committed
288

289
	public long getNextGameGuid() {
Melledy's avatar
Melledy committed
290
		long nextId = ++this.nextGuid;
291
		return ((long) this.getUid() << 32) + nextId;
Melledy's avatar
Melledy committed
292
293
294
	}

	public Account getAccount() {
295
296
297
		if (this.account == null)
			this.account = DatabaseHelper.getAccountById(Integer.toString(this.id));
		return this.account;
Melledy's avatar
Melledy committed
298
299
300
301
302
303
304
305
306
307
308
309
310
	}

	public void setAccount(Account account) {
		this.account = account;
	}

	public GameSession getSession() {
		return session;
	}

	public void setSession(GameSession session) {
		this.session = session;
	}
Asnxthaony's avatar
Asnxthaony committed
311

Melledy's avatar
Melledy committed
312
313
314
	public boolean isOnline() {
		return this.getSession() != null && this.getSession().isActive();
	}
Asnxthaony's avatar
Asnxthaony committed
315

Melledy's avatar
Melledy committed
316
317
318
	public GameServer getServer() {
		return this.getSession().getServer();
	}
Asnxthaony's avatar
Asnxthaony committed
319

Melledy's avatar
Melledy committed
320
321
322
	public synchronized World getWorld() {
		return this.world;
	}
Asnxthaony's avatar
Asnxthaony committed
323

Melledy's avatar
Melledy committed
324
325
326
	public synchronized void setWorld(World world) {
		this.world = world;
	}
Asnxthaony's avatar
Asnxthaony committed
327

328
	public synchronized Scene getScene() {
329
330
331
		return scene;
	}

332
	public synchronized void setScene(Scene scene) {
333
334
335
		this.scene = scene;
	}

AnimeGitB's avatar
AnimeGitB committed
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
	synchronized public void setClimate(ClimateType climate) {
		this.climate = climate;
		this.session.send(new PacketSceneAreaWeatherNotify(this));
	}

	synchronized public void setWeather(int weather) {
		this.setWeather(weather, ClimateType.CLIMATE_NONE);
	}

	synchronized public void setWeather(int weatherId, ClimateType climate) {
		// Lookup default climate for this weather
		if (climate == ClimateType.CLIMATE_NONE) {
			WeatherData w = GameData.getWeatherDataMap().get(weatherId);
			if (w != null) {
				climate = w.getDefaultClimate();
			}
		}
		this.weatherId = weatherId;
		this.climate = climate;
		this.session.send(new PacketSceneAreaWeatherNotify(this));
	}

Melledy's avatar
Melledy committed
358
359
360
361
362
363
364
365
	public String getNickname() {
		return nickname;
	}

	public void setNickname(String nickName) {
		this.nickname = nickName;
		this.updateProfile();
	}
Asnxthaony's avatar
Asnxthaony committed
366

Melledy's avatar
Melledy committed
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
	public int getHeadImage() {
		return headImage;
	}

	public void setHeadImage(int picture) {
		this.headImage = picture;
		this.updateProfile();
	}

	public String getSignature() {
		return signature;
	}

	public void setSignature(String signature) {
		this.signature = signature;
		this.updateProfile();
	}

385
386
387
388
389
390
391
392
	public Integer getWidgetId() {
		return widgetId;
	}

	public void setWidgetId(Integer widgetId) {
		this.widgetId = widgetId;
	}

393
394
395
396
397
398
399
400
401
402
403
404
405
	public void setRealmList(Set<Integer> realmList) {
		this.realmList = realmList;
	}

	public void addRealmList(int realmId) {
		if (this.realmList == null) {
			this.realmList = new HashSet<>();
		} else if (this.realmList.contains(realmId)) {
			return;
		}
		this.realmList.add(realmId);
	}

Melledy's avatar
Melledy committed
406
	public int getCurrentRealmId() {
407
408
409
		return currentRealmId;
	}

Melledy's avatar
Melledy committed
410
	public void setCurrentRealmId(int currentRealmId) {
411
412
		this.currentRealmId = currentRealmId;
	}
Melledy's avatar
Melledy committed
413
	
414
415
	public Position getPosition() {
		return position;
Melledy's avatar
Melledy committed
416
	}
Asnxthaony's avatar
Asnxthaony committed
417

Melledy's avatar
Melledy committed
418
419
420
	public Position getRotation() {
		return rotation;
	}
Asnxthaony's avatar
Asnxthaony committed
421

Melledy's avatar
Melledy committed
422
423
424
	public int getLevel() {
		return this.getProperty(PlayerProperty.PROP_PLAYER_LEVEL);
	}
Asnxthaony's avatar
Asnxthaony committed
425

426
	public boolean setLevel(int level) {
427
428
429
430
		if (this.getLevel() == level) {
			return true;
		}

431
		if (this.setProperty(PlayerProperty.PROP_PLAYER_LEVEL, level)) {
432
			// Update world level and profile.
433
434
			this.updateWorldLevel();
			this.updateProfile();
435
436

			// Handle OpenState unlocks from level-up.
437
			this.getOpenStateManager().unlockLevelDependentStates();
438

439
440
441
			return true;
		}
		return false;
442
443
	}

Melledy's avatar
Melledy committed
444
445
446
	public int getExp() {
		return this.getProperty(PlayerProperty.PROP_PLAYER_EXP);
	}
Asnxthaony's avatar
Asnxthaony committed
447

Melledy's avatar
Melledy committed
448
449
450
	public int getWorldLevel() {
		return this.getProperty(PlayerProperty.PROP_PLAYER_WORLD_LEVEL);
	}
akatatsu27's avatar
akatatsu27 committed
451

452
453
454
455
456
457
458
459
	public boolean setWorldLevel(int level) {
		if (this.setProperty(PlayerProperty.PROP_PLAYER_WORLD_LEVEL, level)) {
			if (this.world.getHost() == this)  // Don't update World's WL if we are in someone else's world
            	this.world.setWorldLevel(level);
			this.updateProfile();
			return true;
		}
		return false;
460
	}
Asnxthaony's avatar
Asnxthaony committed
461

462
463
464
465
	public int getForgePoints() {
		return this.getProperty(PlayerProperty.PROP_PLAYER_FORGE_POINT);
	}

466
	public boolean setForgePoints(int value) {
467
		if (value == this.getForgePoints()) {
468
			return true;
469
470
		}

471
		return this.setProperty(PlayerProperty.PROP_PLAYER_FORGE_POINT, value);
472
473
	}

Melledy's avatar
Melledy committed
474
475
476
	public int getPrimogems() {
		return this.getProperty(PlayerProperty.PROP_PLAYER_HCOIN);
	}
Asnxthaony's avatar
Asnxthaony committed
477

478
479
	public boolean setPrimogems(int primogem) {
		return this.setProperty(PlayerProperty.PROP_PLAYER_HCOIN, primogem);
Melledy's avatar
Melledy committed
480
	}
Asnxthaony's avatar
Asnxthaony committed
481

Melledy's avatar
Melledy committed
482
483
484
	public int getMora() {
		return this.getProperty(PlayerProperty.PROP_PLAYER_SCOIN);
	}
Asnxthaony's avatar
Asnxthaony committed
485

486
487
	public boolean setMora(int mora) {
		return this.setProperty(PlayerProperty.PROP_PLAYER_SCOIN, mora);
Melledy's avatar
Melledy committed
488
	}
KingRainbow44's avatar
KingRainbow44 committed
489

490
491
492
493
	public int getCrystals() {
		return this.getProperty(PlayerProperty.PROP_PLAYER_MCOIN);
	}

494
495
	public boolean setCrystals(int crystals) {
		return this.setProperty(PlayerProperty.PROP_PLAYER_MCOIN, crystals);
496
	}
Asnxthaony's avatar
Asnxthaony committed
497

498
499
500
501
	public int getHomeCoin() {
		return this.getProperty(PlayerProperty.PROP_PLAYER_HOME_COIN);
	}

502
503
	public boolean setHomeCoin(int coin) {
		return this.setProperty(PlayerProperty.PROP_PLAYER_HOME_COIN, coin);
504
	}
Melledy's avatar
Melledy committed
505
	private int getExpRequired(int level) {
506
		PlayerLevelData levelData = GameData.getPlayerLevelDataMap().get(level);
Asnxthaony's avatar
Asnxthaony committed
507
		return levelData != null ? levelData.getExp() : 0;
Melledy's avatar
Melledy committed
508
	}
Asnxthaony's avatar
Asnxthaony committed
509

Melledy's avatar
Melledy committed
510
	private float getExpModifier() {
511
		return GAME_OPTIONS.rates.adventureExp;
Melledy's avatar
Melledy committed
512
	}
Asnxthaony's avatar
Asnxthaony committed
513

Melledy's avatar
Melledy committed
514
515
516
517
	// Affected by exp rate
	public void earnExp(int exp) {
		addExpDirectly((int) (exp * getExpModifier()));
	}
Asnxthaony's avatar
Asnxthaony committed
518

Melledy's avatar
Melledy committed
519
520
521
522
523
	// Directly give player exp
	public void addExpDirectly(int gain) {
		int level = getLevel();
		int exp = getExp();
		int reqExp = getExpRequired(level);
Asnxthaony's avatar
Asnxthaony committed
524

Melledy's avatar
Melledy committed
525
		exp += gain;
Asnxthaony's avatar
Asnxthaony committed
526

Melledy's avatar
Melledy committed
527
528
529
530
		while (exp >= reqExp && reqExp > 0) {
			exp -= reqExp;
			level += 1;
			reqExp = getExpRequired(level);
Asnxthaony's avatar
Asnxthaony committed
531

532
			// Set level each time to allow level-up specific logic to run.
533
			this.setLevel(level);
Melledy's avatar
Melledy committed
534
		}
Asnxthaony's avatar
Asnxthaony committed
535

Melledy's avatar
Melledy committed
536
537
538
		// Set exp
		this.setProperty(PlayerProperty.PROP_PLAYER_EXP, exp);
	}
Asnxthaony's avatar
Asnxthaony committed
539

540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
	private void updateWorldLevel() {
		int currentWorldLevel = this.getWorldLevel();
		int currentLevel = this.getLevel();

		int newWorldLevel =
			(currentLevel >= 55) ? 8 :
			(currentLevel >= 50) ? 7 :
			(currentLevel >= 45) ? 6 :
			(currentLevel >= 40) ? 5 :
			(currentLevel >= 35) ? 4 :
			(currentLevel >= 30) ? 3 :
			(currentLevel >= 25) ? 2 :
			(currentLevel >= 20) ? 1 :
			0;

		if (newWorldLevel != currentWorldLevel) {
			this.setWorldLevel(newWorldLevel);
		}
	}

Melledy's avatar
Melledy committed
560
561
562
	private void updateProfile() {
		getProfile().syncWithCharacter(this);
	}
Asnxthaony's avatar
Asnxthaony committed
563

Melledy's avatar
Melledy committed
564
565
566
567
568
569
570
	public boolean isFirstLoginEnterScene() {
		return !this.hasSentAvatarDataNotify;
	}

	public TeamManager getTeamManager() {
		return this.teamManager;
	}
KingRainbow44's avatar
KingRainbow44 committed
571

572
	public TowerData getTowerData() {
573
		if (towerData == null) {
574
575
576
			// because of mistake, null may be saved as storage at some machine, this if can be removed in future
			towerData = new TowerData();
		}
577
578
		return towerData;
	}
KingRainbow44's avatar
KingRainbow44 committed
579

Melledy's avatar
Melledy committed
580
581
582
583
584
585
586
587
588
589
590
	public PlayerGachaInfo getGachaInfo() {
		return gachaInfo;
	}

	public PlayerProfile getProfile() {
		if (this.playerProfile == null) {
			this.playerProfile = new PlayerProfile(this);
		}
		return playerProfile;
	}

591
592
	// TODO: Based on the proto, property value could be int or float.
	//  Although there's no float value at this moment, our code should be prepared for float values.
Melledy's avatar
Melledy committed
593
594
595
	public Map<Integer, Integer> getProperties() {
		return properties;
	}
Asnxthaony's avatar
Asnxthaony committed
596

597
	public boolean setProperty(PlayerProperty prop, int value) {
AnimeGitB's avatar
AnimeGitB committed
598
599
600
601
602
		return setPropertyWithSanityCheck(prop, value, true);
	}

	public boolean setProperty(PlayerProperty prop, int value, boolean sendPacket) {
		return setPropertyWithSanityCheck(prop, value, sendPacket);
Melledy's avatar
Melledy committed
603
	}
Asnxthaony's avatar
Asnxthaony committed
604

Melledy's avatar
Melledy committed
605
606
607
608
609
	public int getProperty(PlayerProperty prop) {
		return getProperties().get(prop.getId());
	}

	public MpSettingType getMpSetting() {
610
		return MpSettingType.MP_SETTING_TYPE_ENTER_AFTER_APPLY; // TEMP
Melledy's avatar
Melledy committed
611
	}
KingRainbow44's avatar
KingRainbow44 committed
612

613
614
615
	public Queue<AttackResult> getAttackResults() {
		return this.attackResults;
	}
Asnxthaony's avatar
Asnxthaony committed
616

Melledy's avatar
Melledy committed
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
	public synchronized Int2ObjectMap<CoopRequest> getCoopRequests() {
		return coopRequests;
	}

	public int getEnterSceneToken() {
		return enterSceneToken;
	}

	public void setEnterSceneToken(int enterSceneToken) {
		this.enterSceneToken = enterSceneToken;
	}

	public int getNameCardId() {
		return nameCardId;
	}

	public void setNameCardId(int nameCardId) {
		this.nameCardId = nameCardId;
		this.updateProfile();
	}

	public int getMainCharacterId() {
		return mainCharacterId;
	}

	public void setMainCharacterId(int mainCharacterId) {
		this.mainCharacterId = mainCharacterId;
	}

	public int getPeerId() {
		return peerId;
	}

	public void setPeerId(int peerId) {
		this.peerId = peerId;
	}

	public int getClientTime() {
		return session.getClientTime();
	}

	public long getLastPingTime() {
		return session.getLastPingTime();
	}

	public boolean isPaused() {
		return paused;
	}

	public void setPaused(boolean newPauseState) {
		boolean oldPauseState = this.paused;
		this.paused = newPauseState;
Asnxthaony's avatar
Asnxthaony committed
669

Melledy's avatar
Melledy committed
670
671
672
673
674
675
676
		if (newPauseState && !oldPauseState) {
			this.onPause();
		} else if (oldPauseState && !newPauseState) {
			this.onUnpause();
		}
	}

677
678
679
680
681
682
683
684
	public long getSpringLastUsed() {
		return springLastUsed;
	}

	public void setSpringLastUsed(long val) {
		springLastUsed = val;
	}

ImmuState's avatar
ImmuState committed
685
686
687
688
689
690
691
692
	public int getNextResinRefresh() {
		return nextResinRefresh;
	}

	public void setNextResinRefresh(int value) {
		this.nextResinRefresh = value;
	}

Melledy's avatar
Melledy committed
693
694
695
696
697
698
699
	public SceneLoadState getSceneLoadState() {
		return sceneState;
	}

	public void setSceneLoadState(SceneLoadState sceneState) {
		this.sceneState = sceneState;
	}
Asnxthaony's avatar
Asnxthaony committed
700

Melledy's avatar
Melledy committed
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
	public boolean isInMultiplayer() {
		return this.getWorld() != null && this.getWorld().isMultiplayer();
	}

	public int getSceneId() {
		return sceneId;
	}

	public void setSceneId(int sceneId) {
		this.sceneId = sceneId;
	}

	public int getRegionId() {
		return regionId;
	}

	public void setRegionId(int regionId) {
		this.regionId = regionId;
	}
Asnxthaony's avatar
Asnxthaony committed
720

Yazawazi's avatar
Yazawazi committed
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
	public void setShowAvatars(boolean showAvatars) {
		this.showAvatars = showAvatars;
	}

	public boolean isShowAvatars() {
		return showAvatars;
	}

	public void setShowAvatarList(List<Integer> showAvatarList) {
		this.showAvatarList = showAvatarList;
	}

	public List<Integer> getShowAvatarList() {
		return showAvatarList;
	}

737
738
739
740
741
742
743
744
	public int getLastDailyReset() {
		return this.lastDailyReset;
	}

	public void setLastDailyReset(int value) {
		this.lastDailyReset = value;
	}

Yazawazi's avatar
Yazawazi committed
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
	public boolean inMoonCard() {
		return moonCard;
	}

	public void setMoonCard(boolean moonCard) {
		this.moonCard = moonCard;
	}

	public void addMoonCardDays(int days) {
		this.moonCardDuration += days;
	}

	public int getMoonCardDuration() {
		return moonCardDuration;
	}

	public void setMoonCardDuration(int moonCardDuration) {
		this.moonCardDuration = moonCardDuration;
	}

	public Date getMoonCardStartTime() {
		return moonCardStartTime;
	}

	public void setMoonCardStartTime(Date moonCardStartTime) {
		this.moonCardStartTime = moonCardStartTime;
	}

	public Set<Date> getMoonCardGetTimes() {
		return moonCardGetTimes;
	}

	public void setMoonCardGetTimes(Set<Date> moonCardGetTimes) {
		this.moonCardGetTimes = moonCardGetTimes;
	}

	public int getMoonCardRemainDays() {
		Calendar remainCalendar = Calendar.getInstance();
		remainCalendar.setTime(moonCardStartTime);
		remainCalendar.add(Calendar.DATE, moonCardDuration);
		Date theLastDay = remainCalendar.getTime();
Yazawazi's avatar
Yazawazi committed
786
		Date now = DateHelper.onlyYearMonthDay(new Date());
KingRainbow44's avatar
KingRainbow44 committed
787
		return (int) ((theLastDay.getTime() - now.getTime()) / (24 * 60 * 60 * 1000)); // By copilot
Yazawazi's avatar
Yazawazi committed
788
789
790
	}

	public void rechargeMoonCard() {
791
		inventory.addItem(new GameItem(203, 300));
Yazawazi's avatar
Yazawazi committed
792
793
794
		if (!moonCard) {
			moonCard = true;
			Date now = new Date();
Yazawazi's avatar
Yazawazi committed
795
			moonCardStartTime = DateHelper.onlyYearMonthDay(now);
Yazawazi's avatar
Yazawazi committed
796
797
798
799
800
801
802
803
804
805
806
807
808
			moonCardDuration = 30;
		} else {
			moonCardDuration += 30;
		}
		if (!moonCardGetTimes.contains(moonCardStartTime)) {
			moonCardGetTimes.add(moonCardStartTime);
		}
	}

	public void getTodayMoonCard() {
		if (!moonCard) {
			return;
		}
Yazawazi's avatar
Yazawazi committed
809
		Date now = DateHelper.onlyYearMonthDay(new Date());
Yazawazi's avatar
Yazawazi committed
810
811
812
813
814
815
816
817
818
819
820
821
822
823
		if (moonCardGetTimes.contains(now)) {
			return;
		}
		Date stopTime = new Date();
		Calendar stopCalendar = Calendar.getInstance();
		stopCalendar.setTime(stopTime);
		stopCalendar.add(Calendar.DATE, moonCardDuration);
		stopTime = stopCalendar.getTime();
		if (now.after(stopTime)) {
			moonCard = false;
			return;
		}
		moonCardGetTimes.add(now);
		addMoonCardDays(1);
824
825
		GameItem item = new GameItem(201, 90);
		getInventory().addItem(item, ActionReason.BlessingRedeemReward);
Yazawazi's avatar
Yazawazi committed
826
827
828
		session.send(new PacketCardProductRewardNotify(getMoonCardRemainDays()));
	}

Kinesis's avatar
Kinesis committed
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
	public void addExpeditionInfo(long avaterGuid, int expId, int hourTime, int startTime){
		ExpeditionInfo exp = new ExpeditionInfo();
		exp.setExpId(expId);
		exp.setHourTime(hourTime);
		exp.setState(1);
		exp.setStartTime(startTime);
		expeditionInfo.put(avaterGuid, exp);
	}

	public void removeExpeditionInfo(long avaterGuid){
		expeditionInfo.remove(avaterGuid);
	}

	public ExpeditionInfo getExpeditionInfo(long avaterGuid){
		return expeditionInfo.get(avaterGuid);
	}

Kengxxiao's avatar
Kengxxiao committed
846
847
848
849
	public List<ShopLimit> getShopLimit() {
		return shopLimit;
	}

Kengxxiao's avatar
Kengxxiao committed
850
851
852
853
854
	public ShopLimit getGoodsLimit(int goodsId) {
		Optional<ShopLimit> shopLimit = this.shopLimit.stream().filter(x -> x.getShopGoodId() == goodsId).findFirst();
		if (shopLimit.isEmpty())
			return null;
		return shopLimit.get();
Kengxxiao's avatar
Kengxxiao committed
855
856
	}

Kengxxiao's avatar
Kengxxiao committed
857
858
859
860
861
862
863
	public void addShopLimit(int goodsId, int boughtCount, int nextRefreshTime) {
		ShopLimit target = getGoodsLimit(goodsId);
		if (target != null) {
			target.setHasBought(target.getHasBought() + boughtCount);
			target.setHasBoughtInPeriod(target.getHasBoughtInPeriod() + boughtCount);
			target.setNextRefreshTime(nextRefreshTime);
		} else {
Kengxxiao's avatar
Kengxxiao committed
864
865
866
			ShopLimit sl = new ShopLimit();
			sl.setShopGoodId(goodsId);
			sl.setHasBought(boughtCount);
Kengxxiao's avatar
Kengxxiao committed
867
868
869
			sl.setHasBoughtInPeriod(boughtCount);
			sl.setNextRefreshTime(nextRefreshTime);
			getShopLimit().add(sl);
Kengxxiao's avatar
Kengxxiao committed
870
871
872
		}
		this.save();
	}
873
	public boolean getUnlimitedStamina() {
874
875
		return stamina;
	}
876
	public void setUnlimitedStamina(boolean stamina) {
877
878
		this.stamina = stamina;
	}
KingRainbow44's avatar
KingRainbow44 committed
879
	public boolean inGodmode() {
Melledy's avatar
Melledy committed
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
		return godmode;
	}

	public void setGodmode(boolean godmode) {
		this.godmode = godmode;
	}

	public boolean hasSentAvatarDataNotify() {
		return hasSentAvatarDataNotify;
	}

	public void setHasSentAvatarDataNotify(boolean hasSentAvatarDataNotify) {
		this.hasSentAvatarDataNotify = hasSentAvatarDataNotify;
	}

ProxyismGH's avatar
ProxyismGH committed
895
	public void addAvatar(Avatar avatar, boolean addToCurrentTeam) {
Melledy's avatar
Melledy committed
896
		boolean result = getAvatars().addAvatar(avatar);
Asnxthaony's avatar
Asnxthaony committed
897

Melledy's avatar
Melledy committed
898
899
900
		if (result) {
			// Add starting weapon
			getAvatars().addStartingWeapon(avatar);
Asnxthaony's avatar
Asnxthaony committed
901

Melledy's avatar
Melledy committed
902
903
904
905
			// Done
			if (hasSentAvatarDataNotify()) {
				// Recalc stats
				avatar.recalcStats();
ProxyismGH's avatar
ProxyismGH committed
906
907
908
909
910
911
				// Packet, show notice on left if the avatar will be added to the team
				sendPacket(new PacketAvatarAddNotify(avatar, addToCurrentTeam && this.getTeamManager().canAddAvatarToCurrentTeam()));
				if (addToCurrentTeam) {
					// If space in team, add
					this.getTeamManager().addAvatarToCurrentTeam(avatar);
				}
Melledy's avatar
Melledy committed
912
913
914
915
916
			}
		} else {
			// Failed adding avatar
		}
	}
Asnxthaony's avatar
Asnxthaony committed
917

ProxyismGH's avatar
ProxyismGH committed
918
919
920
921
	public void addAvatar(Avatar avatar) {
		addAvatar(avatar, true);
	}

Melledy's avatar
Melledy committed
922
923
924
925
	public void addFlycloak(int flycloakId) {
		this.getFlyCloakList().add(flycloakId);
		this.sendPacket(new PacketAvatarGainFlycloakNotify(flycloakId));
	}
Asnxthaony's avatar
Asnxthaony committed
926

Melledy's avatar
Melledy committed
927
928
929
930
	public void addCostume(int costumeId) {
		this.getCostumeList().add(costumeId);
		this.sendPacket(new PacketAvatarGainCostumeNotify(costumeId));
	}
Asnxthaony's avatar
Asnxthaony committed
931

Melledy's avatar
Melledy committed
932
933
934
935
	public void addNameCard(int nameCardId) {
		this.getNameCardList().add(nameCardId);
		this.sendPacket(new PacketUnlockNameCardNotify(nameCardId));
	}
Asnxthaony's avatar
Asnxthaony committed
936

Melledy's avatar
Melledy committed
937
938
939
940
	public void setNameCard(int nameCardId) {
		if (!this.getNameCardList().contains(nameCardId)) {
			return;
		}
Asnxthaony's avatar
Asnxthaony committed
941

Melledy's avatar
Melledy committed
942
		this.setNameCardId(nameCardId);
Asnxthaony's avatar
Asnxthaony committed
943

Melledy's avatar
Melledy committed
944
945
		this.sendPacket(new PacketSetNameCardRsp(nameCardId));
	}
Asnxthaony's avatar
Asnxthaony committed
946

Melledy's avatar
Melledy committed
947
	public void dropMessage(Object message) {
948
949
950
951
		if (this.messageHandler != null) {
			this.messageHandler.append(message.toString());
			return;
		}
952
953
954

		this.getServer().getChatManager().sendPrivateMessageFromServer(getUid(), message.toString());
		// this.sendPacket(new PacketPrivateChatNotify(GameConstants.SERVER_CONSOLE_UID, getUid(), message.toString()));
Melledy's avatar
Melledy committed
955
	}
Melledy's avatar
Melledy committed
956
957
958

	/**
	 * Sends a message to another player.
Asnxthaony's avatar
Asnxthaony committed
959
960
	 *
	 * @param sender  The sender of the message.
Melledy's avatar
Melledy committed
961
962
	 * @param message The message to send.
	 */
963
	public void sendMessage(Player sender, Object message) {
964
965
		// this.sendPacket(new PacketPrivateChatNotify(sender.getUid(), this.getUid(), message.toString()));
		this.getServer().getChatManager().sendPrivateMessage(sender, this.getUid(), message.toString());
Melledy's avatar
Melledy committed
966
	}
Asnxthaony's avatar
Asnxthaony committed
967

Benjamin Elsdon's avatar
Benjamin Elsdon committed
968
	// ---------------------MAIL------------------------
969

970
	public List<Mail> getAllMail() { return this.getMailHandler().getMail(); }
971

Benjamin Elsdon's avatar
Benjamin Elsdon committed
972
	public void sendMail(Mail message) {
973
		this.getMailHandler().sendMail(message);
974
	}
Benjamin Elsdon's avatar
Benjamin Elsdon committed
975
976

	public boolean deleteMail(int mailId) {
977
		return this.getMailHandler().deleteMail(mailId);
Benjamin Elsdon's avatar
Benjamin Elsdon committed
978
979
	}

980
	public Mail getMail(int index) { return this.getMailHandler().getMailById(index); }
KingRainbow44's avatar
KingRainbow44 committed
981

982
	public int getMailId(Mail message) {
983
		return this.getMailHandler().getMailIndex(message);
Benjamin Elsdon's avatar
Benjamin Elsdon committed
984
985
	}

986
	public boolean replaceMailByIndex(int index, Mail message) {
987
		return this.getMailHandler().replaceMailByIndex(index, message);
Benjamin Elsdon's avatar
Benjamin Elsdon committed
988
	}
989

Melledy's avatar
Melledy committed
990
991
992
993
	public void interactWith(int gadgetEntityId, GadgetInteractReq interactReq) {
		GameEntity target = getScene().getEntityById(gadgetEntityId);
		
		if (target == null) {
Melledy's avatar
Melledy committed
994
995
			return;
		}
Melledy's avatar
Melledy committed
996
997
		
		target.onInteract(this, interactReq);
Melledy's avatar
Melledy committed
998
	}
Asnxthaony's avatar
Asnxthaony committed
999

Melledy's avatar
Melledy committed
1000
	public void onPause() {
1001
		getStaminaManager().stopSustainedStaminaHandler();
Melledy's avatar
Melledy committed
1002
	}
Asnxthaony's avatar
Asnxthaony committed
1003

Melledy's avatar
Melledy committed
1004
	public void onUnpause() {
1005
		getStaminaManager().startSustainedStaminaHandler();
Melledy's avatar
Melledy committed
1006
	}
Asnxthaony's avatar
Asnxthaony committed
1007

1008
	public void sendPacket(BasePacket packet) {
Melledy's avatar
Melledy committed
1009
		this.getSession().send(packet);
Melledy's avatar
Melledy committed
1010
	}
Asnxthaony's avatar
Asnxthaony committed
1011

Melledy's avatar
Melledy committed
1012
1013
	public OnlinePlayerInfo getOnlinePlayerInfo() {
		OnlinePlayerInfo.Builder onlineInfo = OnlinePlayerInfo.newBuilder()
Asnxthaony's avatar
Asnxthaony committed
1014
1015
1016
1017
1018
1019
				.setUid(this.getUid())
				.setNickname(this.getNickname())
				.setPlayerLevel(this.getLevel())
				.setMpSettingType(this.getMpSetting())
				.setNameCardId(this.getNameCardId())
				.setSignature(this.getSignature())
Melledy's avatar
Melledy committed
1020
				.setProfilePicture(ProfilePicture.newBuilder().setAvatarId(this.getHeadImage()));
Asnxthaony's avatar
Asnxthaony committed
1021

Melledy's avatar
Melledy committed
1022
		if (this.getWorld() != null) {
Melledy's avatar
Melledy committed
1023
			onlineInfo.setCurPlayerNumInWorld(getWorld().getPlayerCount());
Melledy's avatar
Melledy committed
1024
1025
1026
		} else {
			onlineInfo.setCurPlayerNumInWorld(1);
		}
Asnxthaony's avatar
Asnxthaony committed
1027

Melledy's avatar
Melledy committed
1028
1029
1030
		return onlineInfo.build();
	}

Asnxthaony's avatar
Asnxthaony committed
1031
	public PlayerBirthday getBirthday() {
Miyucchi's avatar
Miyucchi committed
1032
1033
1034
1035
1036
1037
1038
1039
		return this.birthday;
	}

	public void setBirthday(int d, int m) {
		this.birthday = new PlayerBirthday(d, m);
		this.updateProfile();
	}

Asnxthaony's avatar
Asnxthaony committed
1040
1041
1042
1043
	public boolean hasBirthday() {
		return this.birthday.getDay() > 0;
	}

Melledy's avatar
Melledy committed
1044
1045
	public PlayerCodex getCodex() { 
	    return this.codex; 
1046
1047
1048
1049
1050
1051
	}

	public void setRewardedLevels(Set<Integer> rewardedLevels) {
		this.rewardedLevels = rewardedLevels;
	}

Melledy's avatar
Melledy committed
1052
	public SocialDetail.Builder getSocialDetail() {
Yazawazi's avatar
Yazawazi committed
1053
		List<SocialShowAvatarInfoOuterClass.SocialShowAvatarInfo> socialShowAvatarInfoList = new ArrayList<>();
Yazawazi's avatar
Yazawazi committed
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
		if (this.isOnline()) {
			if (this.getShowAvatarList() != null) {
				for (int avatarId : this.getShowAvatarList()) {
					socialShowAvatarInfoList.add(
							socialShowAvatarInfoList.size(),
							SocialShowAvatarInfoOuterClass.SocialShowAvatarInfo.newBuilder()
									.setAvatarId(avatarId)
									.setLevel(getAvatars().getAvatarById(avatarId).getLevel())
									.setCostumeId(getAvatars().getAvatarById(avatarId).getCostume())
									.build()
					);
				}
			}
		} else {
1068
1069
			List<Integer> showAvatarList = DatabaseHelper.getPlayerByUid(id).getShowAvatarList();
			AvatarStorage avatars = DatabaseHelper.getPlayerByUid(id).getAvatars();
Yazawazi's avatar
Yazawazi committed
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
			avatars.loadFromDatabase();
			if (showAvatarList != null) {
				for (int avatarId : showAvatarList) {
					socialShowAvatarInfoList.add(
							socialShowAvatarInfoList.size(),
							SocialShowAvatarInfoOuterClass.SocialShowAvatarInfo.newBuilder()
									.setAvatarId(avatarId)
									.setLevel(avatars.getAvatarById(avatarId).getLevel())
									.setCostumeId(avatars.getAvatarById(avatarId).getCostume())
									.build()
					);
				}
			}
Yazawazi's avatar
Yazawazi committed
1083
1084
		}

Asnxthaony's avatar
Asnxthaony committed
1085
1086
		SocialDetail.Builder social = SocialDetail.newBuilder()
				.setUid(this.getUid())
Yazawazi's avatar
Yazawazi committed
1087
				.setProfilePicture(ProfilePicture.newBuilder().setAvatarId(this.getHeadImage()))
Asnxthaony's avatar
Asnxthaony committed
1088
1089
1090
1091
1092
1093
				.setNickname(this.getNickname())
				.setSignature(this.getSignature())
				.setLevel(this.getLevel())
				.setBirthday(this.getBirthday().getFilledProtoWhenNotEmpty())
				.setWorldLevel(this.getWorldLevel())
				.setNameCardId(this.getNameCardId())
Yazawazi's avatar
Yazawazi committed
1094
1095
				.setIsShowAvatar(this.isShowAvatars())
				.addAllShowAvatarInfoList(socialShowAvatarInfoList)
Asnxthaony's avatar
Asnxthaony committed
1096
				.setFinishAchievementNum(0);
Melledy's avatar
Melledy committed
1097
1098
		return social;
	}
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108

	public List<ShowAvatarInfoOuterClass.ShowAvatarInfo> getShowAvatarInfoList() {
		List<ShowAvatarInfoOuterClass.ShowAvatarInfo> showAvatarInfoList = new ArrayList<>();

		Player player;
		boolean shouldRecalc;
		if (this.isOnline()) {
			player = this;
			shouldRecalc = false;
		} else {
1109
			player = DatabaseHelper.getPlayerByUid(id);
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
			player.getAvatars().loadFromDatabase();
			player.getInventory().loadFromDatabase();
			shouldRecalc = true;
		}

		List<Integer> showAvatarList = player.getShowAvatarList();
		AvatarStorage avatars = player.getAvatars();
		if (showAvatarList != null) {
			for (int avatarId : showAvatarList) {
				Avatar avatar = avatars.getAvatarById(avatarId);
				if (shouldRecalc) {
					avatar.recalcStats();
				}
				showAvatarInfoList.add(avatar.toShowAvatarInfoProto());
			}
		}
		return showAvatarInfoList;
	}
KingRainbow44's avatar
KingRainbow44 committed
1128

1129
1130
	public PlayerWorldLocationInfoOuterClass.PlayerWorldLocationInfo getWorldPlayerLocationInfo() {
		return PlayerWorldLocationInfoOuterClass.PlayerWorldLocationInfo.newBuilder()
Asnxthaony's avatar
Asnxthaony committed
1131
1132
1133
				.setSceneId(this.getSceneId())
				.setPlayerLoc(this.getPlayerLocationInfo())
				.build();
Melledy's avatar
Melledy committed
1134
	}
Asnxthaony's avatar
Asnxthaony committed
1135

Melledy's avatar
Melledy committed
1136
	public PlayerLocationInfo getPlayerLocationInfo() {
Asnxthaony's avatar
Asnxthaony committed
1137
1138
		return PlayerLocationInfo.newBuilder()
				.setUid(this.getUid())
1139
				.setPos(this.getPosition().toProto())
Asnxthaony's avatar
Asnxthaony committed
1140
1141
				.setRot(this.getRotation().toProto())
				.build();
Melledy's avatar
Melledy committed
1142
	}
Asnxthaony's avatar
Asnxthaony committed
1143

1144
1145
1146
	public void loadBattlePassManager() {
		if (this.battlePassManager != null) return;
		this.battlePassManager = DatabaseHelper.loadBattlePass(this);
1147
		this.battlePassManager.getMissions().values().removeIf(mission -> mission.getData() == null);
1148
	}
1149

1150
	public PlayerCollectionRecords getCollectionRecordStore() {
1151
		if(this.collectionRecordStore==null){
1152
			this.collectionRecordStore = new PlayerCollectionRecords();
1153
1154
1155
1156
		}
		return collectionRecordStore;
	}

Melledy's avatar
Melledy committed
1157
1158
1159
1160
1161
1162
	public Map<String, MapMark> getMapMarks() {
	    if (this.mapMarks == null) {
	        this.mapMarks = new HashMap<String, MapMark>();
	    }
	    return mapMarks; 
	}
gentlespoon's avatar
gentlespoon committed
1163

Melledy's avatar
Melledy committed
1164
1165
1166
1167
1168
1169
	public PlayerOpenStateManager getOpenStateManager() {
	    if (this.openStateManager == null) {
            this.openStateManager = new PlayerOpenStateManager(this);
        }
        return openStateManager;
    }
gentlespoon's avatar
gentlespoon committed
1170

Melledy's avatar
Melledy committed
1171
    public synchronized void onTick() {
Melledy's avatar
Melledy committed
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
		// Check ping
		if (this.getLastPingTime() > System.currentTimeMillis() + 60000) {
			this.getSession().close();
			return;
		}
		// Check co-op requests
		Iterator<CoopRequest> it = this.getCoopRequests().values().iterator();
		while (it.hasNext()) {
			CoopRequest req = it.next();
			if (req.isExpired()) {
1182
1183
1184
1185
				req.getRequester().sendPacket(new PacketPlayerApplyEnterMpResultNotify(
						this,
						false,
						PlayerApplyEnterMpResultNotifyOuterClass.PlayerApplyEnterMpResultNotify.Reason.REASON_SYSTEM_JUDGE));
Melledy's avatar
Melledy committed
1186
1187
1188
1189
1190
				it.remove();
			}
		}
		// Ping
		if (this.getWorld() != null) {
Melledy's avatar
Melledy committed
1191
1192
			// RTT notify - very important to send this often
			this.sendPacket(new PacketWorldPlayerRTTNotify(this.getWorld()));
Asnxthaony's avatar
Asnxthaony committed
1193

Melledy's avatar
Melledy committed
1194
1195
1196
1197
1198
1199
1200
			// Update player locations if in multiplayer every 5 seconds
			long time = System.currentTimeMillis();
			if (this.getWorld().isMultiplayer() && this.getScene() != null && time > nextSendPlayerLocTime) {
				this.sendPacket(new PacketWorldPlayerLocationNotify(this.getWorld()));
				this.sendPacket(new PacketScenePlayerLocationNotify(this.getScene()));
				this.resetSendPlayerLocTime();
			}
Melledy's avatar
Melledy committed
1201
		}
1202
1203
1204
1205

		// Handle daily reset.
		this.doDailyReset();

Kinesis's avatar
Kinesis committed
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
		// Expedition
		var timeNow = Utils.getCurrentSeconds();
		var needNotify = false;
		for (Long key : expeditionInfo.keySet()) {
			ExpeditionInfo e = expeditionInfo.get(key);
			if(e.getState() == 1){
				if(timeNow - e.getStartTime() >= e.getHourTime() * 60 * 60){
					e.setState(2);
					needNotify = true;
				}
			}
		}
		if(needNotify){
			this.save();
			this.sendPacket(new PacketAvatarExpeditionDataNotify(this));
		}
1222
1223
1224

		// Send updated forge queue data, if necessary.
		this.getForgingManager().sendPlayerForgingUpdate();
ImmuState's avatar
ImmuState committed
1225
1226
1227

		// Recharge resin.
		this.getResinManager().rechargeResin();
1228
1229
	}

1230
	private synchronized void doDailyReset() {
1231
1232
1233
1234
1235
		// Check if we should execute a daily reset on this tick.
		int currentTime = Utils.getCurrentSeconds();

		var currentDate = LocalDate.ofInstant(Instant.ofEpochSecond(currentTime), ZoneId.systemDefault());
		var lastResetDate = LocalDate.ofInstant(Instant.ofEpochSecond(this.getLastDailyReset()), ZoneId.systemDefault());
1236

1237
1238
1239
1240
1241
1242
1243
1244
		if (!currentDate.isAfter(lastResetDate)) {
			return;
		}

		// We should - now execute all the resetting logic we need.
		// Reset forge points.
		this.setForgePoints(300_000);

1245
1246
1247
		// Reset daily BP missions.
		this.getBattlePassManager().resetDailyMissions();

1248
1249
1250
1251
		// Trigger login BP mission, so players who are online during the reset
		// don't have to relog to clear the mission.
		this.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_LOGIN);

1252
1253
1254
1255
1256
		// Reset weekly BP missions.
		if (currentDate.getDayOfWeek() == DayOfWeek.MONDAY) {
			this.getBattlePassManager().resetWeeklyMissions();
		}

1257
1258
1259
		// Done. Update last reset time.
		this.setLastDailyReset(currentTime);
	}
1260

Melledy's avatar
Melledy committed
1261
1262
1263
	public void resetSendPlayerLocTime() {
		this.nextSendPlayerLocTime = System.currentTimeMillis() + 5000;
	}
Melledy's avatar
Melledy committed
1264
1265
1266

	@PostLoad
	private void onLoad() {
1267
		this.getCodex().setPlayer(this);
akatatsu27's avatar
akatatsu27 committed
1268
        this.getOpenStateManager().setPlayer(this);
Melledy's avatar
Melledy committed
1269
1270
		this.getTeamManager().setPlayer(this);
	}
Asnxthaony's avatar
Asnxthaony committed
1271

Melledy's avatar
Melledy committed
1272
1273
1274
	public void save() {
		DatabaseHelper.savePlayer(this);
	}
KingRainbow44's avatar
KingRainbow44 committed
1275

1276
1277
	// Called from tokenrsp
	public void loadFromDatabase() {
Melledy's avatar
Melledy committed
1278
1279
1280
		// Make sure these exist
		if (this.getTeamManager() == null) {
			this.teamManager = new TeamManager(this);
Asnxthaony's avatar
Asnxthaony committed
1281
		}
1282
1283
1284
		if (this.getCodex() == null) {
			this.codex = new PlayerCodex(this);
		}
Asnxthaony's avatar
Asnxthaony committed
1285
		if (this.getProfile().getUid() == 0) {
1286
			this.getProfile().syncWithCharacter(this);
Melledy's avatar
Melledy committed
1287
		}
KingRainbow44's avatar
KingRainbow44 committed
1288

Melledy's avatar
Melledy committed
1289
1290
1291
		// Load from db
		this.getAvatars().loadFromDatabase();
		this.getInventory().loadFromDatabase();
1292
		this.getAvatars().postLoad(); // Needs to be called after inventory is handled
Asnxthaony's avatar
Asnxthaony committed
1293

Melledy's avatar
Melledy committed
1294
		this.getFriendsList().loadFromDatabase();
1295
		this.getMailHandler().loadFromDatabase();
Melledy's avatar
Melledy committed
1296
		this.getQuestManager().loadFromDatabase();
KingRainbow44's avatar
KingRainbow44 committed
1297

1298
		this.loadBattlePassManager();
1299
1300
1301
	}

	public void onLogin() {
Melledy's avatar
Melledy committed
1302
1303
1304
1305
1306
1307
1308
1309
		// Quest - Commented out because a problem is caused if you log out while this quest is active
		/*
		if (getQuestManager().getMainQuestById(351) == null) {
			GameQuest quest = getQuestManager().addQuest(35104);
			if (quest != null) {
				quest.finish();
			}
			getQuestManager().addQuest(35101);
KingRainbow44's avatar
KingRainbow44 committed
1310

Melledy's avatar
Melledy committed
1311
1312
1313
1314
			this.setSceneId(3);
			this.getPos().set(GameConstants.START_POSITION);
		}
		*/
KingRainbow44's avatar
KingRainbow44 committed
1315

Melledy's avatar
Melledy committed
1316
1317
1318
		// Create world
		World world = new World(this);
		world.addPlayer(this);
Asnxthaony's avatar
Asnxthaony committed
1319
1320

		// Multiplayer setting
AnimeGitB's avatar
AnimeGitB committed
1321
1322
		this.setProperty(PlayerProperty.PROP_PLAYER_MP_SETTING_TYPE, this.getMpSetting().getNumber(), false);
		this.setProperty(PlayerProperty.PROP_IS_MP_MODE_AVAILABLE, 1, false);
Asnxthaony's avatar
Asnxthaony committed
1323

1324
1325
1326
		// Execute daily reset logic if this is a new day.
		this.doDailyReset();

Melledy's avatar
Melledy committed
1327
1328
1329
1330
1331
		// Packets
		session.send(new PacketPlayerDataNotify(this)); // Player data
		session.send(new PacketStoreWeightLimitNotify());
		session.send(new PacketPlayerStoreNotify(this));
		session.send(new PacketAvatarDataNotify(this));
Melledy's avatar
Melledy committed
1332
		session.send(new PacketFinishedParentQuestNotify(this));
1333
		session.send(new PacketBattlePassAllDataNotify(this));
Melledy's avatar
Melledy committed
1334
		session.send(new PacketQuestListNotify(this));
1335
		session.send(new PacketCodexDataFullNotify(this));
1336
1337
		session.send(new PacketAllWidgetDataNotify(this));
		session.send(new PacketWidgetGadgetAllDataNotify());
ImmuState's avatar
ImmuState committed
1338
		session.send(new PacketCombineDataNotify(this.unlockedCombines));
1339
		this.forgingManager.sendForgeDataNotify();
ImmuState's avatar
ImmuState committed
1340
		this.resinManager.onPlayerLogin();
1341
		this.cookingManager.sendCookDataNofity();
1342
1343

		// Unlock in case this is an existing user that reached a level before we implemented unlocking.
Melledy's avatar
Melledy committed
1344
1345
		this.getOpenStateManager().unlockLevelDependentStates();
        this.getOpenStateManager().onPlayerLogin();
1346

Yazawazi's avatar
Yazawazi committed
1347
1348
		getTodayMoonCard(); // The timer works at 0:0, some users log in after that, use this method to check if they have received a reward today or not. If not, send the reward.

1349
1350
		// Battle Pass trigger
		this.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_LOGIN);
Akka's avatar
Akka committed
1351

Akka's avatar
Akka committed
1352
		this.furnitureManager.onLogin();
1353
1354
1355
		// Home
		home = GameHome.getByUid(getUid());
		home.onOwnerLogin(this);
Akka's avatar
Akka committed
1356
1357
        // Activity
        activityManager = new ActivityManager(this);
1358

Melledy's avatar
Melledy committed
1359
		session.send(new PacketPlayerEnterSceneNotify(this)); // Enter game world
1360
		session.send(new PacketPlayerLevelRewardUpdateNotify(rewardedLevels));
akatatsu27's avatar
akatatsu27 committed
1361

Melledy's avatar
Melledy committed
1362
1363
1364

		// First notify packets sent
		this.setHasSentAvatarDataNotify(true);
KingRainbow44's avatar
KingRainbow44 committed
1365

1366
1367
1368
		// Send server welcome chat.
		this.getServer().getChatManager().sendServerWelcomeMessages(this);
		
1369
1370
		// Set session state
		session.setState(SessionState.ACTIVE);
1371
1372
1373

		// Call join event.
		PlayerJoinEvent event = new PlayerJoinEvent(this); event.call();
1374
		if(event.isCanceled()){ // If event is not cancelled, continue.
1375
			session.close();
1376
1377
			return;
		}
KingRainbow44's avatar
KingRainbow44 committed
1378

1379
1380
1381
		// register
		getServer().registerPlayer(this);
		getProfile().setPlayer(this); // Set online
Melledy's avatar
Melledy committed
1382
	}
Asnxthaony's avatar
Asnxthaony committed
1383

Melledy's avatar
Melledy committed
1384
	public void onLogout() {
1385
		try{
1386
1387
1388
			// Clear chat history.
			this.getServer().getChatManager().clearHistoryOnLogout(this);

1389
1390
			// stop stamina calculation
			getStaminaManager().stopSustainedStaminaHandler();
1391

1392
			// force to leave the dungeon (inside has a "if")
1393
			this.getServer().getDungeonSystem().exitDungeon(this);
Asnxthaony's avatar
Asnxthaony committed
1394

1395
1396
1397
1398
1399
1400
1401
1402
			// Leave world
			if (this.getWorld() != null) {
				this.getWorld().removePlayer(this);
			}

			// Status stuff
			this.getProfile().syncWithCharacter(this);
			this.getProfile().setPlayer(null); // Set offline
Asnxthaony's avatar
Asnxthaony committed
1403

1404
			this.getCoopRequests().clear();
Asnxthaony's avatar
Asnxthaony committed
1405

1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
			// Save to db
			this.save();
			this.getTeamManager().saveAvatars();
			this.getFriendsList().save();

			// Call quit event.
			PlayerQuitEvent event = new PlayerQuitEvent(this); event.call();
		}catch (Throwable e){
			e.printStackTrace();
			Grasscutter.getLogger().warn("Player (UID {}) save failure", getUid());
		}finally {
			removeFromServer();
		}
	}
1420

1421
1422
1423
1424
1425
	public void removeFromServer() {
		// Remove from server.
		//Note: DON'T DELETE BY UID,BECAUSE THERE ARE MULTIPLE SAME UID PLAYERS WHEN DUPLICATED LOGIN!
		//so I decide to delete by object rather than uid
		getServer().getPlayers().values().removeIf(player1 -> player1 == this);
Melledy's avatar
Melledy committed
1426
	}
Asnxthaony's avatar
Asnxthaony committed
1427

Akka's avatar
Akka committed
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
    public int getLegendaryKey() {
        return this.getProperty(PlayerProperty.PROP_PLAYER_LEGENDARY_KEY);
    }
    public synchronized void addLegendaryKey(int count) {
        this.setProperty(PlayerProperty.PROP_PLAYER_LEGENDARY_KEY, getLegendaryKey() + count);
    }
    public synchronized void useLegendaryKey(int count) {
        this.setProperty(PlayerProperty.PROP_PLAYER_LEGENDARY_KEY, getLegendaryKey() - count);
    }

    public enum SceneLoadState {
Asnxthaony's avatar
Asnxthaony committed
1439
1440
		NONE(0), LOADING(1), INIT(2), LOADED(3);

Melledy's avatar
Melledy committed
1441
		private final int value;
Asnxthaony's avatar
Asnxthaony committed
1442

Melledy's avatar
Melledy committed
1443
1444
1445
		private SceneLoadState(int value) {
			this.value = value;
		}
Asnxthaony's avatar
Asnxthaony committed
1446

Melledy's avatar
Melledy committed
1447
1448
1449
1450
		public int getValue() {
			return this.value;
		}
	}
1451
1452
1453
1454

	public void setMessageHandler(MessageHandler messageHandler) {
		this.messageHandler = messageHandler;
	}
1455

AnimeGitB's avatar
AnimeGitB committed
1456
1457
1458
1459
1460
1461
1462
	public int getPropertyMin(PlayerProperty prop) {
		if (prop.getDynamicRange()) {
			return switch (prop) {
				default -> 0;
			};
		} else {
			return prop.getMin();
1463
		}
AnimeGitB's avatar
AnimeGitB committed
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
	}

	public int getPropertyMax(PlayerProperty prop) {
		if (prop.getDynamicRange()) {
			return switch (prop) {
				case PROP_CUR_SPRING_VOLUME -> getProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME);
				case PROP_CUR_PERSIST_STAMINA -> getProperty(PlayerProperty.PROP_MAX_STAMINA);
				default -> 0;
			};
		} else {
			return prop.getMax();
		}
	}

	private boolean setPropertyWithSanityCheck(PlayerProperty prop, int value, boolean sendPacket) {
		int min = this.getPropertyMin(prop);
		int max = this.getPropertyMax(prop);
		if (min <= value && value <= max) {
			this.properties.put(prop.getId(), value);
			if (sendPacket) {
				// Update player with packet
				this.sendPacket(new PacketPlayerPropNotify(this, prop));
			}
			return true;
		} else {
1489
1490
		return false;
	}
AnimeGitB's avatar
AnimeGitB committed
1491
	}
1492

Melledy's avatar
Melledy committed
1493
}