Initial commit

This commit is contained in:
Melledy
2022-04-17 05:43:07 -07:00
commit 7925d1cda3
354 changed files with 20869 additions and 0 deletions

View File

@@ -0,0 +1,262 @@
package emu.grasscutter.game.friends;
import java.util.List;
import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.GenshinPlayer;
import emu.grasscutter.net.proto.DealAddFriendResultTypeOuterClass.DealAddFriendResultType;
import emu.grasscutter.server.packet.send.PacketAskAddFriendNotify;
import emu.grasscutter.server.packet.send.PacketAskAddFriendRsp;
import emu.grasscutter.server.packet.send.PacketDealAddFriendRsp;
import emu.grasscutter.server.packet.send.PacketDeleteFriendNotify;
import emu.grasscutter.server.packet.send.PacketDeleteFriendRsp;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
public class FriendsList {
private final GenshinPlayer player;
private final Int2ObjectMap<Friendship> friends;
private final Int2ObjectMap<Friendship> pendingFriends;
private boolean loaded = false;
public FriendsList(GenshinPlayer player) {
this.player = player;
this.friends = new Int2ObjectOpenHashMap<Friendship>();
this.pendingFriends = new Int2ObjectOpenHashMap<Friendship>();
}
public GenshinPlayer getPlayer() {
return player;
}
public boolean hasLoaded() {
return loaded;
}
public synchronized Int2ObjectMap<Friendship> getFriends() {
return friends;
}
public synchronized Int2ObjectMap<Friendship> getPendingFriends() {
return this.pendingFriends;
}
public synchronized boolean isFriendsWith(int uid) {
return this.getFriends().containsKey(uid);
}
private synchronized Friendship getFriendshipById(int id) {
Friendship friendship = this.getFriends().get(id);
if (friendship == null) {
friendship = this.getPendingFriendById(id);
}
return friendship;
}
private synchronized Friendship getFriendById(int id) {
return this.getFriends().get(id);
}
private synchronized Friendship getPendingFriendById(int id) {
return this.getPendingFriends().get(id);
}
public void addFriend(Friendship friendship) {
getFriends().put(friendship.getFriendId(), friendship);
}
public void addPendingFriend(Friendship friendship) {
getPendingFriends().put(friendship.getFriendId(), friendship);
}
public synchronized void handleFriendRequest(int targetUid, DealAddFriendResultType result) {
// Check if player has sent friend request
Friendship myFriendship = this.getPendingFriendById(targetUid);
if (myFriendship == null) {
return;
}
// Make sure asker cant do anything
if (myFriendship.getAskerId() == this.getPlayer().getId()) {
return;
}
GenshinPlayer target = getPlayer().getSession().getServer().forceGetPlayerById(targetUid);
if (target == null) {
return; // Should never happen
}
// Get target's friendship
Friendship theirFriendship = null;
if (target.isOnline()) {
theirFriendship = target.getFriendsList().getPendingFriendById(this.getPlayer().getId());
} else {
theirFriendship = DatabaseHelper.getReverseFriendship(myFriendship);
}
if (theirFriendship == null) {
// They dont have us on their friends list anymore, rip
this.getPendingFriends().remove(myFriendship.getOwnerId());
myFriendship.delete();
return;
}
// Handle
if (result == DealAddFriendResultType.DealAddFriendAccept) { // Request accepted
myFriendship.setIsFriend(true);
theirFriendship.setIsFriend(true);
this.getPendingFriends().remove(myFriendship.getOwnerId());
this.addFriend(myFriendship);
if (target.isOnline()) {
target.getFriendsList().getPendingFriends().remove(this.getPlayer().getId());
target.getFriendsList().addFriend(theirFriendship);
}
myFriendship.save();
theirFriendship.save();
} else { // Request declined
// Delete from my pending friends
this.getPendingFriends().remove(myFriendship.getOwnerId());
myFriendship.delete();
// Delete from target uid
if (target.isOnline()) {
theirFriendship = target.getFriendsList().getPendingFriendById(this.getPlayer().getId());
}
theirFriendship.delete();
}
// Packet
this.getPlayer().sendPacket(new PacketDealAddFriendRsp(targetUid, result));
}
public synchronized void deleteFriend(int targetUid) {
Friendship myFriendship = this.getFriendById(targetUid);
if (myFriendship == null) {
return;
}
this.getFriends().remove(targetUid);
myFriendship.delete();
Friendship theirFriendship = null;
GenshinPlayer friend = myFriendship.getFriendProfile().getPlayer();
if (friend != null) {
// Friend online
theirFriendship = friend.getFriendsList().getFriendById(this.getPlayer().getId());
if (theirFriendship != null) {
friend.getFriendsList().getFriends().remove(theirFriendship.getFriendId());
theirFriendship.delete();
friend.sendPacket(new PacketDeleteFriendNotify(theirFriendship.getFriendId()));
}
} else {
// Friend offline
theirFriendship = DatabaseHelper.getReverseFriendship(myFriendship);
if (theirFriendship != null) {
theirFriendship.delete();
}
}
// Packet
this.getPlayer().sendPacket(new PacketDeleteFriendRsp(targetUid));
}
public synchronized void sendFriendRequest(int targetUid) {
GenshinPlayer target = getPlayer().getSession().getServer().forceGetPlayerById(targetUid);
if (target == null || target == this.getPlayer()) {
return;
}
// Check if friend already exists
if (this.getPendingFriends().containsKey(targetUid) || this.getFriends().containsKey(targetUid)) {
return;
}
// Create friendships
Friendship myFriendship = new Friendship(getPlayer(), target, getPlayer());
Friendship theirFriendship = new Friendship(target, getPlayer(), getPlayer());
// Add pending lists
this.addPendingFriend(myFriendship);
if (target.isOnline() && target.getFriendsList().hasLoaded()) {
target.getFriendsList().addPendingFriend(theirFriendship);
target.sendPacket(new PacketAskAddFriendNotify(theirFriendship));
}
// Save
myFriendship.save();
theirFriendship.save();
// Packets
this.getPlayer().sendPacket(new PacketAskAddFriendRsp(targetUid));
}
/** Gets total amount of potential friends
* */
public int getFullFriendCount() {
return this.getPendingFriends().size() + this.getFriends().size();
}
public synchronized void loadFromDatabase() {
if (this.hasLoaded()) {
return;
}
// Get friendships from the db
List<Friendship> friendships = DatabaseHelper.getFriends(player);
friendships.forEach(this::loadFriendFromDatabase);
// Set loaded flag
this.loaded = true;
}
private void loadFriendFromDatabase(Friendship friendship) {
// Set friendship owner
friendship.setOwner(getPlayer());
// Check if friend is online
GenshinPlayer friend = getPlayer().getSession().getServer().getPlayerById(friendship.getFriendProfile().getId());
if (friend != null) {
// Set friend to online mode
friendship.setFriendProfile(friend);
// Update our status on friend's client if theyre online
if (friend.getFriendsList().hasLoaded()) {
Friendship theirFriendship = friend.getFriendsList().getFriendshipById(getPlayer().getId());
if (theirFriendship != null) {
// Update friend profile
theirFriendship.setFriendProfile(getPlayer());
} else {
// They dont have us on their friends list anymore, rip
friendship.delete();
return;
}
}
}
// Finally, load to our friends list
if (friendship.isFriend()) {
getFriends().put(friendship.getFriendId(), friendship);
} else {
getPendingFriends().put(friendship.getFriendId(), friendship);
// TODO - Hacky fix to force client to see a notification for a friendship
if (getPendingFriends().size() == 1) {
getPlayer().getSession().send(new PacketAskAddFriendNotify(friendship));
}
}
}
public void save() {
// Update all our friends
List<Friendship> friendships = DatabaseHelper.getReverseFriends(getPlayer());
for (Friendship friend : friendships) {
friend.setFriendProfile(this.getPlayer());
friend.save();
}
}
}

View File

@@ -0,0 +1,108 @@
package emu.grasscutter.game.friends;
import org.bson.types.ObjectId;
import dev.morphia.annotations.*;
import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.GenshinPlayer;
import emu.grasscutter.net.proto.FriendBriefOuterClass.FriendBrief;
import emu.grasscutter.net.proto.FriendOnlineStateOuterClass.FriendOnlineState;
import emu.grasscutter.net.proto.HeadImageOuterClass.HeadImage;
@Entity(value = "friendships", noClassnameStored = true)
public class Friendship {
@Id private ObjectId id;
@Transient private GenshinPlayer owner;
@Indexed private int ownerId;
@Indexed private int friendId;
private boolean isFriend;
private int askerId;
private PlayerProfile profile;
@Deprecated // Morphia use only
public Friendship() { }
public Friendship(GenshinPlayer owner, GenshinPlayer friend, GenshinPlayer asker) {
this.setOwner(owner);
this.ownerId = owner.getId();
this.friendId = friend.getId();
this.profile = friend.getProfile();
this.askerId = asker.getId();
}
public GenshinPlayer getOwner() {
return owner;
}
public void setOwner(GenshinPlayer owner) {
this.owner = owner;
}
public boolean isFriend() {
return isFriend;
}
public void setIsFriend(boolean b) {
this.isFriend = b;
}
public int getOwnerId() {
return ownerId;
}
public int getFriendId() {
return friendId;
}
public int getAskerId() {
return askerId;
}
public void setAskerId(int askerId) {
this.askerId = askerId;
}
public PlayerProfile getFriendProfile() {
return profile;
}
public void setFriendProfile(GenshinPlayer character) {
if (character == null || this.friendId != character.getId()) return;
this.profile = character.getProfile();
}
public boolean isOnline() {
return getFriendProfile().getPlayer() != null;
}
public void save() {
DatabaseHelper.saveFriendship(this);
}
public void delete() {
DatabaseHelper.deleteFriendship(this);
}
public FriendBrief toProto() {
FriendBrief proto = FriendBrief.newBuilder()
.setUid(getFriendProfile().getId())
.setNickname(getFriendProfile().getName())
.setLevel(getFriendProfile().getPlayerLevel())
.setAvatar(HeadImage.newBuilder().setAvatarId(getFriendProfile().getAvatarId()))
.setWorldLevel(getFriendProfile().getWorldLevel())
.setSignature(getFriendProfile().getSignature())
.setOnlineState(getFriendProfile().isOnline() ? FriendOnlineState.FRIEND_ONLINE : FriendOnlineState.FRIEND_DISCONNECT)
.setIsMpModeAvailable(true)
.setLastActiveTime(getFriendProfile().getLastActiveTime())
.setNameCardId(getFriendProfile().getNameCard())
.setParam(getFriendProfile().getDaysSinceLogin())
.setUnk1(1)
.setUnk2(3)
.build();
return proto;
}
}

View File

@@ -0,0 +1,99 @@
package emu.grasscutter.game.friends;
import dev.morphia.annotations.*;
import emu.grasscutter.game.GenshinPlayer;
import emu.grasscutter.utils.Utils;
public class PlayerProfile {
@Transient private GenshinPlayer player;
private int id;
private int nameCard;
private int avatarId;
private String name;
private String signature;
private int achievements;
private int playerLevel;
private int worldLevel;
private int lastActiveTime;
@Deprecated // Morphia only
public PlayerProfile() { }
public PlayerProfile(GenshinPlayer player) {
this.id = player.getId();
this.syncWithCharacter(player);
}
public int getId() {
return id;
}
public GenshinPlayer getPlayer() {
return player;
}
public synchronized void setPlayer(GenshinPlayer player) {
this.player = player;
}
public String getName() {
return name;
}
public int getNameCard() {
return nameCard;
}
public int getAvatarId() {
return avatarId;
}
public String getSignature() {
return signature;
}
public int getAchievements() {
return achievements;
}
public int getPlayerLevel() {
return playerLevel;
}
public int getWorldLevel() {
return worldLevel;
}
public int getLastActiveTime() {
return lastActiveTime;
}
public void updateLastActiveTime() {
this.lastActiveTime = Utils.getCurrentSeconds();
}
public int getDaysSinceLogin() {
return (int) Math.floor((Utils.getCurrentSeconds() - getLastActiveTime()) / 86400.0);
}
public boolean isOnline() {
return this.getPlayer() != null;
}
public void syncWithCharacter(GenshinPlayer player) {
if (player == null) {
return;
}
this.name = player.getNickname();
this.avatarId = player.getHeadImage();
this.signature = player.getSignature();
this.nameCard = player.getNameCardId();
this.playerLevel = player.getLevel();
this.worldLevel = player.getWorldLevel();
//this.achievements = 0;
this.updateLastActiveTime();
}
}