Skip to content

Clan Requests#856

Open
MrDetonia wants to merge 2 commits into
sebastian-heinz:developfrom
MrDetonia:develop
Open

Clan Requests#856
MrDetonia wants to merge 2 commits into
sebastian-heinz:developfrom
MrDetonia:develop

Conversation

@MrDetonia

Copy link
Copy Markdown

Continues work on Clan Search by implementing the Clan Request feature. This allows characters to request access to a clan, with approval performed by clan owners or clan members with appropriate permissions set. The scouting system remains unimplemented.

Changes:

  • New table "ddon_clan_requests" added to the db schema. Version incremented and migration strategy written.
  • Handlers and related packet structures added for registering, cancelling, approving/rejecting, and listing clan join requests.
  • Clan permissions are respected (+ minor fix to clan search so that non-public clans are not listed).
  • New scheduled weekly task to remove stale requests older than 30 days (as suggested in-game(.
  • Changes to ClanManager's JoinClan so that offline characters can be added to a clan, and notified on next login.

Quirks / potential issues:

  • One character may only have up to one clan request at a time. This is slightly non-intuitive but the client provides feedback to that effect. The limitation here is in the client, not the server implementation (my original approach was to allow arbitrary numbers of requests per character).
  • Testing has been performed with sqlite and pgsql but only using various configurations of 2 test characters. A larger test environment (with more characters + clan members) may be beneficial to ensure there are no undesired behaviours.

Checklist:

  • The project compiles
  • The PR targets develop branch

"clan_id", "requester_character_id", "created", "approved"
};

private readonly string SqlInsertClanRequest = $"""

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Today I learned that C# will let you combine interpolated strings and raw strings.

public class ClanRequestMigration : IMigrationStrategy
{
public uint From => 43;
public uint To => 44;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This may get bumped to 45 depending on the cadence of #822

public bool CheckPermission(uint characterId, ClanPermission perm)
{
var (clanId, member) = ClanMembership(characterId);
var splitPerms = IntToEnums<ClanPermission>((int)member.Permission);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I know I wrote this stupid IntToEnums thing but apparently it's supposed to be a [Flags] Enum and then it just works without all this extra machinery.


if (shouldDelete)
{
Server.Database.ExecuteInTransaction(conn =>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

There's likely a way we can rearrange this logic such that a second transaction is not required.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Marking things as done in a commit-to-be. Has been reworked into a single transaction with network comms outside.


namespace Arrowgene.Ddon.Shared.Entity.PacketStructure
{
public class C2SClanClanJoinRequest : IPacketStructure

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

C2SClanClanRegisterJoinReq

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Marking things as done in a commit-to-be. Renamed.

{
Server.Database.InsertClanRequest(request.ClanId, client.Character.CharacterId, conn);
});
return new S2CClanClanJoinRes();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This feels like it should be sending a notice to people who are currently online and can handle the application.


namespace Arrowgene.Ddon.GameServer.Handler
{
public class ClanClanJoinRequestHandler : GameRequestPacketHandler<C2SClanClanJoinRequest, S2CClanClanJoinRes>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

See comments about the misnamed packets, which would render this ClanClanRegisterJoinHandler

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Marking things as done in a commit-to-be. Renamed.


namespace Arrowgene.Ddon.GameServer.Handler
{
public class ClanClanApproveJoinRequestHandler : GameRequestPacketHandler<C2SClanClanApproveJoinReq, S2CClanClanApproveJoinRes>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

ClanClanApproveJoinHandler

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Marking things as done in a commit-to-be. Renamed.


namespace Arrowgene.Ddon.GameServer.Handler
{
public class ClanClanCancelJoinRequestHandler : GameRequestPacketHandler<C2SClanClanCancelJoinReq, S2CClanClanCancelJoinRes>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

ClanClanCancelJoinHandler

Also this has a notice associated with it, so you likely need to be notifying other clients that the request has been cancelled.

UInt32 clanId = Server.Database.GetClanRequestsByCharacter(request.CharacterId, conn).First().ClanId;
Server.ClanManager.JoinClan(request.CharacterId, clanId, conn);

if (Server.ClientLookup.GetClientByCharacterId(request.CharacterId) == null)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is made somewhat more complicated by the fact that this player may be online, but on a different channel. You can semi-reliably find them with Server.RpcManager.FindPlayerById(request.CharacterId), but you'll have to send an RPC message to their server (if FindPlayer returns >0) to tell them that they've been accepted. You'll have to add a new command to PacketRoute to accommodate a packet for a single person.

private const string DefaultSchemaFile = "Script/schema_sqlite.sql";

public const uint Version = 43;
public const uint Version = 44;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Same as #822, we have to be careful with the merged

bool DeleteClanBaseCustomization(uint clanId, byte type, DbConnection? connectionIn = null);
List<CDataClanSearchResult> SearchClans(CDataClanSearchParam param, DbConnection? connectionIn = null);
bool InsertClanRequest(uint clanId, uint characterId, DbConnection? connectionIn = null);
List<CDataClanJoinRequest> GetClanRequestsByCharacter(uint characterId, DbConnection? connectionIn = null);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Considering there can only be one application, shouldn't these return a single entity instead of a list?

public abstract CDataClanMemberInfo GetClanMember(uint characterId, DbConnection? connectionIn = null);
public abstract bool UpdateClanMember(CDataClanMemberInfo memberInfo, uint clanId, DbConnection? connectionIn = null);
public abstract bool InsertClanRequest(uint clanId, uint characterId, DbConnection? connectionIn = null);
public abstract List<CDataClanJoinRequest> GetClanRequestsByCharacter(uint characterId, DbConnection? connectionIn = null);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

ditto

character = Server.CharacterManager.SelectCharacter(characterId, false, connectionIn);
if (character == null)
{
Logger.Error("Tried to add offline character to clan but select character query failed.");

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This should probably throw a ResponseErrorException with an appropriate error code so the user can see an error happening instead of having the client follow along thinking it succeeded

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants