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 12 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
15 changes: 15 additions & 0 deletions Arrowgene.Ddon.Database/Files/Database/Script/schema_sqlite.sql
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ CREATE TABLE IF NOT EXISTS ddon_character
"hide_equip_lantern_pawn" BOOLEAN NOT NULL,
"arisen_profile_share_range" SMALLINT NOT NULL,
"fav_warp_slot_num" INTEGER NOT NULL,
"online_status" SMALLINT,
alborrajo marked this conversation as resolved.
Show resolved Hide resolved
CONSTRAINT fk_character_character_common_id FOREIGN KEY ("character_common_id") REFERENCES ddon_character_common ("character_common_id") ON DELETE CASCADE,
CONSTRAINT fk_character_account_id FOREIGN KEY ("account_id") REFERENCES account ("id") ON DELETE CASCADE
);
Expand Down Expand Up @@ -428,3 +429,17 @@ CREATE TABLE IF NOT EXISTS ddon_connection
CONSTRAINT uq_connection_server_id_account_id UNIQUE (server_id, account_id),
CONSTRAINT fk_connection_token_account_id FOREIGN KEY ("account_id") REFERENCES account ("id")
);

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
);
13 changes: 13 additions & 0 deletions Arrowgene.Ddon.Database/IDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ public interface IDatabase
bool UpdateCharacterBaseInfo(Character character);
bool UpdateCharacterMatchingProfile(Character character);
bool UpdateCharacterArisenProfile(Character character);
int UpdateCharacterOnlineStatus(Character character);
int UpdateAllCharactersOnlineStatus(OnlineStatus onlineStatus);

// Pawn
bool CreatePawn(Pawn pawn);
Expand Down Expand Up @@ -140,5 +142,16 @@ 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);
}
}
26 changes: 25 additions & 1 deletion Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbCharacter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public abstract partial class DdonSqlDb<TCon, TCom, TReader> : SqlDb<TCon, TCom,
{
private static readonly string[] CharacterFields = new string[]
{
"version", "character_common_id", "account_id", "first_name", "last_name", "created", "my_pawn_slot_num", "rental_pawn_slot_num", "hide_equip_head_pawn", "hide_equip_lantern_pawn", "arisen_profile_share_range", "fav_warp_slot_num"
"version", "character_common_id", "account_id", "first_name", "last_name", "created", "my_pawn_slot_num", "rental_pawn_slot_num", "hide_equip_head_pawn", "hide_equip_lantern_pawn", "arisen_profile_share_range", "fav_warp_slot_num", "online_status"
};

private static readonly string[] CDataMatchingProfileFields = new string[]
Expand Down Expand Up @@ -61,6 +61,9 @@ public abstract partial class DdonSqlDb<TCon, TCom, TReader> : SqlDb<TCon, TCom,
private static readonly string SqlUpdateCharacterArisenProfile = $"UPDATE \"ddon_character_arisen_profile\" SET {BuildQueryUpdate(CDataArisenProfileFields)} WHERE \"character_id\" = @character_id;";
private static readonly string SqlSelectCharacterArisenProfile = $"SELECT {BuildQueryField(CDataArisenProfileFields)} FROM \"ddon_character_arisen_profile\" WHERE \"character_id\" = @character_id;";
private const string SqlDeleteCharacterArisenProfile = "DELETE FROM \"ddon_character_arisen_profile\" WHERE \"character_id\"=@character_id;";

private static readonly string SqlUpdateOnlineStatus = $"UPDATE \"ddon_character\" SET \"online_status\" = @online_status WHERE \"character_id\" = @character_id;";
private static readonly string SqlUpdateAllOnlineStatus = $"UPDATE \"ddon_character\" SET \"online_status\" = @online_status;";


public bool CreateCharacter(Character character)
Expand Down Expand Up @@ -101,6 +104,25 @@ public bool UpdateCharacterBaseInfo(TCon conn, Character character)

return characterUpdateRowsAffected > NoRowsAffected;
}

public int UpdateCharacterOnlineStatus(Character character)
{
int rowsAffected = ExecuteNonQuery(SqlUpdateOnlineStatus, command =>
{
AddParameter(command, "@online_status", (byte) character.OnlineStatus);
AddParameter(command, "@character_id", character.CharacterId);
});
return rowsAffected;
}

public int UpdateAllCharactersOnlineStatus(OnlineStatus onlineStatus)
{
int rowsAffected = ExecuteNonQuery(SqlUpdateAllOnlineStatus, command =>
{
AddParameter(command, "@online_status", (byte) onlineStatus);
});
return rowsAffected;
}

public bool UpdateCharacterMatchingProfile(Character character)
{
Expand Down Expand Up @@ -366,6 +388,7 @@ private Character ReadAllCharacterData(TReader reader)
character.ArisenProfile.MotionFrameNo = GetUInt32(reader, "motion_frame_no");

character.FavWarpSlotNum = GetUInt32(reader, "fav_warp_slot_num");
character.OnlineStatus = (OnlineStatus) GetByte(reader, "online_status");

return character;
}
Expand All @@ -385,6 +408,7 @@ private void AddParameter(TCom command, Character character)
AddParameter(command, "@hide_equip_head_pawn", character.HideEquipHeadPawn);
AddParameter(command, "@hide_equip_lantern_pawn", character.HideEquipLanternPawn);
AddParameter(command, "@arisen_profile_share_range", character.ArisenProfileShareRange);
AddParameter(command, "@online_status", (byte) character.OnlineStatus);
// CDataMatchingProfile
AddParameter(command, "@entry_job", (byte) character.MatchingProfile.EntryJob);
AddParameter(command, "@entry_job_level", character.MatchingProfile.EntryJobLevel);
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;
}
}
}
9 changes: 9 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 @@ -133,6 +134,9 @@ ClientConnectionChangeArgs connectionChangeEventArgs
= new ClientConnectionChangeArgs(ClientConnectionChangeArgs.EventType.DISCONNECT, client);
connectionChangeEvent(this, connectionChangeEventArgs);
}

client.Character.OnlineStatus = OnlineStatus.Offline;
Database.UpdateCharacterOnlineStatus(client.Character);
}

public override GameClient NewClient(ITcpSocket socket)
Expand Down Expand Up @@ -234,6 +238,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,16 @@
using Arrowgene.Ddon.GameServer.Dump;
using System.Collections.Generic;
using Arrowgene.Ddon.GameServer.Dump;
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 +19,44 @@ 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 =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of these kind of unbracketed if statements. I've seen a lot of bugs where some other person comes to change and then made a mistake due to this kind of style. If Laith is ok with this though, then it is fine.

Database.SelectCharacter(f.GetOtherCharacterId(client.Character.CharacterId));

if (f.Status == ContactListStatus.Accepted)
{
updateCharacterList.Add(ContactListEntity.CharacterToListEml(otherCharacter));
updateMatchingProfileList.Add(new CDataUpdateMatchingProfileInfo()
{
CharacterId = otherCharacter.CharacterId,
Comment = ""
});
}
}

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

client.Send(new S2CCharacterCommunityCharacterStatusGetRes()
{
Result = updateCharacterList.Count + 1
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ public override void Handle(GameClient client, StructurePacket<C2SCharacterSetOn
client.Send(new S2CCharacterSetOnlineStatusRes() {
OnlineStatus = packet.Structure.OnlineStatus
});

Database.UpdateCharacterOnlineStatus(client.Character);
}
}
}
3 changes: 3 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,8 @@ public override void Handle(GameClient client, IPacket packet)
{
S2CConnectionLogoutRes res = new S2CConnectionLogoutRes();
client.Send(res);
client.Character.OnlineStatus = OnlineStatus.Offline;
Database.UpdateCharacterOnlineStatus(client.Character);
}
}
}
Loading
Loading