Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add friend list support #271

Merged
merged 15 commits into from
Apr 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Arrowgene.Ddon.Database/Files/Database/Script/schema_sqlite.sql
Original file line number Diff line number Diff line change
Expand Up @@ -467,3 +467,17 @@ CREATE TABLE IF NOT EXISTS ddon_unlocked_secret_ability
CONSTRAINT pk_ddon_unlocked_secret_ability PRIMARY KEY("character_common_id", "ability_id"),
CONSTRAINT fk_unlocked_secret_ability_character_common_id FOREIGN KEY("character_common_id") REFERENCES ddon_character_common ("character_common_id") ON DELETE CASCADE
);

CREATE TABLE IF NOT EXISTS "ddon_contact_list" (
"id" INTEGER UNIQUE,
"requester_character_id" INTEGER NOT NULL,
"requested_character_id" INTEGER NOT NULL,
"status" SMALLINT NOT NULL,
"type" SMALLINT NOT NULL,
"requester_favorite" BOOLEAN NOT NULL,
"requested_favorite" BOOLEAN NOT NULL,
FOREIGN KEY("requester_character_id") REFERENCES "ddon_character"("character_id"),
FOREIGN KEY("requested_character_id") REFERENCES "ddon_character"("character_id"),
PRIMARY KEY("id" AUTOINCREMENT),
UNIQUE("requester_character_id","requested_character_id")
alborrajo marked this conversation as resolved.
Show resolved Hide resolved
);
11 changes: 11 additions & 0 deletions Arrowgene.Ddon.Database/IDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,17 @@ public interface IDatabase
bool DeleteConnection(int serverId, int accountId);
bool DeleteConnectionsByAccountId(int accountId);
bool DeleteConnectionsByServerId(int serverId);

// ContactList
int InsertContact(uint requestingCharacterId, uint requestedCharacterId, ContactListStatus status,
ContactListType type, bool requesterFavorite, bool requestedFavorite);
int UpdateContact(uint requestingCharacterId, uint requestedCharacterId, ContactListStatus status,
ContactListType type, bool requesterFavorite, bool requestedFavorite);
int DeleteContact(uint requestingCharacterId, uint requestedCharacterId);
int DeleteContactById(uint id);
List<ContactListEntity> SelectContactsByCharacterId(uint characterId);
ContactListEntity SelectContactsByCharacterId(uint characterId1, uint characterId2);
ContactListEntity SelectContactListById(uint id);

// Dragon Force Augmentation
public bool InsertIfNotExistsDragonForceAugmentation(uint commonId, uint elementId, uint pageNo, uint groupNo, uint indexNo);
Expand Down
155 changes: 155 additions & 0 deletions Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbContactList.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
using System.Collections.Generic;
using System.Data.Common;
using Arrowgene.Ddon.Shared.Model;

namespace Arrowgene.Ddon.Database.Sql.Core
{
public abstract partial class DdonSqlDb<TCon, TCom, TReader> : SqlDb<TCon, TCom, TReader>
where TCon : DbConnection
where TCom : DbCommand
where TReader : DbDataReader
{

private static readonly string TableName = "ddon_contact_list";

private static readonly string[] ContactListFields = new string[]
{
/* id */ "requester_character_id", "requested_character_id", "status", "type", "requester_favorite", "requested_favorite"
};

private static readonly string SqlInsertContact = $"INSERT INTO \"{TableName}\" ({BuildQueryField(ContactListFields)}) VALUES ({BuildQueryInsert(ContactListFields)});";
private static readonly string SqlSelectContactById = $"SELECT \"id\", {BuildQueryField(ContactListFields)} FROM \"{TableName}\" WHERE \"id\"=@id;";
private static readonly string SqlSelectContactsByCharacterId = $"SELECT \"id\", {BuildQueryField(ContactListFields)} FROM \"{TableName}\" WHERE \"requester_character_id\"=@character_id or \"requested_character_id\"=@character_id;";

private static readonly string SqlSelectContactsByCharacterIds = $"SELECT \"id\", {BuildQueryField(ContactListFields)} FROM \"{TableName}\" " +
$"WHERE (\"requester_character_id\"=@character_id_1 and \"requested_character_id\"=@character_id_2) or " +
$"(\"requester_character_id\"=@character_id_2 and \"requested_character_id\"=@character_id_1);";

private static readonly string SqlDeleteContact = $"DELETE FROM \"{TableName}\" WHERE \"requester_character_id\"=@requester_character_id and \"requested_character_id\"=@requested_character_id;";
private static readonly string SqlDeleteContactById = $"DELETE FROM \"{TableName}\" WHERE \"id\"=@id;";


private static readonly string SqlUpdateContactByCharIds = $"UPDATE \"{TableName}\" SET \"status\"=@status, \"type\"=@type, \"requester_favorite\"=@requester_favorite, \"requested_favorite\"=@requested_favorite WHERE \"requester_character_id\"=@requester_character_id and \"requested_character_id\"=@requested_character_id;";


public int InsertContact(uint requestingCharacterId, uint requestedCharacterId, ContactListStatus status, ContactListType type, bool requesterFavorite, bool requestedFavorite)
{
int rowsAffected = ExecuteNonQuery(SqlInsertContact, command =>
{
AddParameter(command, "@requester_character_id", requestingCharacterId);
AddParameter(command, "@requested_character_id", requestedCharacterId);
AddParameter(command, "@status", (byte) status);
AddParameter(command, "@type", (byte) type);
AddParameter(command, "@requester_favorite", requesterFavorite);
AddParameter(command, "@requested_favorite", requestedFavorite);
}, out long autoIncrement);

if (rowsAffected > NoRowsAffected)
{
return (int)autoIncrement;
}

return 0;
}

public int UpdateContact(uint requestingCharacterId, uint requestedCharacterId, ContactListStatus status, ContactListType type, bool requesterFavorite, bool requestedFavorite)
{
int rowsAffected = ExecuteNonQuery(SqlUpdateContactByCharIds, command =>
{
AddParameter(command, "@requester_character_id", requestingCharacterId);
AddParameter(command, "@requested_character_id", requestedCharacterId);
AddParameter(command, "@status", (byte) status);
AddParameter(command, "@type", (byte) type);
AddParameter(command, "@requester_favorite", requesterFavorite);
AddParameter(command, "@requested_favorite", requestedFavorite);
});

return rowsAffected;
}

public int DeleteContact(uint requestingCharacterId, uint requestedCharacterId)
{
int rowsAffected = ExecuteNonQuery(SqlDeleteContact, command =>
{
AddParameter(command, "@requester_character_id", requestingCharacterId);
AddParameter(command, "@requested_character_id", requestedCharacterId);
});
return rowsAffected;
}

public int DeleteContactById(uint id)
{
int rowsAffected = ExecuteNonQuery(SqlDeleteContactById, command =>
{
AddParameter(command, "@id", id);
});
return rowsAffected;
}

public List<ContactListEntity> SelectContactsByCharacterId(uint characterId)
{
List<ContactListEntity> entities = new List<ContactListEntity>();
ExecuteReader(SqlSelectContactsByCharacterId,
command => { AddParameter(command, "@character_id", characterId); }, reader =>
{
while (reader.Read())
{
ContactListEntity e = EntityReader(reader);
entities.Add(e);
}
});

return entities;
}

public ContactListEntity SelectContactsByCharacterId(uint characterId1, uint characterId2)
{
ContactListEntity entity = null;
ExecuteReader(SqlSelectContactsByCharacterIds,
command =>
{
AddParameter(command, "@character_id_1", characterId1);
AddParameter(command, "@character_id_2", characterId2);
}, reader =>
{
if (reader.Read())
{
entity = EntityReader(reader);
}
});

return entity;
}

public ContactListEntity SelectContactListById(uint id)
{
ContactListEntity entity = null;
ExecuteReader(SqlSelectContactById,
command =>
{
AddParameter(command, "@id", id);
}, reader =>
{
if (reader.Read())
{
entity = EntityReader(reader);
}
});

return entity;
}

private ContactListEntity EntityReader(TReader reader)
{
ContactListEntity e = new ContactListEntity();
e.Id = GetUInt32(reader, "id");
e.RequesterCharacterId = GetUInt32(reader, "requester_character_id");
e.RequestedCharacterId = GetUInt32(reader, "requested_character_id");
e.Status = (ContactListStatus) GetByte(reader, "status");
e.Type = (ContactListType) GetByte(reader, "type");
e.RequesterFavorite = GetBoolean(reader, "requester_favorite");
e.RequestedFavorite = GetBoolean(reader, "requested_favorite");
return e;
}
}
}
76 changes: 76 additions & 0 deletions Arrowgene.Ddon.GameServer/Characters/ContactListManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using Arrowgene.Ddon.Database;
using Arrowgene.Ddon.Shared.Entity.Structure;
using Arrowgene.Ddon.Shared.Model;

namespace Arrowgene.Ddon.GameServer.Characters;

public class ContactListManager
{
public static Character getCharWithOnlineStatus(DdonGameServer server, IDatabase database, uint charId)
{
var otherClient = server.ClientLookup.GetClientByCharacterId(charId);
Character otherCharacter = null;
if (otherClient == null || otherClient.Character == null)
{
otherCharacter = database.SelectCharacter(charId);
otherCharacter.OnlineStatus = OnlineStatus.Offline;
}
else
{
otherCharacter = otherClient.Character;
}

return otherCharacter;
}

public static CDataCommunityCharacterBaseInfo CharacterToCommunityInfo(Character c)
{
return new CDataCommunityCharacterBaseInfo()
{
CharacterId = c.CharacterId,
CharacterName = new CDataCharacterName()
{
FirstName = c.FirstName,
LastName = c.LastName
},
ClanName = "", // TODO get clan

};

}

public static CDataCharacterListElement CharacterToListEml(Character c)
{
return new CDataCharacterListElement()
{
OnlineStatus = c.OnlineStatus,
MatchingProfile = c.MatchingProfile.Comment,
ServerId = c.Server.Id,
CommunityCharacterBaseInfo = CharacterToCommunityInfo(c),
CurrentJobBaseInfo = new CDataJobBaseInfo()
{
Job = c.Job,
Level = (byte)(c.ActiveCharacterJobData?.Lv ?? 0x00)
},
EntryJobBaseInfo = new CDataJobBaseInfo()
{
// TODO
Job = c.MatchingProfile.EntryJob,
Level = (byte)(c.MatchingProfile?.EntryJobLevel ?? 0x00)
}
};
}

public static CDataFriendInfo CharacterToFriend(Character c, uint unFriendNo, bool isFavorite)
{
return new CDataFriendInfo()
{
IsFavorite = isFavorite,
PendingStatus = 0x00, // TODO
UnFriendNo = unFriendNo,
CharacterListElement = CharacterToListEml(c)

};

}
}
6 changes: 6 additions & 0 deletions Arrowgene.Ddon.GameServer/DdonGameServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
using Arrowgene.Networking.Tcp;
using Arrowgene.Ddon.GameServer.Shop;
using Arrowgene.Ddon.GameServer.Characters;
using Arrowgene.Ddon.Shared.Model;

namespace Arrowgene.Ddon.GameServer
{
Expand Down Expand Up @@ -243,6 +244,11 @@ private void LoadPacketHandler()
AddHandler(new EquipUpdateHidePawnLanternHandler(this));

AddHandler(new FriendGetFriendListHandler(this));
AddHandler(new FriendApplyFriendListHandler(this));
AddHandler(new FriendApproveFriendListHandler(this));
AddHandler(new FriendRemoveFriendHandler(this));
AddHandler(new FriendRegisterFavoriteFriendHandler(this));
AddHandler(new FriendCancelFriendApplicationHandler(this));
AddHandler(new FriendGetRecentCharacterListHandler(this));

AddHandler(new Gp_28_2_1_Handler(this));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
using Arrowgene.Ddon.GameServer.Dump;
using System.Collections.Generic;
using Arrowgene.Ddon.GameServer.Characters;
using Arrowgene.Ddon.Server;
using Arrowgene.Ddon.Server.Network;
using Arrowgene.Ddon.Shared.Entity.PacketStructure;
using Arrowgene.Ddon.Shared.Entity.Structure;
using Arrowgene.Ddon.Shared.Model;
using Arrowgene.Ddon.Shared.Network;
using Arrowgene.Logging;

namespace Arrowgene.Ddon.GameServer.Handler
{
public class CharacterCommunityCharacterStatusGetHandler : PacketHandler<GameClient>
public class CharacterCommunityCharacterStatusGetHandler : GameStructurePacketHandler<C2SCharacterCommunityCharacterStatusGetReq>
{
private static readonly ServerLogger Logger = LogProvider.Logger<ServerLogger>(typeof(CharacterCommunityCharacterStatusGetHandler));

Expand All @@ -15,11 +18,43 @@ public CharacterCommunityCharacterStatusGetHandler(DdonGameServer server) : base
{
}

public override PacketId Id => PacketId.C2S_CHARACTER_COMMUNITY_CHARACTER_STATUS_GET_REQ;
// public override PacketId Id => PacketId.C2S_CHARACTER_COMMUNITY_CHARACTER_STATUS_GET_REQ;

public override void Handle(GameClient client, IPacket packet)
public override void Handle(GameClient client, StructurePacket<C2SCharacterCommunityCharacterStatusGetReq> packet)
{
client.Send(InGameDump.Dump_65);
// client.Send(InGameDump.Dump_65);

List<CDataCharacterListElement> updateCharacterList = new List<CDataCharacterListElement>();
List<CDataUpdateMatchingProfileInfo> updateMatchingProfileList = new List<CDataUpdateMatchingProfileInfo>();

List<ContactListEntity> friends = Database.SelectContactsByCharacterId(client.Character.CharacterId);

foreach (var f in friends)
{
if (f.Type != ContactListType.FriendList || f.Status != ContactListStatus.Accepted)
{
continue;
}
Character otherCharacter =
ContactListManager.getCharWithOnlineStatus(Server,Database, f.GetOtherCharacterId(client.Character.CharacterId));
updateCharacterList.Add(ContactListManager.CharacterToListEml(otherCharacter));
updateMatchingProfileList.Add(new CDataUpdateMatchingProfileInfo()
{
CharacterId = otherCharacter.CharacterId,
Comment = otherCharacter.MatchingProfile.Comment
});
}

client.Send(new S2CCharacterCommunityCharacterStatusUpdateNtc()
{
UpdateCharacterList = updateCharacterList,
UpdateMatchingProfileList = updateMatchingProfileList
});

client.Send(new S2CCharacterCommunityCharacterStatusGetRes()
{
Result = updateCharacterList.Count + 1
});
}
}
}
2 changes: 2 additions & 0 deletions Arrowgene.Ddon.GameServer/Handler/ConnectionLogoutHandler.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Arrowgene.Ddon.Server;
using Arrowgene.Ddon.Server.Network;
using Arrowgene.Ddon.Shared.Entity.PacketStructure;
using Arrowgene.Ddon.Shared.Model;
using Arrowgene.Ddon.Shared.Network;
using Arrowgene.Logging;

Expand All @@ -21,6 +22,7 @@ public override void Handle(GameClient client, IPacket packet)
{
S2CConnectionLogoutRes res = new S2CConnectionLogoutRes();
client.Send(res);
client.Character.OnlineStatus = OnlineStatus.Offline;
}
}
}
Loading
Loading