1414#
1515# Group names are case insensitive. They will always be converted to lower case internally (however you should use lower case for group names in the config!)
1616
17- # CONFIG Current Log File Version
18- # 1.Milestone.Subversion
19- LOGFILE_VERSION = "2.0.4"
17+ # CONFIG Current Log File Version.
18+ # NOTE Also change this in the Dockerfile
19+ LOGFILE_VERSION = "2.1.0" # Major.Milestone.Subversion
2020
2121PSEUDONYM_LENGTH = 32
2222LEVEL_ENCODING = 'UTF-8' # was Windows-1252
2323TIME_DRIFT_THRESHOLD = 200 # ms
2424STALE_LOGFILE_TIME = 48 * 60 * 60 # close logfiles after 48h
2525MAX_ERROR_LOGS_PER_PLAYER = 25
2626
27+ # Number of seconds, after which the player is considered disconnected. A "Back Online"
28+ # message will be printed to the log, if the player connects afterwards. Also used for the
29+ # Prometheus Online Player Count metric
30+ BACK_ONLINE_THRESHOLD_S = 5.0 # [s]
31+
32+ # The interval at which prometheus metrics without an event source shall be updated
33+ METRIC_UPDATE_INTERVAL = 1 # [s]
34+
2735# NOTE: This is used when the client needs to request assets from the server. If you need
2836# the server side asset folder, use gameConfig.getAssetPath()
2937REVERSIM_STATIC_URL = "/assets"
3038
39+ DEFAULT_FOOTER = {
40+ "researchInfo" : REVERSIM_STATIC_URL + "/researchInfo/researchInfo.html"
41+ }
42+
3143class GroupNotFound (Exception ):
3244 """Raised when a group is requested, which is not in the config"""
3345 pass
@@ -89,7 +101,7 @@ def getDefaultGamerules() -> dict[str, Optional[Union[str, int, bool, dict[str,
89101
90102 "allowRepetition" : False ,
91103
92- "footer" : getFooter () ,
104+ "footer" : DEFAULT_FOOTER ,
93105
94106 "urlPreSurvey" : None ,
95107 "urlPostSurvey" : None ,
@@ -98,26 +110,34 @@ def getDefaultGamerules() -> dict[str, Optional[Union[str, int, bool, dict[str,
98110 }
99111
100112# Default gamerules, will be overridden by the gamerules defined inside the group
101- gameruleDefault = None
113+ gameruleDefault = getDefaultGamerules ()
114+
115+
116+ def load_config (fileName : str , instanceFolder : str | None = None ) -> dict [str , Any ]:
117+ """Helper to load a JSON configuration relative to the Flask instance folder into a `dict`"""
118+
119+ if instanceFolder is None :
120+ instanceFolder = getInstanceFolder ()
121+
122+ configPath = safe_join (instanceFolder , fileName )
123+ with open (configPath , "r" , encoding = LEVEL_ENCODING ) as f :
124+ # Load Config file & fill default gamerules
125+ logging .info (f'Loading config "{ configPath } "...' )
126+ return json .load (f )
102127
103128
104- def loadConfig (configName : str = "conf/gameConfig.json" , instanceFolder : str = 'instance' ):
129+ def loadGameConfig (configName : str = "conf/gameConfig.json" , instanceFolder : str = 'instance' ):
105130 """Read gameConfig.json into the config variable"""
106131 global __configStorage , __instance_folder
107132 __instance_folder = instanceFolder
108133
109134 # load the config (groups, gamerules etc.)
110135 try :
111- configPath = safe_join (instanceFolder , configName )
112- with open (configPath , "r" , encoding = "utf-8" ) as f :
113- # Load Config file & fill default gamerules
114- logging .info ('Loading config "' + configPath + '"...' )
115- __configStorage = json .load (f )
116- gameruleDefault = getDefaultGamerules ()
136+ __configStorage = load_config (fileName = configName , instanceFolder = instanceFolder )
117137
118- # Get Git Hash from Config
119- __configStorage ['gitHash' ] = get_git_revision_hash (shortHash = True )
120- logging .info ("Game Version: " + LOGFILE_VERSION + "-" + getGitHash ())
138+ # Get Git Hash from Config
139+ __configStorage ['gitHash' ] = get_git_revision_hash (shortHash = True )
140+ logging .info ("Game Version: " + LOGFILE_VERSION + "-" + getGitHash ())
121141
122142 # Validate and initialize all groups / add default gamerule
123143 for g in __configStorage ['groups' ]:
@@ -255,9 +275,6 @@ def getDefaultLang() -> str:
255275
256276def getFooter () -> Dict [str , str ]:
257277 """Get the footer from the config or return the Default Footer if none is specified"""
258- DEFAULT_FOOTER = {
259- "researchInfo" : REVERSIM_STATIC_URL + "/researchInfo/researchInfo.html"
260- }
261278 return config ('footer' , DEFAULT_FOOTER )
262279
263280
@@ -301,6 +318,14 @@ def getGroupsDisabledErrorLogging() -> list[str]:
301318 ]
302319
303320
321+ def getLevelList (name : str ):
322+ """Get a level list in the new format"""
323+ try :
324+ return __configStorage ['levels' ][name ]
325+ except KeyError :
326+ raise GroupNotFound ("Could not find the level list with name '" + name + "'!" )
327+
328+
304329#########################
305330# Phase Constants #
306331#########################
@@ -329,18 +354,18 @@ def getGroupsDisabledErrorLogging() -> list[str]:
329354# NOTE Special case: 'text' is written in the level list, but 'info' is send to the server,
330355# see doc/Overview.md#levels-info-screens-etc
331356REMAP_LEVEL_TYPES = {
332- 'text' : 'info'
357+ 'text' : LevelType . INFO
333358}
334359
335360# The new types for the Alternative Task shall also be treated as levels aka tasks
336- LEVEL_FILETYPES_WITH_TASK = ['level' , 'url' , 'iframe' ]
361+ LEVEL_FILETYPES_WITH_TASK = [LevelType . LEVEL , LevelType . URL , LevelType . IFRAME ]
337362
338363LEVEL_BASE_FOLDER = 'levels'
339- LEVEL_FILE_PATHS = {
340- 'level' : LEVEL_BASE_FOLDER + '/differentComplexityLevels/' ,
341- 'info' : LEVEL_BASE_FOLDER + '/infoPanel/' ,
342- 'tutorial' : LEVEL_BASE_FOLDER + '/elementIntroduction/' ,
343- 'special' : LEVEL_BASE_FOLDER + '/special/'
364+ LEVEL_FILE_PATHS : dict [ str , str ] = {
365+ LevelType . LEVEL : LEVEL_BASE_FOLDER + '/differentComplexityLevels/' ,
366+ LevelType . INFO : LEVEL_BASE_FOLDER + '/infoPanel/' ,
367+ LevelType . TUTORIAL : LEVEL_BASE_FOLDER + '/elementIntroduction/' ,
368+ LevelType . SPECIAL : LEVEL_BASE_FOLDER + '/special/'
344369}
345370
346371# config name for the pause timer
0 commit comments