diff --git a/cooperative_cuisine/__init__.py b/cooperative_cuisine/__init__.py
index a0577c01a74d76250c0152a73dcd51e1ab5093a9..4ec76825b50da6103fa25529f88754827d16013a 100644
--- a/cooperative_cuisine/__init__.py
+++ b/cooperative_cuisine/__init__.py
@@ -323,7 +323,7 @@ player_config:
 effect_manager:  # fire effect
   ...
 
-extra_setup_functions:  # scores, recording, msgs, etc.
+hook_callbacks:  # scores, recording, msgs, etc.
   ...
 ```
 
diff --git a/cooperative_cuisine/configs/environment_config.yaml b/cooperative_cuisine/configs/environment_config.yaml
index 69fefccee00483d1049d929548385a5fd6f17e89..e63620d0843c21a7f77e0dbf92538a5acc0c1779 100644
--- a/cooperative_cuisine/configs/environment_config.yaml
+++ b/cooperative_cuisine/configs/environment_config.yaml
@@ -98,114 +98,96 @@ effect_manager:
       fire_burns_ingredients_and_meals: true
 
 
-extra_setup_functions:
+hook_callbacks:
   # # ---------------  Scoring  ---------------
   orders:
-    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-    kwargs:
-      hooks: [ completed_order ]
-      callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
-      callback_class_kwargs:
-        static_score: 20
-        score_on_specific_kwarg: meal_name
-        score_map:
-          Burger: 15
-          OnionSoup: 10
-          Salad: 5
-          TomatoSoup: 10
+    hooks: [ completed_order ]
+    callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
+    callback_class_kwargs:
+      static_score: 20
+      score_on_specific_kwarg: meal_name
+      score_map:
+        Burger: 15
+        OnionSoup: 10
+        Salad: 5
+        TomatoSoup: 10
   not_ordered_meals:
-    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-    kwargs:
-      hooks: [ serve_not_ordered_meal ]
-      callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
-      callback_class_kwargs:
-        static_score: 2
+    hooks: [ serve_not_ordered_meal ]
+    callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
+    callback_class_kwargs:
+      static_score: 2
   trashcan_usages:
-    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-    kwargs:
-      hooks: [ trashcan_usage ]
-      callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
-      callback_class_kwargs:
-        static_score: -5
+    hooks: [ trashcan_usage ]
+    callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
+    callback_class_kwargs:
+      static_score: -5
   expired_orders:
-    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-    kwargs:
-      hooks: [ order_expired ]
-      callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
-      callback_class_kwargs:
-        static_score: -10
-  # # --------------- Recording ---------------
+    hooks: [ order_expired ]
+    callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
+    callback_class_kwargs:
+      static_score: -10
+    # --------------- Recording ---------------
   #  json_states:
-  #    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-  #    kwargs:
-  #      hooks: [ json_state ]
-  #      callback_class: !!python/name:cooperative_cuisine.recording.FileRecorder ''
-  #      callback_class_kwargs:
-  #        log_path: USER_LOG_DIR/ENV_NAME/json_states.jsonl
+  #    hooks: [ json_state ]
+  #    callback_class: !!python/name:cooperative_cuisine.recording.FileRecorder ''
+  #    callback_class_kwargs:
+  #      record_path: USER_LOG_DIR/ENV_NAME/json_states.jsonl
   actions:
-    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-    kwargs:
-      hooks: [ pre_perform_action ]
-      callback_class: !!python/name:cooperative_cuisine.recording.FileRecorder ''
-      callback_class_kwargs:
-        log_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl
+    hooks: [ pre_perform_action ]
+    callback_class: !!python/name:cooperative_cuisine.recording.FileRecorder ''
+    callback_class_kwargs:
+      record_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl
   random_env_events:
-    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-    kwargs:
-      hooks: [ order_duration_sample, plate_out_of_kitchen_time ]
-      callback_class: !!python/name:cooperative_cuisine.recording.FileRecorder ''
-      callback_class_kwargs:
-        log_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl
-        add_hook_ref: true
+    hooks: [ order_duration_sample, plate_out_of_kitchen_time ]
+    callback_class: !!python/name:cooperative_cuisine.recording.FileRecorder ''
+    callback_class_kwargs:
+      record_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl
+      add_hook_ref: true
   env_configs:
-    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-    kwargs:
-      hooks: [ env_initialized, item_info_config ]
-      callback_class: !!python/name:cooperative_cuisine.recording.FileRecorder ''
-      callback_class_kwargs:
-        log_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl
-        add_hook_ref: true
+    hooks: [ env_initialized, item_info_config ]
+    callback_class: !!python/name:cooperative_cuisine.recording.FileRecorder ''
+    callback_class_kwargs:
+      record_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl
+      add_hook_ref: true
 
   # Game event recording
   game_events:
-    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-    kwargs:
-      hooks:
-        - post_counter_pick_up
-        - post_counter_drop_off
-        - post_dispenser_pick_up
-        - cutting_board_100
-        - player_start_interaction
-        - player_end_interact
-        - post_serving
-        - no_serving
-        - dirty_plate_arrives
-        - trashcan_usage
-        - plate_cleaned
-        - added_plate_to_sink
-        - drop_on_sink_addon
-        - pick_up_from_sink_addon
-        - serve_not_ordered_meal
-        - serve_without_plate
-        - completed_order
-        - new_orders
-        - order_expired
-        - action_on_not_reachable_counter
-        - new_fire
-        - fire_spreading
-        - drop_off_on_cooking_equipment
-        - players_collide
-        - post_plate_dispenser_pick_up
-        - post_plate_dispenser_drop_off
-        - on_item_transition
-        - progress_started
-        - progress_finished
-        - content_ready
+    hooks:
+      - post_counter_pick_up
+      - post_counter_drop_off
+      - post_dispenser_pick_up
+      - cutting_board_100
+      - player_start_interaction
+      - player_end_interact
+      - post_serving
+      - no_serving
+      - dirty_plate_arrives
+      - trashcan_usage
+      - plate_cleaned
+      - added_plate_to_sink
+      - drop_on_sink_addon
+      - pick_up_from_sink_addon
+      - serve_not_ordered_meal
+      - serve_without_plate
+      - completed_order
+      - new_orders
+      - order_expired
+      - action_on_not_reachable_counter
+      - new_fire
+      - fire_spreading
+      - drop_off_on_cooking_equipment
+      - players_collide
+      - post_plate_dispenser_pick_up
+      - post_plate_dispenser_drop_off
+      - on_item_transition
+      - progress_started
+      - progress_finished
+      - content_ready
 
-      callback_class: !!python/name:cooperative_cuisine.recording.FileRecorder ''
-      callback_class_kwargs:
-        log_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl
-        add_hook_ref: true
+    callback_class: !!python/name:cooperative_cuisine.recording.FileRecorder ''
+    callback_class_kwargs:
+      record_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl
+      add_hook_ref: true
 
 
 #  info_msg:
diff --git a/cooperative_cuisine/configs/study/level1/level1_config.yaml b/cooperative_cuisine/configs/study/level1/level1_config.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/cooperative_cuisine/configs/study/level2/level2_config.yaml b/cooperative_cuisine/configs/study/level2/level2_config.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/cooperative_cuisine/configs/tutorial_env_config.yaml b/cooperative_cuisine/configs/tutorial_env_config.yaml
index 86a1d0341679a6a64c32bf2ec9f9cc5c30adea15..15a9fc6dde4672cb7b0c40b9a875c8f20195ba50 100644
--- a/cooperative_cuisine/configs/tutorial_env_config.yaml
+++ b/cooperative_cuisine/configs/tutorial_env_config.yaml
@@ -94,4 +94,4 @@ effect_manager:
       fire_burns_ingredients_and_meals: true
 
 
-extra_setup_functions:
\ No newline at end of file
+hook_callbacks:
\ No newline at end of file
diff --git a/cooperative_cuisine/counters.py b/cooperative_cuisine/counters.py
index 01421b2137f238b8413eee3194a227c2c87e8576..cd84be94e982e3c6e49ecefd501dbe075be84237 100644
--- a/cooperative_cuisine/counters.py
+++ b/cooperative_cuisine/counters.py
@@ -684,10 +684,10 @@ class PlateDispenser(Counter):
 
     def progress(self, passed_time: timedelta, now: datetime):
         """Check if plates arrive from outside the kitchen and add a dirty plate accordingly"""
-        if self.next_plate_time < now:
+        if self.next_plate_time <= now:
             idx_delete = []
             for i, times in enumerate(self.out_of_kitchen_timer):
-                if times < now:
+                if times <= now:
                     self.hook(DIRTY_PLATE_ARRIVES, counter=self, times=times, now=now)
                     idx_delete.append(i)
                     log.debug("Add dirty plate")
diff --git a/cooperative_cuisine/effects.py b/cooperative_cuisine/effects.py
index 3688ce649ef10ddbddb1671709a75db41f5a1fe6..53989c0916dd9d8f2fe82b44ef2be4f971e1da29 100644
--- a/cooperative_cuisine/effects.py
+++ b/cooperative_cuisine/effects.py
@@ -163,9 +163,9 @@ class FireEffectManager(EffectManager):
                 self.active_effects.append((effect, target))
             # reset new effects
             self.new_effects = []
-        if self.next_finished_timer < now:
+        if self.next_finished_timer <= now:
             for effect, target in self.active_effects:
-                if self.effect_to_timer[effect.uuid] < now:
+                if self.effect_to_timer[effect.uuid] <= now:
                     if isinstance(target, Item):
                         target = find_item_on_counters(target.uuid, self.counters)
                     if target:
diff --git a/cooperative_cuisine/environment.py b/cooperative_cuisine/environment.py
index e898be1862752bfaa80ceb429256134dc710c621..25eff9e6734879d45dfad4ad6359c2b75fc1826f 100644
--- a/cooperative_cuisine/environment.py
+++ b/cooperative_cuisine/environment.py
@@ -51,6 +51,7 @@ from cooperative_cuisine.hooks import (
     ACTION_INTERACT_START,
     ITEM_INFO_CONFIG,
     POST_STEP,
+    hooks_via_callback_class,
 )
 from cooperative_cuisine.items import (
     ItemInfo,
@@ -90,8 +91,8 @@ class EnvironmentConfig(TypedDict):
     """Configuration about the player characters."""
     layout_chars: dict[str, str]
     """Definition of which characters in the layout file correspond to which kitchen counter."""
-    extra_setup_functions: dict[str, dict]
-    """Configuration of extra setup functions, for example hook behavior."""
+    hook_callbacks: dict[str, dict]
+    """Configuration of callbacks via HookCallbackClass."""
     effect_manager: dict
     """Config of different effects in the environment, which control for example fire behavior."""
 
@@ -184,7 +185,7 @@ class Environment:
                 "player_config"
             ].view_range
 
-        self.extra_setup_functions()
+        self.hook_callbacks()
 
         self.layout_config: str = layout_config
         """The layout config for the environment"""
@@ -570,16 +571,14 @@ class Environment:
         """
         self.hook.register_callback(hook_ref, callback)
 
-    def extra_setup_functions(self):
+    def hook_callbacks(self):
         """Executes extra setup functions specified in the environment configuration."""
-        if self.environment_config["extra_setup_functions"]:
-            for function_name, function_def in self.environment_config[
-                "extra_setup_functions"
+        if self.environment_config["hook_callbacks"]:
+            for callback_name, setup_kwargs in self.environment_config[
+                "hook_callbacks"
             ].items():
-                log.info(f"Setup function {function_name}")
-                function_def["func"](
-                    name=function_name, env=self, **function_def["kwargs"]
-                )
+                log.info(f"Setup hook callback {callback_name}")
+                hooks_via_callback_class(name=callback_name, env=self, **setup_kwargs)
 
     def increment_score(self, score: int | float, info: str = ""):
         """Add a value to the current score and log it."""
diff --git a/cooperative_cuisine/hooks.py b/cooperative_cuisine/hooks.py
index 8c9cb70bc2a0c2dddfe22ecd096994bb170bd1f6..f16b10afe108fd2572ba801a01d0dca679882e21 100644
--- a/cooperative_cuisine/hooks.py
+++ b/cooperative_cuisine/hooks.py
@@ -2,7 +2,7 @@
 You can add callbacks at specific points in the environment.
 This "hook" mechanism is defined here.
 
-You can add hooks via the `environment_config` under `extra_setup_functions` and the here defined
+You can add hooks via the `environment_config` under `hook_callbacks` and the here defined
 `hooks_via_callback_class` function.
 
 Each hook get different kwargs. But `env` with the environment object and `hook_ref` with the name of the hook are
@@ -229,7 +229,7 @@ def hooks_via_callback_class(
     callback_class: Type[HookCallbackClass],
     callback_class_kwargs: dict[str, Any],
 ):
-    """Function to reference in the `environment_config.yml` to add functionality via hooks and a configured callback class.
+    """Setup hook callback class.
 
     Args:
         name: A string representing the name of the callback class instance.
diff --git a/cooperative_cuisine/info_msg.py b/cooperative_cuisine/info_msg.py
index 3d6d95257a0e138282e32b8666a72627f12cb974..d8077ed06d116579c0265644e30a29dd1a2fdf5d 100644
--- a/cooperative_cuisine/info_msg.py
+++ b/cooperative_cuisine/info_msg.py
@@ -1,22 +1,18 @@
 """Based on hooks, text-based info msgs can be displayed.
 
  ```yaml
- extra_setup_functions:
+ hook_callbacks:
    info_msg:
-    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-    kwargs:
-      hooks: [ cutting_board_100 ]
-      callback_class: !!python/name:cooperative_cuisine.info_msg.InfoMsgManager ''
-      callback_class_kwargs:
-        msg: Glückwunsch du hast was geschnitten!
-  fire_msg:
-    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-    kwargs:
-      hooks: [ new_fire ]
-      callback_class: !!python/name:cooperative_cuisine.info_msg.InfoMsgManager ''
-      callback_class_kwargs:
-        msg: Feuer, Feuer, Feuer
-        level: Warning
+    hooks: [ cutting_board_100 ]
+    callback_class: !!python/name:cooperative_cuisine.info_msg.InfoMsgManager ''
+    callback_class_kwargs:
+      msg: Glückwunsch du hast was geschnitten!
+   fire_msg:
+     hooks: [ new_fire ]
+     callback_class: !!python/name:cooperative_cuisine.info_msg.InfoMsgManager ''
+     callback_class_kwargs:
+       msg: Feuer, Feuer, Feuer
+       level: Warning
  ```
 
  """
@@ -95,7 +91,7 @@ class InfoMsgManager(HookCallbackClass):
         for player_id, msgs in env.info_msgs_per_player.items():
             delete_msgs = []
             for idx, msg in enumerate(msgs):
-                if msg["end_time"] < env.env_time:
+                if msg["end_time"] <= env.env_time:
                     delete_msgs.append(idx)
             for idx in reversed(delete_msgs):
                 msgs.pop(idx)
diff --git a/cooperative_cuisine/pygame_2d_vis/drawing.py b/cooperative_cuisine/pygame_2d_vis/drawing.py
index f9a994625856c4ee3a2bee92e208c0df7b6a6d3c..0c67eaf1549e8e45510832cc5c9296591883715c 100644
--- a/cooperative_cuisine/pygame_2d_vis/drawing.py
+++ b/cooperative_cuisine/pygame_2d_vis/drawing.py
@@ -2,6 +2,7 @@ import argparse
 import colorsys
 import json
 import os
+import sys
 from datetime import datetime, timedelta
 from pathlib import Path
 
@@ -1002,7 +1003,21 @@ def generate_recipe_images(config: dict, folder_path: str | Path):
         pygame.image.save(screen, f"{folder_path}/{graph_dict['meal']}.png")
 
 
-if __name__ == "__main__":
+def main(args):
+    """
+
+    Runs the Cooperative Cuisine Image Generation process.
+
+    This method takes command line arguments to specify the state file, visualization configuration file, and output file for the generated image. It then reads the visualization configuration
+    * file and state file, and calls the 'save_screenshot' and 'generate_recipe_images' methods to generate the image.
+
+    Args:
+        -s, --state: A command line argument of type `argparse.FileType("r", encoding="UTF-8")`. Specifies the state file to use for image generation. If not provided, the default value is 'ROOT_DIR / "pygame_2d_vis" / "sample_state.json"'.
+
+        -v, --visualization_config: A command line argument of type `argparse.FileType("r", encoding="UTF-8")`. Specifies the visualization configuration file to use for image generation. If not provided, the default value is 'ROOT_DIR / "pygame_2d_vis" / "visualization.yaml"'.
+
+        -o, --output_file: A command line argument of type `str`. Specifies the output file path for the generated image. If not provided, the default value is 'ROOT_DIR / "generated" / "screenshot.jpg"'.
+    """
     parser = argparse.ArgumentParser(
         prog="Cooperative Cuisine Image Generation",
         description="Generate images for a state in json.",
@@ -1024,12 +1039,16 @@ if __name__ == "__main__":
         "-o",
         "--output_file",
         type=str,
-        default=ROOT_DIR / "pygame_2d_vis" / "generated" / "screenshot.jpg",
+        default=ROOT_DIR / "generated" / "screenshot.jpg",
     )
-    args = parser.parse_args()
+    args = parser.parse_args(args)
     with open(args.visualization_config, "r") as f:
         viz_config = yaml.safe_load(f)
     with open(args.state, "r") as f:
         state = json.load(f)
     save_screenshot(state, viz_config, args.output_file)
     generate_recipe_images(viz_config, args.output_file.parent)
+
+
+if __name__ == "__main__":
+    main(sys.argv[1:])
diff --git a/cooperative_cuisine/pygame_2d_vis/video_replay.py b/cooperative_cuisine/pygame_2d_vis/video_replay.py
index acb08cbaefafc6ad20e855f28eb6f7d274318956..46521e12524e1c0c2b2074cc5b1f68486d354364 100644
--- a/cooperative_cuisine/pygame_2d_vis/video_replay.py
+++ b/cooperative_cuisine/pygame_2d_vis/video_replay.py
@@ -72,14 +72,14 @@ def simulate(
 
     You can record the relevant files via hooks in the environment_config:
     ```yaml
-    extra_setup_functions
+    hook_callbacks
       env_configs:
         func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
         kwargs:
           hooks: [ env_initialized, item_info_config ]
           callback_class: !!python/name:cooperative_cuisine.recording.FileRecorder ''
           callback_class_kwargs:
-            log_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl
+            record_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl
             add_hook_ref: true
       actions:
         func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
@@ -87,7 +87,7 @@ def simulate(
           hooks: [ pre_perform_action ]
           callback_class: !!python/name:cooperative_cuisine.recording.FileRecorder ''
           callback_class_kwargs:
-            log_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl
+            record_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl
     ```
 
     You can call simulation function via the command line. For example by replacing the ENVIRONMENT_ID (Linux system) or the complete path:
@@ -219,7 +219,7 @@ def from_json_states(
        hooks: [ json_state ]
        callback_class: !!python/name:cooperative_cuisine.recording.FileRecorder ''
        callback_class_kwargs:
-         log_path: USER_LOG_DIR/ENV_NAME/json_states.jsonl
+         record_path: USER_LOG_DIR/ENV_NAME/json_states.jsonl
     ```
 
     You can call this function via the command line:
diff --git a/cooperative_cuisine/recording.py b/cooperative_cuisine/recording.py
index b124a7f2a6ec5a149b5960b34dd46a38e355c907..a71cc0b3654b67f238798726e19c756a2f1f8111 100644
--- a/cooperative_cuisine/recording.py
+++ b/cooperative_cuisine/recording.py
@@ -7,42 +7,35 @@ https://pypi.org/project/platformdirs/) -> `user_log_dir`.
 - `ENV_NAME` to the name of the environment.
 
 ```yaml
-extra_setup_functions:
+hook_callbacks:
   json_states:
-    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-    kwargs:
-      hooks: [ json_state ]
-      callback_class: !!python/name:cooperative_cuisine.recording.FileRecorder ''
-      callback_class_kwargs:
-        log_path: USER_LOG_DIR/ENV_NAME/json_states.jsonl
+    hooks: [ json_state ]
+    callback_class: !!python/name:cooperative_cuisine.recording.FileRecorder ''
+    callback_class_kwargs:
+      record_path: USER_LOG_DIR/ENV_NAME/json_states.jsonl
   actions:
-    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-    kwargs:
-      hooks: [ pre_perform_action ]
-      callback_class: !!python/name:cooperative_cuisine.recording.FileRecorder ''
-      callback_class_kwargs:
-        log_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl
+    hooks: [ pre_perform_action ]
+    callback_class: !!python/name:cooperative_cuisine.recording.FileRecorder ''
+    callback_class_kwargs:
+      record_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl
   random_env_events:
-    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-    kwargs:
-      hooks: [ order_duration_sample, plate_out_of_kitchen_time ]
-      callback_class: !!python/name:cooperative_cuisine.recording.FileRecorder ''
-      callback_class_kwargs:
-        log_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl
-        add_hook_ref: true
+    hooks: [ order_duration_sample, plate_out_of_kitchen_time ]
+    callback_class: !!python/name:cooperative_cuisine.recording.FileRecorder ''
+    callback_class_kwargs:
+      record_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl
+      add_hook_ref: true
   env_configs:
-    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-    kwargs:
-      hooks: [ env_initialized, item_info_config ]
-      callback_class: !!python/name:cooperative_cuisine.recording.FileRecorder ''
-      callback_class_kwargs:
-        log_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl
-        add_hook_ref: true
+    hooks: [ env_initialized, item_info_config ]
+    callback_class: !!python/name:cooperative_cuisine.recording.FileRecorder ''
+    callback_class_kwargs:
+      record_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl
+      add_hook_ref: true
 ```
 """
 import json
 import logging
 import os
+import sys
 import traceback
 from pathlib import Path
 from string import Template
@@ -68,7 +61,7 @@ class FileRecorder(HookCallbackClass):
         self,
         name: str,
         env: Environment,
-        log_path: str = "USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl",
+        record_path: str = "USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl",
         add_hook_ref: bool = False,
         **kwargs,
     ):
@@ -77,19 +70,19 @@ class FileRecorder(HookCallbackClass):
         Args:
             name (str): The name of the recorder. This name is used to replace the placeholder "LOG_RECORD_NAME" in the default log file path.
             env (Environment): The environment in which the recorder is being used.
-            log_path (str, optional): The path to the log file. Defaults to "USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl".
+            record_path (str, optional): The path to the log file. Defaults to "USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl".
             add_hook_ref (bool, optional): Indicates whether to add a hook reference to the recorded data. Defaults to False.
             **kwargs: Additional keyword arguments.
         """
         super().__init__(name, env, **kwargs)
         self.add_hook_ref: bool = add_hook_ref
         """Indicates whether to add a hook reference to the recorded data. Default value is False."""
-        log_path = log_path.replace("LOG_RECORD_NAME", name)
-        log_path = Path(expand_path(log_path, env_name=env.env_name))
-        self.log_path: Path = log_path
+        record_path = record_path.replace("LOG_RECORD_NAME", name)
+        record_path = Path(expand_path(record_path, env_name=env.env_name))
+        self.record_path: Path = record_path
         """The path to the log file. Default value is "USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl"."""
-        log.info(f"Recorder record for {name} in file://{log_path}")
-        os.makedirs(log_path.parent, exist_ok=True)
+        log.info(f"Recorder record for {name} in file://{record_path}")
+        os.makedirs(record_path.parent, exist_ok=True)
 
     def __call__(self, hook_ref: str, env: Environment, **kwargs):
         for key, item in kwargs.items():
@@ -134,8 +127,8 @@ class FileRecorder(HookCallbackClass):
                 )
                 + "\n"
             )
-            with open(self.log_path, "a") as log_file:
-                log_file.write(record)
+            with open(self.record_path, "a") as record_file:
+                record_file.write(record)
         except TypeError as e:
             traceback.print_exception(e)
             log.info(
@@ -151,7 +144,7 @@ def print_recorded_events_human_readable(jsonl_path: Path):
 
     """
 
-    def stringify_item(item_):
+    def stringify_item(item_) -> str | None:
         if isinstance(item_, float):
             return str(item_)
         if isinstance(item_, str):
@@ -226,7 +219,5 @@ def print_recorded_events_human_readable(jsonl_path: Path):
 
 
 if __name__ == "__main__":
-    json_lines_path: Path = Path(
-        "/home/fabian/.local/state/cooperative_cuisine/log/fcb095915c454446b9ee2905ff534610/game_events.jsonl"
-    )
+    json_lines_path: Path = Path(sys.argv[0])
     print_recorded_events_human_readable(json_lines_path)
diff --git a/cooperative_cuisine/reinforcement_learning/environment_config_rl.yaml b/cooperative_cuisine/reinforcement_learning/environment_config_rl.yaml
index 2eb50382df3b4a5ddae99e632707cae76bdcf7dc..1167ad76732ba1dcdde9506f73d94ee1b42cccf6 100644
--- a/cooperative_cuisine/reinforcement_learning/environment_config_rl.yaml
+++ b/cooperative_cuisine/reinforcement_learning/environment_config_rl.yaml
@@ -98,86 +98,64 @@ effect_manager: { }
 #      fire_burns_ingredients_and_meals: true
 
 
-extra_setup_functions:
+hook_callbacks:
   # # ---------------  Scoring  ---------------
   orders:
-    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-    kwargs:
-      hooks: [ completed_order ]
-      callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
-      callback_class_kwargs:
-        static_score: 0.95
+    hooks: [ completed_order ]
+    callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
+    callback_class_kwargs:
+      static_score: 0.95
 
   serve_not_ordered_meals:
-    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-    kwargs:
-      hooks: [ serve_not_ordered_meal ]
-      callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
-      callback_class_kwargs:
-        static_score: 0.95
+    hooks: [ serve_not_ordered_meal ]
+    callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
+    callback_class_kwargs:
+      static_score: 0.95
   trashcan_usages:
-    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-    kwargs:
-      hooks: [ trashcan_usage ]
-      callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
-      callback_class_kwargs:
-        static_score: -0.2
+    hooks: [ trashcan_usage ]
+    callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
+    callback_class_kwargs:
+      static_score: -0.2
   item_cut:
-    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-    kwargs:
-      hooks: [ cutting_board_100 ]
-      callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
-      callback_class_kwargs:
-        static_score: 0.1
+    hooks: [ cutting_board_100 ]
+    callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
+    callback_class_kwargs:
+      static_score: 0.1
   stepped:
-    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-    kwargs:
-      hooks: [ post_step ]
-      callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
-      callback_class_kwargs:
-        static_score: -0.01
+    hooks: [ post_step ]
+    callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
+    callback_class_kwargs:
+      static_score: -0.01
   combine:
-    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-    kwargs:
-      hooks: [ drop_off_on_cooking_equipment ]
-      callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
-      callback_class_kwargs:
-        static_score: 0.01
+    hooks: [ drop_off_on_cooking_equipment ]
+    callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
+    callback_class_kwargs:
+      static_score: 0.01
   start_interact:
-    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-    kwargs:
-      hooks: [ player_start_interaction ]
-      callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
-      callback_class_kwargs:
-        static_score: 0.01
-  #  json_states:
-  #    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-  #    kwargs:
-  #      hooks: [ json_state ]
-  #      log_class: !!python/name:cooperative_cuisine.recording.LogRecorder ''
-  #      log_class_kwargs:
-  #        log_path: USER_LOG_DIR/ENV_NAME/json_states.jsonl
+    hooks: [ player_start_interaction ]
+    callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
+    callback_class_kwargs:
+      static_score: 0.01
+#    json_states:
+#      hooks: [ json_state ]
+#      record_class: !!python/name:cooperative_cuisine.recording.LogRecorder ''
+#      record_class_kwargs:
+#        record_path: USER_LOG_DIR/ENV_NAME/json_states.jsonl
 #  actions:
-#    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-#    kwargs:
-#      hooks: [ pre_perform_action ]
-#      log_class: !!python/name:cooperative_cuisine.recording.LogRecorder ''
-#      log_class_kwargs:
-#        log_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl
+#    hooks: [ pre_perform_action ]
+#    record_class: !!python/name:cooperative_cuisine.recording.LogRecorder ''
+#    record_class_kwargs:
+#      record_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl
 #  random_env_events:
-#    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-#    kwargs:
-#      hooks: [ order_duration_sample, plate_out_of_kitchen_time ]
-#      log_class: !!python/name:cooperative_cuisine.recording.LogRecorder ''
-#      log_class_kwargs:
-#        log_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl
-#        add_hook_ref: true
+#    hooks: [ order_duration_sample, plate_out_of_kitchen_time ]
+#    record_class: !!python/name:cooperative_cuisine.recording.LogRecorder ''
+#    record_class_kwargs:
+#      record_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl
+#      add_hook_ref: true
 #  env_configs:
-#    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-#    kwargs:
-#      hooks: [ env_initialized, item_info_config ]
-#      log_class: !!python/name:cooperative_cuisine.recording.LogRecorder ''
-#      log_class_kwargs:
-#        log_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl
-#        add_hook_ref: true
+#    hooks: [ env_initialized, item_info_config ]
+#    record_class: !!python/name:cooperative_cuisine.recording.LogRecorder ''
+#    record_class_kwargs:
+#      record_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl
+#      add_hook_ref: true
 
diff --git a/cooperative_cuisine/scores.py b/cooperative_cuisine/scores.py
index 5ae2c6ce83146b3d190f67e3e8236e3637a1b244..3bab87e92f29caec0903c3865c747757f3b0c188 100644
--- a/cooperative_cuisine/scores.py
+++ b/cooperative_cuisine/scores.py
@@ -1,5 +1,5 @@
 """
-Scores are managed via hooks. You can add them in the `environment_config` under `extra_setup_functions`.
+Scores are managed via hooks. You can add them in the `environment_config` under `hook_callbacks`.
 
 The here defined `ScoreViaHooks` is a `HookCallbackClass`. It allows you to define how the score is effected by
 specific hook events.
@@ -12,41 +12,33 @@ You can:
 You can filter the events via `kwarg_filter`.
 
 ```yaml
-extra_setup_functions:
+hook_callbacks:
   orders:
-    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-    kwargs:
-      hooks: [ completed_order ]
-      callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
-      callback_class_kwargs:
-        static_score: 20
-        score_on_specific_kwarg: meal_name
-        score_map:
-          Burger: 15
-          OnionSoup: 10
-          Salad: 5
-          TomatoSoup: 10
+    hooks: [ completed_order ]
+    callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
+    callback_class_kwargs:
+      static_score: 20
+      score_on_specific_kwarg: meal_name
+      score_map:
+        Burger: 15
+        OnionSoup: 10
+        Salad: 5
+        TomatoSoup: 10
   not_ordered_meals:
-    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-    kwargs:
-      hooks: [ serve_not_ordered_meal ]
-      callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
-      callback_class_kwargs:
-        static_score: 2
+    hooks: [ serve_not_ordered_meal ]
+    callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
+    callback_class_kwargs:
+      static_score: 2
   trashcan_usages:
-    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-    kwargs:
-      hooks: [ trashcan_usage ]
-      callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
-      callback_class_kwargs:
-        static_score: -5
+    hooks: [ trashcan_usage ]
+    callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
+    callback_class_kwargs:
+      static_score: -5
   expired_orders:
-    func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class ''
-    kwargs:
-      hooks: [ order_expired ]
-      callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
-      callback_class_kwargs:
-        static_score: -10
+    hooks: [ order_expired ]
+    callback_class: !!python/name:cooperative_cuisine.scores.ScoreViaHooks ''
+    callback_class_kwargs:
+      static_score: -10
 ```
 
 
@@ -94,13 +86,13 @@ class ScoreViaHooks(HookCallbackClass):
             **kwargs: Additional keyword arguments to be passed to the parent class.
         """
         super().__init__(name, env, **kwargs)
-        self.score_map = score_map
+        self.score_map: dict[str, float] = score_map
         """Mapping of hook references to scores."""
-        self.static_score = static_score
+        self.static_score: float = static_score
         """The static score to be added if no other conditions are met."""
-        self.kwarg_filter = kwarg_filter
+        self.kwarg_filter: dict[str, Any] = kwarg_filter
         """Filtering condition for keyword arguments."""
-        self.score_on_specific_kwarg = score_on_specific_kwarg
+        self.score_on_specific_kwarg: str = score_on_specific_kwarg
         """The specific keyword argument to score on."""
 
     def __call__(self, hook_ref: str, env: Environment, **kwargs):
@@ -114,7 +106,7 @@ class ScoreViaHooks(HookCallbackClass):
                 self.env.increment_score(self.static_score, info=hook_ref)
         elif self.score_map and hook_ref in self.score_map:
             if self.kwarg_filter:
-                if kwargs.items() <= self.kwarg_filter.items():
+                if self.kwarg_filter.items() <= kwargs.items():
                     self.env.increment_score(
                         self.score_map[hook_ref],
                         info=f"{hook_ref} - {self.kwarg_filter}",
diff --git a/tests/test_game_server.py b/tests/test_game_server.py
index f072fed2dd5413ad016e64406146262c93723e53..84271ad77196e85aef512ce9994e0d8926130a0e 100644
--- a/tests/test_game_server.py
+++ b/tests/test_game_server.py
@@ -282,3 +282,10 @@ def test_websocket_wrong_inputs(create_env_config):
         finally:
             task.cancel()
             loop.close()
+
+
+def test_root():
+    with TestClient(app) as client:
+        res = client.get("/")
+        assert res.status_code == status.HTTP_200_OK
+        assert res.json() == {"Cooperative": "Cuisine"}
diff --git a/tests/test_pygame.py b/tests/test_pygame.py
new file mode 100644
index 0000000000000000000000000000000000000000..9df43e0813184f20a159001c91684f149bc17fbb
--- /dev/null
+++ b/tests/test_pygame.py
@@ -0,0 +1,40 @@
+import json
+import os
+
+import yaml
+
+from cooperative_cuisine import ROOT_DIR
+from cooperative_cuisine.pygame_2d_vis.drawing import calc_angle, Visualizer, main
+from cooperative_cuisine.pygame_2d_vis.game_colors import RGB, WHITE, colors
+
+
+def test_colors():
+    assert RGB(red=255, green=255, blue=255).hex_format() == "#FFFFFF"
+    assert WHITE.hex_format() == "#FFFFFF"
+    assert len(colors) >= 552
+
+
+def test_calc_angle():
+    assert calc_angle([0.0, 1.0], [1.0, 0.0]) == -90.0
+    assert calc_angle([1.0, 1.0], [1.0, -1.0]) == -90.0
+
+
+def test_drawing():
+    main([])
+    assert os.path.exists(os.path.join(ROOT_DIR, "generated", "screenshot.jpg"))
+
+
+def test_visualizer():
+    with open(ROOT_DIR / "pygame_2d_vis" / "visualization.yaml", "r") as file:
+        visualization_config = yaml.safe_load(file)
+
+    vis = Visualizer(visualization_config)
+
+    vis.create_player_colors(10)
+    assert len(vis.player_colors) >= 10
+    assert len(set(vis.player_colors)) >= 10
+
+    with open(ROOT_DIR / "pygame_2d_vis" / "sample_state.json", "r") as file:
+        state = json.load(file)
+    image = vis.get_state_image(40, state)
+    assert image.shape == (480, 360, 3)
diff --git a/tests/test_start.py b/tests/test_start.py
index fec31349ac50c39f3d02019c66f1a7f802389a33..784709530f86f1f44c89d9dfd83c4836265b66dd 100644
--- a/tests/test_start.py
+++ b/tests/test_start.py
@@ -1,17 +1,35 @@
+from collections import deque
 from datetime import timedelta
 
 import numpy as np
 import pytest
+import yaml
 
 from cooperative_cuisine import ROOT_DIR
 from cooperative_cuisine.action import ActionType, InterActionData, Action
-from cooperative_cuisine.counters import Counter, CuttingBoard
+from cooperative_cuisine.counters import (
+    Counter,
+    CuttingBoard,
+    CookingCounter,
+    ServingWindow,
+    Trashcan,
+    PlateDispenser,
+    Sink,
+)
+from cooperative_cuisine.effects import FireEffectManager
 from cooperative_cuisine.environment import (
     Environment,
 )
 from cooperative_cuisine.game_server import PlayerRequestType
-from cooperative_cuisine.hooks import Hooks
-from cooperative_cuisine.items import Item, ItemInfo, ItemType
+from cooperative_cuisine.hooks import (
+    Hooks,
+    SERVE_NOT_ORDERED_MEAL,
+    PLAYER_ADDED,
+    POST_STEP,
+)
+from cooperative_cuisine.info_msg import InfoMsgManager
+from cooperative_cuisine.items import Item, ItemInfo, ItemType, Plate, CookingEquipment
+from cooperative_cuisine.scores import ScoreViaHooks
 from cooperative_cuisine.server_results import (
     PlayerInfo,
     CreateEnvResult,
@@ -21,7 +39,7 @@ from cooperative_cuisine.state_representation import (
     StateRepresentation,
     create_json_schema,
 )
-from cooperative_cuisine.utils import create_init_env_time
+from cooperative_cuisine.utils import create_init_env_time, get_touching_counters
 
 layouts_folder = ROOT_DIR / "configs" / "layouts"
 environment_config_path = ROOT_DIR / "configs" / "environment_config.yaml"
@@ -54,7 +72,6 @@ def layout_config():
     with open(layout_path, "r") as file:
         layout = file.read()
     return layout
-    env.add_player("0")
 
 
 @pytest.fixture
@@ -218,6 +235,7 @@ def test_processing(env_config, layout_config, item_info):
         },
     )
     env.counters.append(counter)
+    env.overwrite_counters(env.counters)
 
     tomato = Item(name="Tomato", item_info=None)
     env.add_player("1", np.array([2, 3]))
@@ -308,3 +326,354 @@ def test_server_result_definition():
         msg="123",
         player_hash="1234324",
     )
+
+
+def test_fire(env_config, layout_config, item_info):
+    env = Environment(env_config, layout_config, item_info, as_files=False)
+    env.add_player("0")
+    oven = None
+    for c in env.counters:
+        if (
+            isinstance(c, CookingCounter)
+            and c.name == "Stove"
+            and c.occupied_by.name == "Pan"
+        ):
+            oven = c
+            break
+    assert oven is not None
+
+    raw_patty = Item(name="RawPatty", item_info=env.item_info["RawPatty"])
+
+    assert oven.can_drop_off(raw_patty)
+
+    oven.drop_off(raw_patty, "0")
+    assert isinstance(oven.occupied_by, CookingEquipment)
+    assert oven.occupied_by.content_list == [raw_patty]
+
+    env.step(timedelta(seconds=env.item_info["CookedPatty"].seconds))
+    assert oven.occupied_by.content_list[0].name == "CookedPatty"
+    env.step(timedelta(seconds=env.item_info["BurntCookedPatty"].seconds))
+    assert oven.occupied_by.content_list[0].name == "BurntCookedPatty"
+    env.step(timedelta(seconds=env.item_info["Fire"].seconds))
+    assert len(oven.occupied_by.active_effects) != 0
+    assert oven.occupied_by.active_effects[0].name == "Fire"
+
+    fire_manager = env.effect_manager["FireManager"]
+    assert isinstance(fire_manager, FireEffectManager)
+    env.step(fire_manager.next_finished_timer - env.env_time)
+
+    touching_counters = get_touching_counters(oven, env.counters)
+    next_empty = None
+    connect_counter = None
+    for c in touching_counters:
+        if c.occupied_by:
+            assert len(c.occupied_by.active_effects) == 1
+        else:
+            assert len(c.active_effects) == 1
+            next_touching = get_touching_counters(c, env.counters)
+            for a in next_touching:
+                if a not in touching_counters and a.__class__.__name__ == "Counter":
+                    a.occupied_by = None
+                    next_empty = a
+                    connect_counter = c
+    env.step(timedelta(seconds=0.01))
+    assert next_empty is not None
+    next_empty.occupied_by = Item(name="Tomato", item_info=env.item_info["Tomato"])
+    env.step(
+        fire_manager.effect_to_timer[connect_counter.active_effects[0].uuid]
+        - env.env_time
+    )
+    assert len(next_empty.occupied_by.active_effects) == 1
+
+    fire_extinguisher = None
+    for c in env.counters:
+        if c.occupied_by and c.occupied_by.name == "Extinguisher":
+            fire_extinguisher = c.occupied_by
+            c.occupied_by = None
+            break
+
+    assert fire_extinguisher is not None
+    env.players["0"].holding = fire_extinguisher
+    env.players["0"].pos = oven.pos
+    env.players["0"].pos[1] += 1.0
+    env.perform_action(
+        Action(
+            player="0",
+            action_type=ActionType.MOVEMENT,
+            action_data=np.array([0.0, -1.0]),
+            duration=0.1,
+        )
+    )
+    env.step(timedelta(seconds=0.1))
+
+    env.perform_action(
+        Action(
+            player="0",
+            action_type=ActionType.INTERACT,
+            action_data=InterActionData.START,
+        )
+    )
+    env.step(timedelta(seconds=env.item_info["Extinguisher"].seconds))
+    env.step(timedelta(seconds=env.item_info["Extinguisher"].seconds))
+
+    assert len(oven.occupied_by.active_effects) == 0
+
+
+def test_score(env_config, layout_config, item_info):
+    def incr_score_callback(hook_ref, env: Environment, meal, meal_name, **kwargs):
+        assert isinstance(meal, Item)
+        assert isinstance(meal_name, str)
+        assert meal_name == "TomatoSoup"
+        env.increment_score(1_000, "Soup Soup")
+
+    env = Environment(env_config, layout_config, item_info, as_files=False)
+    assert env.score == 0.0
+    env.add_player("0")
+    env.register_callback_for_hook(SERVE_NOT_ORDERED_MEAL, incr_score_callback)
+    env.register_callback_for_hook(
+        SERVE_NOT_ORDERED_MEAL,
+        ScoreViaHooks(
+            name="123",
+            env=env,
+            score_on_specific_kwarg="meal_name",
+            score_map={"TomatoSoup": 2_000},
+        ),
+    )
+    env.register_callback_for_hook(
+        SERVE_NOT_ORDERED_MEAL,
+        ScoreViaHooks(name="124", env=env, score_map={SERVE_NOT_ORDERED_MEAL: 4_000}),
+    )
+    env.register_callback_for_hook(
+        SERVE_NOT_ORDERED_MEAL,
+        ScoreViaHooks(
+            name="124",
+            env=env,
+            score_map={SERVE_NOT_ORDERED_MEAL: 8_000},
+            kwarg_filter={"meal_name": "TomatoSoup"},
+        ),
+    )
+    env.register_callback_for_hook(
+        SERVE_NOT_ORDERED_MEAL,
+        ScoreViaHooks(
+            name="123",
+            env=env,
+            score_on_specific_kwarg="meal_name",
+            static_score=16_000,
+            score_map={},
+        ),
+    )
+    serving_window = None
+    for c in env.counters:
+        if isinstance(c, ServingWindow):
+            serving_window = c
+            break
+    assert serving_window is not None
+    env.order_manager.serving_not_ordered_meals = True
+    env.order_manager.open_orders = []
+    plate = Plate(
+        transitions=env.counter_factory.filter_item_info(
+            by_item_type=ItemType.Meal, add_effects=True
+        ),
+        clean=True,
+        item_info=env.item_info["Plate"],
+        hook=env.hook,
+    )
+    plate.content_list = [
+        Item(name="TomatoSoup", item_info=env.item_info["TomatoSoup"])
+    ]
+    assert serving_window.can_drop_off(plate)
+
+    returned = serving_window.drop_off(plate, "0")
+    assert returned is None
+
+    plates_prev = len(serving_window.plate_dispenser.occupied_by)
+    assert env.score >= 31_000
+    assert len(serving_window.plate_dispenser.out_of_kitchen_timer) == 1
+    env.step(serving_window.plate_dispenser.out_of_kitchen_timer[0] - env.env_time)
+    assert len(serving_window.plate_dispenser.out_of_kitchen_timer) == 0
+    assert len(serving_window.plate_dispenser.occupied_by) == plates_prev + 1
+    assert (
+        serving_window.plate_dispenser.occupied_by[0].clean
+        != serving_window.plate_dispenser.plate_config.return_dirty
+    )
+
+
+def test_info_msgs(env_config, layout_config, item_info):
+    env_config_dict = yaml.load(env_config, Loader=yaml.Loader)
+    # TODO change after merge with 115
+    env_config_dict["hook_callbacks"]["dummy_msg"] = {
+        "hooks": [PLAYER_ADDED],
+        "callback_class": InfoMsgManager,
+        "callback_class_kwargs": {"msg": "hello there"},
+    }
+
+    env_config_dict["hook_callbacks"]["dummy_msg_2"] = {
+        "hooks": [POST_STEP],
+        "callback_class": InfoMsgManager,
+        "callback_class_kwargs": {"msg": "step step"},
+    }
+
+    env_config = yaml.dump(env_config_dict)
+
+    env = Environment(env_config, layout_config, item_info, as_files=False)
+    env.add_player("0")
+    assert env.info_msgs_per_player["0"][0]["msg"] == "hello there"
+    env.step(timedelta(seconds=0.1))
+    assert env.info_msgs_per_player["0"][1]["msg"] == "step step"
+    env.step(env.info_msgs_per_player["0"][0]["end_time"] - env.env_time)
+    assert len(env.info_msgs_per_player["0"]) == 2
+    assert env.info_msgs_per_player["0"][0]["msg"] == "step step"
+
+
+def test_trashcan(env_config, layout_config, item_info):
+    env = Environment(env_config, layout_config, item_info, as_files=False)
+    env.add_player("0")
+    trash = None
+    for c in env.counters:
+        if isinstance(c, Trashcan):
+            trash = c
+            break
+    assert trash is not None
+
+    item = Item(name="Tomato", item_info=env.item_info["Tomato"])
+    assert trash.can_drop_off(item)
+    assert trash.drop_off(item, "0") is None
+
+    plate = Plate(
+        transitions=env.counter_factory.filter_item_info(
+            by_item_type=ItemType.Meal, add_effects=True
+        ),
+        clean=True,
+        item_info=env.item_info["Plate"],
+        hook=env.hook,
+    )
+    plate.content_list = [
+        Item(name="TomatoSoup", item_info=env.item_info["TomatoSoup"])
+    ]
+
+    assert trash.can_drop_off(plate)
+    assert trash.drop_off(plate, "0") == plate
+    assert plate.content_list == []
+
+    assert trash.pick_up(True, "0") is None
+
+
+def test_plate_dispenser(env_config, layout_config, item_info):
+    env = Environment(env_config, layout_config, item_info, as_files=False)
+    env.add_player("0")
+    plate_dis = None
+    for c in env.counters:
+        if isinstance(c, PlateDispenser):
+            plate_dis = c
+            break
+
+    assert plate_dis is not None
+    assert (
+        len(plate_dis.occupied_by) > 0
+    )  # Otherwise adapt env_config above before passing to Environment
+    n_plates = len(plate_dis.occupied_by)
+    env = Environment(env_config, layout_config, item_info, as_files=False)
+    env.add_player("0")
+    item = Item(name="ChoppedTomato", item_info=env.item_info["ChoppedTomato"])
+    assert plate_dis.can_drop_off(item)
+    returned = plate_dis.drop_off(item, "0")
+    assert returned is None
+
+    assert len(plate_dis.occupied_by) == n_plates
+
+    first_plate = plate_dis.pick_up(True, "0")
+    assert isinstance(first_plate, Plate)
+    assert (
+        first_plate.content_list and first_plate.content_list[0].name == "ChoppedTomato"
+    )
+
+    assert plate_dis.can_drop_off(first_plate)
+    returned = plate_dis.drop_off(first_plate, "0")
+    assert isinstance(returned, Plate) and len(returned.content_list) == 0
+    assert plate_dis.occupied_by[-1].content_list[0].name == "ChoppedTomato"
+
+    plate_dis.occupied_by = deque()
+    assert plate_dis.can_drop_off(item)
+    returned = plate_dis.drop_off(item, "0")
+    assert returned is None
+    assert plate_dis.occupied_by[0].name == "ChoppedTomato"
+
+
+def test_sink(env_config, layout_config, item_info):
+    env = Environment(env_config, layout_config, item_info, as_files=False)
+    env.add_player("0")
+    sink = None
+    for c in env.counters:
+        if isinstance(c, Sink):
+            sink = c
+            break
+    assert sink is not None
+    env.players["0"].pos = sink.pos
+    env.players["0"].pos[1] -= 1.0
+    env.perform_action(
+        Action(
+            player="0",
+            action_type=ActionType.MOVEMENT,
+            action_data=np.array([0.0, 1.0]),
+            duration=0.1,
+        )
+    )
+    env.step(timedelta(seconds=0.1))
+
+    plate = Plate(
+        transitions=env.counter_factory.filter_item_info(
+            by_item_type=ItemType.Meal, add_effects=True
+        ),
+        clean=False,
+        item_info=env.item_info["Plate"],
+        hook=env.hook,
+    )
+    plate_2 = Plate(
+        transitions=env.counter_factory.filter_item_info(
+            by_item_type=ItemType.Meal, add_effects=True
+        ),
+        clean=False,
+        item_info=env.item_info["Plate"],
+        hook=env.hook,
+    )
+    clean_plate = Plate(
+        transitions=env.counter_factory.filter_item_info(
+            by_item_type=ItemType.Meal, add_effects=True
+        ),
+        clean=True,
+        item_info=env.item_info["Plate"],
+        hook=env.hook,
+    )
+
+    assert sink.can_drop_off(plate)
+    assert sink.drop_off(plate, "0") is None
+    assert sink.can_drop_off(plate_2)
+    assert sink.drop_off(plate_2, "0") is None
+    assert not sink.can_drop_off(clean_plate)
+
+    assert len(sink.occupied_by) == 2
+
+    env.perform_action(
+        Action(
+            player="0",
+            action_type=ActionType.INTERACT,
+            action_data=InterActionData.START,
+        )
+    )
+    env.step(timedelta(seconds=env.item_info["Plate"].seconds))
+    assert len(sink.occupied_by) == 1
+    assert len(sink.sink_addon.occupied_by) == 1
+    assert sink.sink_addon.occupied_by[0].clean
+    assert not sink.occupied_by[0].clean
+    assert sink.pick_up(True, "0") is None
+    env.step(timedelta(seconds=env.item_info["Plate"].seconds))
+    assert len(sink.occupied_by) == 0
+    assert len(sink.sink_addon.occupied_by) == 2
+    assert sink.sink_addon.occupied_by[0].clean
+    assert sink.sink_addon.occupied_by[1].clean
+
+    item = Item(name="ChoppedTomato", item_info=env.item_info["ChoppedTomato"])
+    assert sink.sink_addon.can_drop_off(item)
+    assert sink.sink_addon.drop_off(item, "0") is None
+    assert sink.sink_addon.pick_up(True, "0").content_list[0].name == "ChoppedTomato"
+    assert len(sink.sink_addon.occupied_by) == 1
diff --git a/tests/test_study_server.py b/tests/test_study_server.py
index 0d979633495d970274dad50e005dfa61f4513fcc..20776e60b190ee93d4bceec0c89e38c35deb531e 100644
--- a/tests/test_study_server.py
+++ b/tests/test_study_server.py
@@ -92,4 +92,40 @@ def test_game_server_crashed():
     assert res.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR
 
 
+def test_tutorial():
+    test_response = Response()
+    test_response.status_code = status.HTTP_200_OK
+    test_response.encoding = "utf8"
+    test_response._content = json.dumps(
+        {
+            "player_info": {
+                "0": {
+                    "player_id": "0",
+                    "client_id": "ksjdhfkjsdfn",
+                    "player_hash": "shdfbmsndfb",
+                }
+            },
+            "env_id": "123456789",
+            "recipe_graphs": [],
+        }
+    ).encode()
+    with mock.patch.object(
+        study_server_module, "request_game_server", return_value=test_response
+    ) as mock_call:
+        with TestClient(app) as client:
+            res = client.post("/connect_to_tutorial/124")
+
+    assert res.status_code == status.HTTP_200_OK
+
+    mock_call.assert_called_once()
+
+    with mock.patch.object(
+        study_server_module, "request_game_server", return_value=test_response
+    ) as mock_call:
+        with TestClient(app) as client:
+            res = client.post("/disconnect_from_tutorial/124")
+
+    assert res.status_code == status.HTTP_200_OK
+
+
 # TOOD test bots