Skip to content

Commit c0a1a93

Browse files
authored
Merge pull request #3 from emsec/dev
Release 2.1.0
2 parents be9efa5 + b0cdb21 commit c0a1a93

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+2419
-878
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ __pycache__/
2121
env/
2222
venv/
2323
.venv/
24+
tmp/
2425

2526
# --- Debugpy logs, WinMerge Backups etc.
2627
*.log

.vscode/extensions.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@
55
// List of extensions which should be recommended for users of this workspace.
66
"recommendations": [
77
"firefox-devtools.vscode-firefox-debug",
8-
"wholroyd.jinja",
8+
"samuelcolvin.jinjahtml",
99
"ms-python.python",
1010
"ms-python.vscode-pylance",
1111
"streetsidesoftware.code-spell-checker",
12-
"DrMerfy.overtype",
1312
"bierner.markdown-mermaid",
1413
"alexfromxd.showtime",
1514
"ms-toolsai.datawrangler",

.vscode/launch.json

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"module": "flask",
99
"env": {
1010
"FLASK_APP": "gameServer.py",
11-
"FLASK_DEBUG": "1"
11+
"FLASK_DEBUG": "1",
12+
"PROMETHEUS_MULTIPROC_DIR": "tmp"
1213
},
1314
"args": [
1415
"run",
@@ -30,10 +31,14 @@
3031
"static/src/externalLibraries/**"
3132
],
3233
"preferences": {
34+
"browser.startup.homepage": "http://localhost:8000/index",
3335
"browser.translations.automaticallyPopup": false,
3436
"browser.translations.neverTranslateLanguages": "en",
3537
"browser.search.suggest.enabled": false,
36-
"browser.urlbar.suggest.searches": false
38+
"browser.urlbar.suggest.searches": false,
39+
"browser.engagement.sidebar-button.has-used": true,
40+
"sidebar.new-sidebar.has-used": true,
41+
"browser.startup.windowsLaunchOnLogin.disableLaunchOnLoginPrompt": true
3742
}
3843
},
3944
{
@@ -44,8 +49,12 @@
4449
"justMyCode": true,
4550
"console": "integratedTerminal",
4651
"args": [
47-
"${input:screenshotGeneratorPseudonym}"
48-
]
52+
"${input:screenshotGeneratorPseudonym}",
53+
"-g", "cognitive_obfuscation"
54+
],
55+
"env": {
56+
"REVERSIM_INSTANCE": "./reversim-conf",
57+
}
4958
},
5059
{
5160
"name": "Firefox & BurpProxy",
@@ -70,6 +79,6 @@
7079
"id": "screenshotGeneratorPseudonym",
7180
"description": "The pseudonym to generate the screenshots with. Create it by starting the game in a group with the level viewer.",
7281
"type": "promptString"
73-
},
82+
}
7483
]
7584
}

.vscode/settings.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
// Syntax highlighting for Jinja templates
4343
"files.associations": {
4444
"**/templates/*.html": "jinja",
45-
"**/logFile_*.txt": "log"
45+
"**/logFile_*.txt": "log",
4646
},
4747

4848
// Try to guess the Encoding of the games level files and languageDict
@@ -67,6 +67,8 @@
6767
"editor.wordWrapColumn": 120
6868
},
6969

70+
"ruff.lineLength": 90, // 90ish, same as the first rule in the editor
71+
7072
// Defaults for Overtype (the EINFG key in german)
7173
"overtype.perEditor": false,
7274
"overtype.paste": true,
@@ -97,7 +99,9 @@
9799
"camou",
98100
"Camou",
99101
"chrono",
102+
"Cityblock",
100103
"clow",
104+
"Cogn",
101105
"colorama",
102106
"Cooldown",
103107
"cpus",
@@ -129,6 +133,7 @@
129133
"getpid",
130134
"gnds",
131135
"graphviz",
136+
"gunicorn",
132137
"handdrawn",
133138
"hardmode",
134139
"highscore",
@@ -157,16 +162,21 @@
157162
"levelname",
158163
"limesurvey",
159164
"linestretch",
165+
"livesum",
160166
"localtime",
161167
"logfile",
162168
"logfiles",
163169
"logto",
164170
"markupsafe",
165171
"matplotlib",
166172
"millis",
173+
"Minkowski",
167174
"MITM",
168175
"monobackgroundcolor",
176+
"mostrecent",
169177
"Multiport",
178+
"MULTIPROC",
179+
"multiversion",
170180
"netlist",
171181
"netlists",
172182
"nmbr",
@@ -220,6 +230,7 @@
220230
"TIMESYNC",
221231
"tweens",
222232
"tzdata",
233+
"uinput",
223234
"unforgeable",
224235
"upleft",
225236
"upscaled",

Dockerfile

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@ ENV GAME_GIT_HASH=$GAME_GIT_HASH
1111
ARG GAME_GIT_HASH_SHORT
1212
ENV GAME_GIT_HASH_SHORT=$GAME_GIT_HASH_SHORT
1313

14+
ARG PROMETHEUS_MULTIPROC_DIR="/tmp/prometheus_multiproc"
15+
1416
# Labels as per:
1517
# https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys
1618
MAINTAINER Max Planck Institute for Security and Privacy
1719
LABEL org.opencontainers.image.authors="Max Planck Institute for Security and Privacy"
18-
LABEL org.opencontainers.image.version="2.0.4"
20+
# NOTE Also change the version in config.py
21+
LABEL org.opencontainers.image.version="2.1.0"
1922
LABEL org.opencontainers.image.licenses="AGPL-3.0-only"
2023
LABEL org.opencontainers.image.description="Ready to deploy Docker container to use ReverSim for research. ReverSim is an open-source environment for the browser, originally developed at the Max Planck Institute for Security and Privacy (MPI-SP) to study human aspects in hardware reverse engineering."
2124
LABEL org.opencontainers.image.source="https://github.com/emsec/ReverSim"
@@ -52,14 +55,17 @@ WORKDIR /usr/var/reversim-instance
5255
COPY examples/conf conf
5356
COPY instance/conf conf
5457

58+
# Setup for Prometheus multiprocessing
59+
WORKDIR ${PROMETHEUS_MULTIPROC_DIR}
60+
ENV PROMETHEUS_MULTIPROC_DIR=${PROMETHEUS_MULTIPROC_DIR}
61+
5562
# Create empty statistics folders
5663
WORKDIR /usr/var/reversim-instance/statistics/LogFiles
5764
WORKDIR /usr/var/reversim-instance/statistics/canvasPics
5865
WORKDIR /usr/src/hregame
5966

60-
# Specify mount points for the statistics folder, game config, levels, researchInfo & disclaimer
67+
# Specify mount points for the statistics folder, levels, researchInfo & disclaimer
6168
VOLUME /usr/var/reversim-instance/statistics
62-
VOLUME /usr/var/reversim-instance/conf
6369
VOLUME /var/log/uwsgi
6470

6571
# Exposes the port that uWSGI is listening to (as configured in hre_game.ini)

app/__init__.py

Whitespace-only changes.

app/config.py

Lines changed: 50 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,32 @@
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

2121
PSEUDONYM_LENGTH = 32
2222
LEVEL_ENCODING = 'UTF-8' # was Windows-1252
2323
TIME_DRIFT_THRESHOLD = 200 # ms
2424
STALE_LOGFILE_TIME = 48 * 60 * 60 # close logfiles after 48h
2525
MAX_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()
2937
REVERSIM_STATIC_URL = "/assets"
3038

39+
DEFAULT_FOOTER = {
40+
"researchInfo": REVERSIM_STATIC_URL + "/researchInfo/researchInfo.html"
41+
}
42+
3143
class 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

256276
def 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
331356
REMAP_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

338363
LEVEL_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

app/model/Level.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,8 @@ def getLogType(self) -> str:
108108

109109

110110
def getName(self) -> str:
111-
"""Name of the level with no file extension.
112-
111+
"""The uniform name of the level with no file extension.
112+
113113
Replace the folder separators if you want to use it inside a folder path.
114114
"""
115115
return Level.uniformName(self.fileName)
@@ -189,6 +189,7 @@ def getBasePath(type: str) -> str:
189189
else:
190190
return safe_join(gameConfig.getAssetPath(), LEVEL_BASE_FOLDER)
191191

192+
192193
KEY_CAMOUFLAGE = 'camouflage'
193194
KEY_COVERT = 'covert'
194195

0 commit comments

Comments
 (0)