From 0bff92c6258fc5b918fe976371f7b1b22c0b3284 Mon Sep 17 00:00:00 2001
From: Unbistrackted <112902220+Unbistrackted@users.noreply.github.com>
Date: Thu, 12 Mar 2026 06:11:56 -0300
Subject: [PATCH] feat: initial implementation
needs testing
---
.../Features/Votes/UserIndentifier.cs | 8 +-
Callvote.API/Features/Votes/Vote.cs | 42 ++++++-
Callvote.API/Features/Votes/VoteHandler.cs | 108 +++++++++++++-----
3 files changed, 121 insertions(+), 37 deletions(-)
diff --git a/Callvote.API/Features/Votes/UserIndentifier.cs b/Callvote.API/Features/Votes/UserIndentifier.cs
index 572e5d6..c10756b 100644
--- a/Callvote.API/Features/Votes/UserIndentifier.cs
+++ b/Callvote.API/Features/Votes/UserIndentifier.cs
@@ -51,7 +51,7 @@ public UserIndentifier(int userId, string name, string uniqueId)
/// The first instance to compare.
/// The second instance to compare.
/// true if the two instances are equal; otherwise, false.
- public static bool operator ==(UserIndentifier left, UserIndentifier right) => left.Equals(right);
+ public static bool operator ==(UserIndentifier left, UserIndentifier right) => (left == null || right == null) && left.Equals(right);
///
/// Determines whether two instances of the class are not equal.
@@ -59,13 +59,13 @@ public UserIndentifier(int userId, string name, string uniqueId)
/// The first instance to compare.
/// The second instance to compare.
/// true if the two instances not are equal; otherwise, false.
- public static bool operator !=(UserIndentifier left, UserIndentifier right) => !left.Equals(right);
+ public static bool operator !=(UserIndentifier left, UserIndentifier right) => (left == null || right == null) && !left.Equals(right);
///
- public bool Equals(UserIndentifier other) => this.UniqueUserId == other.UniqueUserId && this.UserId == other.UserId;
+ public override bool Equals(object obj) => obj is UserIndentifier other && this.Equals(other);
///
- public override bool Equals(object obj) => obj is UserIndentifier other && this.Equals(other);
+ public bool Equals(UserIndentifier other) => other is not null && this.UniqueUserId == other.UniqueUserId;
///
public override int GetHashCode() => this.UniqueUserId.GetHashCode();
diff --git a/Callvote.API/Features/Votes/Vote.cs b/Callvote.API/Features/Votes/Vote.cs
index c54a79d..48b09c5 100644
--- a/Callvote.API/Features/Votes/Vote.cs
+++ b/Callvote.API/Features/Votes/Vote.cs
@@ -432,19 +432,57 @@ public virtual string BuildResultsMessage()
///
/// Starts the by registering the commands and starting the coroutine.
///
- internal void StartVote()
+ /// Returns the .
+ internal CallVoteStatus StartVote()
{
+ CallingVoteEventArgs e = new(this);
+ EventsHandlers.OnCallingVote(e);
+ if (!e.IsAllowed)
+ {
+ return CallVoteStatus.VoteCancelled;
+ }
+
this.RegisterAllVoteOptions();
this.StartVoteCoroutine();
+
+ CalledVoteEventArgs ev = new(this);
+ EventsHandlers.OnCalledVote(ev);
+ return CallVoteStatus.VoteStarted;
}
///
/// Stops the by unregistering the command and stopping the coroutine.
///
- internal void FinishVote()
+ /// If the voting will display the results message or invoke the Callback.
+ internal void FinishVote(bool isForced = false)
{
+ VoteEndingEventArgs e = new(this);
+ if (!e.IsAllowed)
+ {
+ return;
+ }
+
this.UnregisterVoteOptionsCommand();
this.StopVoteCoroutine();
+
+ if (!isForced)
+ {
+ return;
+ }
+
+ if (this.Callback == null)
+ {
+ DisplayHandler.Show(this.ResultsMessageDuration, this.BuildResultsMessage(), this.AllowedPlayers);
+ }
+ else
+ {
+ this?.Callback.Invoke(this);
+ }
+
+ // Prevents being null when it's later used in the event
+ Vote vote = this;
+ VoteEndedEventArgs ev = new(vote);
+ EventsHandlers.OnVoteEnded(ev);
}
///
diff --git a/Callvote.API/Features/Votes/VoteHandler.cs b/Callvote.API/Features/Votes/VoteHandler.cs
index 833122a..a0a6d30 100644
--- a/Callvote.API/Features/Votes/VoteHandler.cs
+++ b/Callvote.API/Features/Votes/VoteHandler.cs
@@ -1,8 +1,8 @@
using System;
+using System.Collections.Generic;
+using System.Linq;
using Callvote.API.Enums;
-using Callvote.API.Events;
-using Callvote.API.Events.EventArgs;
-using Callvote.API.Features.Displays;
+using Utils.NonAllocLINQ;
namespace Callvote.API.Features.Votes
{
@@ -23,6 +23,10 @@ static VoteHandler()
}
#endif
+ public static IReadOnlyCollection ReadOnlyActiveParallelVotes => ActiveParallelVotes;
+
+ internal static HashSet ActiveParallelVotes { get; } = new HashSet();
+
///
/// Gets the currently active instance. Null when no vote is in progress.
///
@@ -43,28 +47,25 @@ static VoteHandler()
/// If queueing is enabled the will be enqueued and the is started immediately.
///
/// The to start or enqueue.
+ /// If the vote will be ran in parallel.
/// A representing if the action was sucessfull, or for example, if the queue is full.
- public static CallVoteStatus CallVote(Vote vote)
+ public static CallVoteStatus CallVote(Vote vote, bool isParallel = false)
{
if (vote == null)
{
throw new ArgumentNullException(nameof(vote), "Vote cannot be null!");
}
- if (!IsVoteActive)
+ if (isParallel)
{
- CallingVoteEventArgs e = new(vote);
- EventsHandlers.OnCallingVote(e);
- if (!e.IsAllowed)
- {
- return CallVoteStatus.VoteCancelled;
- }
+ ActiveParallelVotes.Add(vote);
+ return CurrentVote.StartVote();
+ }
+ if (!IsVoteActive)
+ {
CurrentVote = vote;
- CurrentVote.StartVote();
- CalledVoteEventArgs ev = new(CurrentVote);
- EventsHandlers.OnCalledVote(ev);
- return CallVoteStatus.VoteStarted;
+ return CurrentVote.StartVote();
}
return CallVoteStatus.VoteInProgress;
@@ -82,31 +83,76 @@ public static void FinishVote(bool isForced = false)
return;
}
- VoteEndingEventArgs e = new(CurrentVote);
- if (!e.IsAllowed)
+ CurrentVote?.FinishVote(isForced);
+
+ CurrentVote = null;
+ }
+
+ public static void StopParallelVote(Vote vote, bool isForced = false)
+ {
+ if (vote == null)
+ {
+ return;
+ }
+
+ if (!ActiveParallelVotes.Contains(vote) && !vote.IsCoroutineActive)
{
return;
}
- CurrentVote?.FinishVote();
+ CurrentVote?.FinishVote(isForced);
+ ActiveParallelVotes.Remove(vote);
+ }
+
+ public static void StopParallelVote(long voteId, bool isForced = false)
+ {
+ if (voteId == 0)
+ {
+ return;
+ }
- if (!isForced)
+ if (!ActiveParallelVotes.TryGetFirst(v => v.VoteId == voteId, out Vote vote))
{
- if (CurrentVote?.Callback == null)
- {
- DisplayHandler.Show(CurrentVote.ResultsMessageDuration, CurrentVote.BuildResultsMessage(), CurrentVote.AllowedPlayers);
- }
- else
- {
- CurrentVote?.Callback.Invoke(CurrentVote);
- }
+ return;
}
- Vote vote = CurrentVote;
- CurrentVote = null;
+ StopParallelVote(vote, isForced);
+ }
+
+ public static void StopParallelVotes(UserIndentifier user, bool isForced = false)
+ {
+ if (user == null)
+ {
+ return;
+ }
+
+ foreach (Vote vote in ActiveParallelVotes.Where(v => v.CallVotePlayer == user))
+ {
+ StopParallelVote(vote, isForced);
+ }
+ }
+
+ public static void StopParallelVotes(IEnumerable votes, bool isForced = false)
+ {
+ if (votes == null)
+ {
+ return;
+ }
+
+ foreach (Vote vote in votes)
+ {
+ StopParallelVote(vote, isForced);
+ }
+ }
+
+ public static void StopAllParallelVotes(bool isForced = false)
+ {
+ foreach (Vote vote in ActiveParallelVotes)
+ {
+ StopParallelVote(vote, isForced);
+ }
- VoteEndedEventArgs ev = new(vote);
- EventsHandlers.OnVoteEnded(ev);
+ ActiveParallelVotes.Clear();
}
///