Implemented alot of methods in ExpansionsManager
This commit is contained in:
@ -1,7 +1,4 @@
|
|||||||
"""
|
"""
|
||||||
Needs to manage any number of players
|
|
||||||
Needs to work with mulitple rounds/turns
|
|
||||||
Needs to handle modifying number of players
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -10,10 +7,23 @@ import copy
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import PDOLib
|
||||||
|
|
||||||
_log = logging.getLogger('NoPE-Lib')
|
_log = logging.getLogger('NoPE-Lib')
|
||||||
|
|
||||||
|
|
||||||
|
class Hook:
|
||||||
|
def __init__(self):
|
||||||
|
self._slot = []
|
||||||
|
|
||||||
|
def __call__(self, *args, **kwargs):
|
||||||
|
for fct in self._slot:
|
||||||
|
fct(*args, **kwargs)
|
||||||
|
|
||||||
|
def connect(self, fct):
|
||||||
|
self._slot.append(fct)
|
||||||
|
|
||||||
|
|
||||||
class PlayersManager:
|
class PlayersManager:
|
||||||
"""
|
"""
|
||||||
Manager of players for a given game.
|
Manager of players for a given game.
|
||||||
@ -22,20 +32,21 @@ class PlayersManager:
|
|||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
gameID (str): The gameID of the active game
|
gameID (str): The gameID of the active game
|
||||||
|
config (dict): The in memory state of the config file
|
||||||
|
|
||||||
Methods:
|
Methods:
|
||||||
save:
|
save:
|
||||||
"""
|
"""
|
||||||
defaultPlayerConfig = {"flags": [], "expansions": {}, "games": {}}
|
defaultPlayerConfig = {"flags": [], "expansions": {}, "games": {}}
|
||||||
|
|
||||||
def __init__(self, gameID: str=None, activePlayers: list[str]=None, playersPath: str='./players.json', loggerID: str='PlayersManager', includedExpansions: tuple[str]=None):
|
def __init__(self, gameID: str=None, activePlayers: list[str]=None, playersPath: str='../players.json', loggerID: str='PlayersManager', includedExpansions: tuple[str]=None):
|
||||||
"""
|
"""
|
||||||
Initialises a list of players.
|
Initialises a list of players.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
gameID (str): The ID of the game used in the configuration file.
|
gameID (str): The ID of the game used in the configuration file.
|
||||||
activePlayers (list of str): The names of the players that are playing. Defaults to none which includes all players.
|
activePlayers (list of str): The names of the players that are playing. Defaults to none which includes all players.
|
||||||
playersPath (str, optional): The path of the players.json file. Defaults to './players.json'.
|
playersPath (str, optional): The path of the players.json file. Defaults to '../players.json'.
|
||||||
loggerID (str, optional): The ID used for logging. Defaults to 'Players'.
|
loggerID (str, optional): The ID used for logging. Defaults to 'Players'.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -54,45 +65,45 @@ class PlayersManager:
|
|||||||
for name, player_cfg in self._cfg["players"].items()
|
for name, player_cfg in self._cfg["players"].items()
|
||||||
if name in activePlayers
|
if name in activePlayers
|
||||||
}
|
}
|
||||||
|
# Create the signals
|
||||||
|
self.onMadePlayerActive = Hook()
|
||||||
|
self.onMadePlayerInactive = Hook()
|
||||||
|
|
||||||
def __getitem__(self, playerName: str):
|
def __getitem__(self, playerName: str):
|
||||||
"""
|
"""
|
||||||
Get a player object.
|
Get a player object.
|
||||||
If the player wasn't active, make them active.
|
If the player is not active, make them active.
|
||||||
If the player didn't exist previously, create a default config and make them active.
|
If the player didn't exist previously, create a default config and make them active.
|
||||||
"""
|
"""
|
||||||
if playerName not in self._cfg["players"]:
|
if playerName not in self._player_data:
|
||||||
# Create the brand new player
|
|
||||||
self._cfg["players"][playerName] = copy.deepcopy(self.defaultPlayerConfig)
|
|
||||||
newPlayer = Player(playerName, self, **self._cfg["players"][playerName])
|
|
||||||
# Make the player active
|
|
||||||
self._player_data[playerName] = newPlayer
|
|
||||||
self._log.debug(f"Created a new player called {playerName}")
|
|
||||||
return newPlayer
|
|
||||||
elif playerName not in self._player_data:
|
|
||||||
# Fetch the player's data and make them active
|
# Fetch the player's data and make them active
|
||||||
playerAdded = Player(playerName, self, **self._cfg["players"][playerName])
|
if playerName in self._cfg["players"]:
|
||||||
self._player_data[playerName] = playerAdded
|
self[playerName] = self._cfg["players"][playerName]
|
||||||
self._log.debug(f"Fetched {playerName}'s data and made them active")
|
else:
|
||||||
return playerAdded
|
self[playerName] = copy.deepcopy(self.defaultPlayerConfig)
|
||||||
else:
|
self._log.debug(f"Made {playerName} active")
|
||||||
return self._player_data[playerName]
|
self.onMadePlayerActive(playerName)
|
||||||
|
return self._player_data[playerName]
|
||||||
|
|
||||||
def __setitem__(self, playerName: str, config: dict, makeNewPlayerObject=False):
|
def __setitem__(self, playerName: str, config: dict, makeNewPlayerObject=False):
|
||||||
"""
|
"""
|
||||||
Replace a player's config with another one. It possible to also automatically replace the
|
Replace a player's config with another one.
|
||||||
player object by specifying the corresponding argument.
|
|
||||||
|
Arguments:
|
||||||
|
playerName (str):
|
||||||
|
config (dict):
|
||||||
|
makeNewPlayerObject (bool):
|
||||||
"""
|
"""
|
||||||
self._log.debug(f"Changed {playerName}'s config")
|
|
||||||
if makeNewPlayerObject:
|
if makeNewPlayerObject:
|
||||||
previousInstance = self._player_data[playerName]
|
previousInstance = self._player_data[playerName]
|
||||||
self._player_data[playerName] = Player(playerName, self, **config)
|
self._player_data[playerName] = Player(playerName, self, **config)
|
||||||
del previousInstance
|
del previousInstance
|
||||||
self._cfg["players"][playerName] = config
|
self._cfg["players"][playerName] = config
|
||||||
|
self._log.debug(f"Changed {playerName}'s config")
|
||||||
|
|
||||||
def __delitem__(self, playerName):
|
def __delitem__(self, playerName):
|
||||||
self._log.debug(f"Removing {playerName} from active players")
|
|
||||||
del self._player_data[playerName]
|
del self._player_data[playerName]
|
||||||
|
self._log.debug(f"Removed {playerName} from active players")
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self._player_data)
|
return len(self._player_data)
|
||||||
@ -103,6 +114,10 @@ class PlayersManager:
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"Manager is playing {self.gameID} with {len(self._player_data)} active players"
|
return f"Manager is playing {self.gameID} with {len(self._player_data)} active players"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def config(self):
|
||||||
|
return copy.deepcopy(self._cfg)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def gameID(self):
|
def gameID(self):
|
||||||
return self._currentGameID
|
return self._currentGameID
|
||||||
@ -146,6 +161,7 @@ class Player:
|
|||||||
|
|
||||||
Methods:
|
Methods:
|
||||||
generateConfig:
|
generateConfig:
|
||||||
|
punish:
|
||||||
"""
|
"""
|
||||||
def __init__(self, name: str, manager: PlayersManager, **cfg):
|
def __init__(self, name: str, manager: PlayersManager, **cfg):
|
||||||
self._name = name
|
self._name = name
|
||||||
@ -195,7 +211,7 @@ class Player:
|
|||||||
def punish(self, value, preferedExpansion: str=None):
|
def punish(self, value, preferedExpansion: str=None):
|
||||||
do_something = lambda value: value
|
do_something = lambda value: value
|
||||||
additionalInfos = {
|
additionalInfos = {
|
||||||
"expansionID": "challengeDB",
|
"expansionClass": "challengeDB",
|
||||||
"balancedValue": 0.2,
|
"balancedValue": 0.2,
|
||||||
"showOnScreen": "Do 100 push-up",
|
"showOnScreen": "Do 100 push-up",
|
||||||
"error": None,
|
"error": None,
|
||||||
@ -210,6 +226,7 @@ class Expansion:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
|
specs:
|
||||||
|
|
||||||
Methods:
|
Methods:
|
||||||
step:
|
step:
|
||||||
@ -217,13 +234,19 @@ class Expansion:
|
|||||||
close:
|
close:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""
|
"""
|
||||||
Raise an error if not available
|
Intended behavior of the method:
|
||||||
|
Initialise the creation of an expansion
|
||||||
|
Should NEVER use any kind of argument.
|
||||||
|
Raise an error if not available.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def specs(self):
|
||||||
|
return self._specs
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def step(self, action):
|
def step(self, action):
|
||||||
"""
|
"""
|
||||||
@ -239,6 +262,9 @@ class Expansion:
|
|||||||
def close(self):
|
def close(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def makeSpecs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ExpansionsManager:
|
class ExpansionsManager:
|
||||||
"""
|
"""
|
||||||
@ -247,15 +273,32 @@ class ExpansionsManager:
|
|||||||
Attributes:
|
Attributes:
|
||||||
includedExpansions: tuple of str
|
includedExpansions: tuple of str
|
||||||
Container of the expansions to try making available
|
Container of the expansions to try making available
|
||||||
releventExpansions: dict of Expansion
|
activeExpansions: dict of Expansion
|
||||||
Container of the relevent expansions
|
Container of the relevent expansions
|
||||||
|
|
||||||
|
TODO Add an interface to allow the modification of the expansion settings
|
||||||
"""
|
"""
|
||||||
|
defaultExpansionConfig = {"players": (), "types": ()}
|
||||||
|
keysConvert2Tuple = ("players", "types")
|
||||||
tags = ["shock", "spice", "sour", "drink", "challenge"]
|
tags = ["shock", "spice", "sour", "drink", "challenge"]
|
||||||
|
|
||||||
def __init__(self, playersManager: PlayersManager, includedExpansions: tuple[str]=None):
|
def __init__(self, playersManager: PlayersManager, includedExpansions: tuple[str]=None):
|
||||||
|
# Create the attributes
|
||||||
self.playersManager = playersManager
|
self.playersManager = playersManager
|
||||||
|
self._cfg = playersManager.config["expansions"]
|
||||||
self._includedExpansions = tuple(includedExpansions) if includedExpansions is not None else ()
|
self._includedExpansions = tuple(includedExpansions) if includedExpansions is not None else ()
|
||||||
self._releventExpansions = {} # TODO Populate the dictionnary
|
# Convert the required lists into tuples
|
||||||
|
for expansionID in self._cfg["expansions"]:
|
||||||
|
for key in self.keysConvert2Tuple:
|
||||||
|
self._cfg["expansions"][expansionID][key] = tuple(self._cfg["expansions"][expansionID][key])
|
||||||
|
# Compute the active expansions
|
||||||
|
self._activeExpansions = {}
|
||||||
|
for expansionID in self._listPossiblyValidExpansions():
|
||||||
|
self._createExpansion(expansionID)
|
||||||
|
|
||||||
|
# Connect the signals to the slots
|
||||||
|
playersManager.onMadePlayerActive.connect(self._activePlayerAdded)
|
||||||
|
playersManager.onMadePlayerInactive.connect(self._activePlayerRemoved)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def includedExpansions(self):
|
def includedExpansions(self):
|
||||||
@ -264,40 +307,104 @@ class ExpansionsManager:
|
|||||||
@includedExpansions.setter
|
@includedExpansions.setter
|
||||||
def includedExpansions(self, newIncluded: tuple[str]):
|
def includedExpansions(self, newIncluded: tuple[str]):
|
||||||
self._includedExpansions = tuple(newIncluded)
|
self._includedExpansions = tuple(newIncluded)
|
||||||
self._includedChanged()
|
self._includeChanged()
|
||||||
|
|
||||||
@includedExpansions.deleter
|
@includedExpansions.deleter
|
||||||
def includedExpansions(self):
|
def includedExpansions(self):
|
||||||
self._includedExpansions = ()
|
self._includedExpansions = ()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def releventExpansions(self):
|
def activeExpansions(self):
|
||||||
return self._releventExpansions
|
return self._activeExpansions
|
||||||
|
|
||||||
def _includedChanged(self):
|
def _getPlayersFillMissing(self, expansionID):
|
||||||
"""
|
return set(self._cfg["expansions"].get(expansionID, {}).get("players", []))
|
||||||
Compute the list of only the relevant expansions.
|
|
||||||
The expansions that do not exists are excluded.
|
|
||||||
The expansions that have no players assigned to them are excluded.
|
|
||||||
The expansions that are not responding are excluded.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _activePlayersChanged(self, activatePlayers):
|
def _listPossiblyValidExpansions(self):
|
||||||
pass
|
"""
|
||||||
|
List expansions that are included, defined in PDOLib and are available to at least one active player.
|
||||||
|
"""
|
||||||
|
activePlayers = set(self.playersManager.keys())
|
||||||
|
possiblyValidExpansions = [
|
||||||
|
expansion for expansion in self._includedExpansions
|
||||||
|
if hasattr(PDOLib, expansion) and not self._getPlayersFillMissing(expansion).isdisjoint(activePlayers)
|
||||||
|
]
|
||||||
|
return possiblyValidExpansions
|
||||||
|
|
||||||
|
def _createExpansion(self, expansionID):
|
||||||
|
try:
|
||||||
|
expansion = getattr(PDOLib, expansionID)()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self._activeExpansions[expansionID] = expansion
|
||||||
|
|
||||||
|
def _removeExpansion(self, expansionID):
|
||||||
|
expansion = self._activeExpansions.pop(expansionID)
|
||||||
|
expansion.close()
|
||||||
|
del expansion
|
||||||
|
|
||||||
|
def _includeChanged(self):
|
||||||
|
"""
|
||||||
|
Update the list of active Expansions
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Compute the expansions involved in the modification
|
||||||
|
possiblyValidExpansions = set(self._listPossiblyValidExpansions())
|
||||||
|
previousExpansions = set(self._includedExpansions)
|
||||||
|
# Remove irrelevant expansions
|
||||||
|
expansionsToRemove = previousExpansions.difference(possiblyValidExpansions)
|
||||||
|
for expansionID in expansionsToRemove:
|
||||||
|
self._activeExpansions.pop(expansionID).close()
|
||||||
|
# Add the new expansions
|
||||||
|
expansionsToAdd = possiblyValidExpansions.difference(previousExpansions)
|
||||||
|
for expansionID in expansionsToAdd:
|
||||||
|
self._createExpansion(expansionID)
|
||||||
|
|
||||||
|
def _activePlayerAdded(self, playerName):
|
||||||
|
# Find the expansions that are to be created
|
||||||
|
possibleAddition = set(self._includedExpansions).difference(self.activeExpansions)
|
||||||
|
filteredAddition = [
|
||||||
|
expansionID for expansionID in possibleAddition
|
||||||
|
if hasattr(PDOLib, expansionID) and playerName in self._getPlayersFillMissing(expansionID)
|
||||||
|
]
|
||||||
|
# Create the expansions
|
||||||
|
for expansionID in filteredAddition:
|
||||||
|
self._createExpansion(expansionID)
|
||||||
|
|
||||||
|
def _activePlayerRemoved(self, playerName):
|
||||||
|
# Find the expansions that are to be created
|
||||||
|
filteredRemoval = [
|
||||||
|
expansionID for expansionID in self.activeExpansions
|
||||||
|
if playerName in self._cfg["players"] and len(self._cfg["players"])
|
||||||
|
]
|
||||||
|
# Close the expansions
|
||||||
|
for expansionID in filteredRemoval:
|
||||||
|
self._removeExpansion(expansionID)
|
||||||
|
|
||||||
def _errorOccured(self, expansionID):
|
def _errorOccured(self, expansionID):
|
||||||
pass
|
self._removeExpansion(expansionID)
|
||||||
|
|
||||||
def _expansionPlayersChanged(self, expansionID):
|
def _expansionPlayersChanged(self, expansionID):
|
||||||
pass
|
expansionIsActive = expansionID in self._activeExpansions
|
||||||
|
activePlayers = set(self.playersManager.keys())
|
||||||
|
expansionHasNoPlayers = self._getPlayersFillMissing(expansionID).isdisjoint(activePlayers)
|
||||||
|
expansionDefined = hasattr(PDOLib, expansionID)
|
||||||
|
if expansionIsActive and expansionHasNoPlayers:
|
||||||
|
self._removeExpansion(expansionID)
|
||||||
|
elif (not expansionIsActive) and (not expansionHasNoPlayers) and expansionDefined:
|
||||||
|
self._createExpansion(expansionID)
|
||||||
|
|
||||||
def resetExpansion(self, expansion: Expansion, suppressError: bool=False):
|
def initilizeExpansion(self, expansionID: str, suppressError: bool=False):
|
||||||
"""
|
"""
|
||||||
If the expansion was relevent, close it.
|
Initilise the expansion.
|
||||||
In any case, try creating a new
|
|
||||||
"""
|
"""
|
||||||
# If the expansion was relevent, close it
|
if expansionID in self._activeExpansions:
|
||||||
|
self._removeExpansion(expansionID)
|
||||||
|
if not suppressError:
|
||||||
|
# Make sure that the expansion is relevent
|
||||||
|
# Verify that creating the expansion was successful
|
||||||
|
pass
|
||||||
# Try creating a new instance of the expansion
|
# Try creating a new instance of the expansion
|
||||||
# Update the list of relevent expansions
|
# Update the list of relevent expansions
|
||||||
pass
|
pass
|
||||||
@ -315,3 +422,4 @@ if __name__ == "__main__":
|
|||||||
brosef.gameSave = [1]
|
brosef.gameSave = [1]
|
||||||
print(manager["Brosef"].gameSave)
|
print(manager["Brosef"].gameSave)
|
||||||
brosef.state = ["alive"]
|
brosef.state = ["alive"]
|
||||||
|
# TODO Verify that changing the gameID works as intended
|
||||||
Reference in New Issue
Block a user