diff --git a/src/model/match.py b/src/model/match.py
index e884725..046a4ea 100644
--- a/src/model/match.py
+++ b/src/model/match.py
@@ -37,6 +37,7 @@
from threading import RLock
from time import time
+from nussschale.nussschale import nconfig
from model.multideck import MultiDeck
from nussschale.util.locks import mutex, named_mutex
@@ -51,6 +52,7 @@ class Match:
Class Attributes:
frozen (bool): Whether matches are currently frozen, i.e. whether their
state transitions are disabled.
+ skip_role (str): Which users are allowed to skip the current phase.
"""
# The minimum amount of players for a match
@@ -94,6 +96,9 @@ class Match:
# Whether matches are currently frozen
frozen = False
+ # Which users are allowed to skip the phase
+ skip_role = "owner"
+
@classmethod
@named_mutex("_pool_lock")
def get_by_id(cls, id):
@@ -228,6 +233,9 @@ def __init__(self):
# The chat of this match, tuples with type/message
self._chat = [("SYSTEM", "Match was created.")]
+ # Which users are allowed to skip the phase
+ self.skip_role = nconfig().get("skip-role", "owner")
+
def put_in_pool(self):
"""Puts this match into the match pool."""
Match.add_match(self.id, self)
@@ -304,12 +312,12 @@ def get_seconds_to_next_phase(self):
# Locking is not needed here as access is atomic.
return int(self._timer - time())
- @mutex
- def user_can_skip_phase(self, part):
- """Determine whether a user can skip to the next phase.
+ def user_can_skip_phase(self, participant):
+ """Determine whether a user can skip to the next phase
Args:
- obj: The participant in question.
+ participant: The participant that made the request
+ to skip the phase
Returns:
bool: Whether the given participant can skip to the next phase
@@ -321,24 +329,50 @@ def user_can_skip_phase(self, part):
# The minimum amount of players has to be present
if len(self._participants) < Match._MINIMUM_PLAYERS:
return False
+
+ if self.skip_role == "picker":
+ if self._state == "CHOOSING":
+ return participant.picking
+ else:
+ return True
+ elif self.skip_role == "anyone":
+ return True
+ elif self.skip_role == "majority":
+ participant.wants_skip = True
+ skip_count = 0
+ for part in self.get_participants(False):
+ if part.wants_skip:
+ skip_count += 1
+ majority = int(len(list(self.get_participants(False))) / 2)
+ if skip_count > majority:
+ for part in self.get_participants(False):
+ part.wants_skip = False
+ return True
+ else:
+ self._chat.append(("SYSTEM",
+ "" + participant.nickname +
+ " wants to skip the phase. " +
+ str(majority - skip_count + 1) +
+ " request(s) left to reach majority."))
+ return False
+ else:
+ return self.get_owner_nick() == participant.nickname
- # Currently, only the owner can skip to the next phase
- return self.get_owner_nick() == part.nickname
-
- @mutex
- def skip_to_next_phase(self):
- """Skips directly to the next phase.
+ def skip_to_next_phase(self, nick):
+ """Skips directly to the next phase
- Contract:
- This method locks the match's instance lock.
+ Args:
+ nick (str): The nickname of the user who is skipping the phase.
"""
# One second difference to prevent edge cases of timer change close to
# game state transitions.
if self._timer - time() > 1:
self._timer = time()
self._chat.append(("SYSTEM",
- "" + self.get_owner_nick()
- + " skipped to the next phase."))
+ "" + nick + " skipped to next phase"))
+ else:
+ self._chat.append(("SYSTEM",
+ "Can't skip phase with less than 1 second remaining"))
def _set_state(self, state):
"""Updates the state for this match.
diff --git a/src/model/participant.py b/src/model/participant.py
index 0346a34..e29b3d2 100644
--- a/src/model/participant.py
+++ b/src/model/participant.py
@@ -52,6 +52,7 @@ class Participant:
occurring.
order: The order key of the particpant, used for shuffling.
spectator: Whether the participant is a spectator.
+ wants_skip: Whether the participant wants to skip the phase.
"""
# The number of hand cards per type
@@ -94,6 +95,9 @@ def __init__(self, id: str, nickname: str) -> None:
# participant is part of a match.
self.spectator = False
+ # Whether the particpant wants to skip the phase.
+ self.wants_skip = False
+
# The hand of this participant
self._hand = OrderedDict() # type: Dict[int, HandCard]
self._hand_counter = 1
diff --git a/src/pages/api.py b/src/pages/api.py
index c4678a4..380da46 100644
--- a/src/pages/api.py
+++ b/src/pages/api.py
@@ -311,12 +311,12 @@ def api_skip(ctx: EndpointContext) -> None:
part = match.get_participant(ctx.session["id"])
- # Check that the GET request was made by a user that can skip phases
+ # Check that the POST request was made by a user that can skip phases
if not match.user_can_skip_phase(part):
raise HTTPException.forbidden(True, "not authorized to skip phase")
# Skip remaining time
- match.skip_to_next_phase()
+ match.skip_to_next_phase(part.nickname)
ctx.json_ok()