Skip to content
Snippets Groups Projects
Commit 83f5aacc authored by Annika Österdiekhoff's avatar Annika Österdiekhoff
Browse files

merge main

parents 380c84cc 41b0065a
No related branches found
No related tags found
1 merge request!45Resolve "Controller Support"
Pipeline #46212 passed
Showing
with 819 additions and 81 deletions
##S+###@Q@Q@#
M___________#
T___________|
L___________$
#___________$
#___________P
X___________#
##C#C##Q#Q#B#
; seconds=240
; plates={c:0, d:0}
; dirty_plates=true
; link: https://overcooked.fandom.com/wiki/1-6_(Overcooked!)
; raising platforms based on earthquakes
\ No newline at end of file
_______________
__#QQQ#@@@#____
__#_______$____
__B_______$____
__#_______P____
_______________
__M__A____X____
__L_______#____
__T__A____C____
__#|###C###____
_______________
; seconds=240
; plates={c:0, d:0}
; dirty_plates=false
; link: https://overcooked.fandom.com/wiki/2-1_(Overcooked!)
; moving trucks: counters and ground are moving
\ No newline at end of file
#####P$$|#####
#####____#####
##S+#____#S+##
X____________X
#____________#
U___@__A_@___#
#___@____@___#
#___#_A__#___#
U___#____#___#
#___#____#___#
#?N##C##C##NT#
; seconds=240
; plates={c:0, d:0}
; dirty_plates=true
; link: https://overcooked.fandom.com/wiki/2-2_(Overcooked!)
; rats: steal ingredients + meals
\ No newline at end of file
>>>>>>>>>>>>>>>↓
^#_____##@____#↓
^+A____|#@_A__#↓
^S_____Q#C____$↓
^M_____###____$↓
^L_____Q#C____P↓
^B_____###____#↓
^T_____X#@____X↓
^<<<<<<<<<<<<<<<
; seconds=240
; plates={c:0, d:0}
; dirty_plates=true
; link: https://overcooked.fandom.com/wiki/2-3_(Overcooked!)
; conveyors
\ No newline at end of file
#@@@##C#C######
#_____________$
#___A_________$
#_____________P
####____###___#
X<<<<<<X>>>>>>X
#___###____####
Q_____________#
#________A____#
Q_____________#
##Q#+S#|##BTLM#
; seconds=240
; plates={c:0, d:0}
; dirty_plates=true
; link: https://overcooked.fandom.com/wiki/2-4_(Overcooked!)
\ No newline at end of file
~~~~~~~~~~~~~~~~
~~~~~~P$$~~~~~~~
~~~---------~~~~
~~~-----------~~
~~--#C#C|##----~
~---S#####IA---~
~---+#####K---~~
~---#FFF##F---~~
~~-A----@@@--~~~
~~-----------~~~
~~~~--------~~~~
~~~~~~~----~~~~~
~~~~~~~~~~~~~~~~
; seconds=240
; plates={c:0, d:0}
; dirty_plates=true
; link: https://overcooked.fandom.com/wiki/3-1_(Overcooked!)
; ice: accelerating
\ No newline at end of file
##$$####$$###
####P##______
______?______
______N______
U_____T_____U
X#####X______
U_____#_____U
______#______
__A___#__A___
@@@C#C#______
; seconds=240
; plates={c:0, d:0}
; dirty_plates=false
; link: https://overcooked.fandom.com/wiki/3-2_(Overcooked!)
; moving counters
\ No newline at end of file
__________""#NIK?TX##
__________""#__A____$
__________""#_______$
__________""#__A____P
__________""_________
C_________""________C
#_________""________#
C_________""________C
#|U#U#U@@####F@F@F|##
; seconds=240
; plates={c:0, d:0}
; dirty_plates=false
; link: https://overcooked.fandom.com/wiki/3-3_(Overcooked!)
; moving trucks: counters and ground are moving
\ No newline at end of file
##F#F#~~~~@@F##
X-----~~~-----#
#-A---~~~-----#
I-----~~~-----$
#-----~~~-----$
K-----~~~-----P
|--A-----------
#+S##~---#C#C##
; seconds=240
; plates={c:0, d:0}
; dirty_plates=true
; link: https://overcooked.fandom.com/wiki/3-4_(Overcooked!)
; ice, moving platforms, water
\ No newline at end of file
"""""#|#O#O#O#X#"""
_____#_________#"""
__#____________#"""
__####____##@@@####
__+_______#_______$
__S___A___#___A___$
__#_______#_______P
__#####C#C#____####
__#____________#"""
____"#_________#"""
"""""###DTE?G###"""
; seconds=240
; plates={c:0, d:0}
; dirty_plates=true
; link: https://overcooked.fandom.com/wiki/4-1_(Overcooked!)
; moving counters
\ No newline at end of file
#########|U#@@@#
##S+##C##______$
#______________$
C______________P
#______________#
#______###X#####
U______#######T#
#__A___________#
#___________A__#
N______________#
#########______?
################
; seconds=240
; plates={c:0, d:0}
; dirty_plates=true
; link: https://overcooked.fandom.com/wiki/4-2_(Overcooked!)
; link: https://www.trueachievements.com/game/Overcooked/walkthrough/6
; dark: only flashlight fov
\ No newline at end of file
###S+#####P$$##X#
#_______|_______#
C_______________Q
#_______#_______#
#_______#_______#
###_######@@@_###
B_______#_______#
T__A____#___A___#
M_______________Q
L_______#_______#
#_______#_______#
#################
; seconds=240
; plates={c:0, d:0}
; dirty_plates=true
; link: https://overcooked.fandom.com/wiki/4-3_(Overcooked!)
; moving counters
\ No newline at end of file
#________↓C#O##O###
+________↓________S
S________↓________+
#________↓________#
#________↓#MBLT#@@#
#|#Q##Q#C↓________p
P________↓________$
$_____A__↓________$
$________↓________#
#@@#G?DE#X_________
; seconds=240
; plates={c:0, d:0}
; dirty_plates=true
; link: https://overcooked.fandom.com/wiki/4-4_(Overcooked!)
; moving counters
\ No newline at end of file
##S+#
S___#
+___S
#___+
#+SP#
\ No newline at end of file
#QU#T###NLB#
#__________M
#____A_____#
W__________#
$__________#
############
C__________#
C_____A____#
......
......@@ -26,7 +26,10 @@ import datetime
import logging
import uuid
from enum import Enum
from typing import Optional, TypedDict
from typing import Optional, TypedDict, TYPE_CHECKING
if TYPE_CHECKING:
from overcooked_simulator.effect_manager import EffectManager
log = logging.getLogger(__name__)
"""The logger for this module."""
......@@ -38,6 +41,10 @@ COOKING_EQUIPMENT_ITEM_CATEGORY = "ItemCookingEquipment"
"""The string for the `category` value in the json state representation for all cooking equipments."""
class EffectType(Enum):
Unusable = "Unusable"
class ItemType(Enum):
Ingredient = "Ingredient"
"""All ingredients and process ingredients."""
......@@ -45,6 +52,12 @@ class ItemType(Enum):
"""All combined ingredients that can be served."""
Equipment = "Equipment"
"""All counters and cooking equipments."""
Waste = "Waste"
"""Burnt ingredients and meals."""
Effect = "Effect"
"""Does not change the item but the object attributes, like adding fire."""
Tool = "Tool"
"""Item that remains in hands in extends the interactive abilities of the player."""
@dataclasses.dataclass
......@@ -87,16 +100,24 @@ class ItemInfo:
"""The name of the item, is set automatically by the "group" name of the item."""
seconds: float = dataclasses.field(compare=False, default=0)
"""If progress is needed this argument defines how long it takes to complete the process in seconds."""
# TODO maybe as a lambda/based on Prefix?
needs: list[str] = dataclasses.field(compare=False, default_factory=list)
"""The ingredients/items which are needed to create the item/start the progress."""
equipment: ItemInfo | None = dataclasses.field(compare=False, default=None)
"""On which the item can be created. `null`, `~` (None) converts to Plate."""
manager: str | None | EffectManager = None
"""The manager for the effect."""
effect_type: None | EffectType = None
"""How does the effect effect interaction, combine actions etc."""
recipe: collections.Counter | None = None
"""Internally set in CookingEquipment"""
def __post_init__(self):
self.type = ItemType(self.type)
if self.effect_type:
self.effect_type = EffectType(self.effect_type)
class ActiveTransitionTypedDict(TypedDict):
......@@ -104,7 +125,7 @@ class ActiveTransitionTypedDict(TypedDict):
seconds: int | float
"""The needed seconds to progress for the transition."""
result: str
result: str | Item | Effect
"""The new name of the item after the transition."""
......@@ -125,8 +146,12 @@ class Item:
"""The equipment with which the item was last progressed."""
self.progress_percentage: float = 0.0
"""The current progress percentage of the item if it is progress-able."""
self.inverse_progress: bool = False
"""Whether the progress will produce waste."""
self.uuid: str = uuid.uuid4().hex if uid is None else uid
"""A unique identifier for the item. Useful for GUIs that handles specific asset instances."""
self.active_effects: list[Effect] = []
"""The effects that affect the item."""
def __repr__(self):
if self.progress_equipment is None:
......@@ -167,6 +192,7 @@ class Item:
"""Reset the progress."""
self.progress_equipment = None
self.progress_percentage = 0.0
self.inverse_progress = False
def to_dict(self) -> dict:
"""For the state representation. Only the relevant attributes are put into the dict."""
......@@ -175,6 +201,8 @@ class Item:
"category": self.item_category,
"type": self.name,
"progress_percentage": self.progress_percentage,
"inverse_progress": self.inverse_progress,
"active_effects": [e.to_dict() for e in self.active_effects],
}
......@@ -206,9 +234,17 @@ class CookingEquipment(Item):
def can_combine(self, other) -> bool:
# already cooking or nothing to combine
if other is None:
if other is None or (
isinstance(other, CookingEquipment) and not other.content_list
):
return False
if any(
e.item_info.effect_type == EffectType.Unusable for e in other.active_effects
) or any(
e.item_info.effect_type == EffectType.Unusable for e in self.active_effects
):
return False
if isinstance(other, CookingEquipment):
other = other.content_list
else:
......@@ -226,39 +262,72 @@ class CookingEquipment(Item):
self.content_list.extend(other.content_list)
return_value = other
other.reset_content()
other.reset()
elif isinstance(other, list):
self.content_list.extend(other)
else:
self.content_list.append(other)
ingredients = collections.Counter(item.name for item in self.content_list)
for result, transition in self.transitions.items():
if ingredients == transition.recipe:
if transition.seconds == 0:
self.content_ready = Item(name=result, item_info=transition)
else:
self.active_transition = {
"seconds": transition.seconds,
"result": Item(name=result, item_info=transition),
}
break
else:
self.content_ready = None
self.check_active_transition()
return return_value
def can_progress(self) -> bool:
"""Check if the cooking equipment can progress items at all."""
return self.active_transition is not None
return self.active_transition is not None and not any(
e.item_info.effect_type == EffectType.Unusable for e in self.active_effects
)
def progress(self, passed_time: datetime.timedelta, now: datetime.datetime):
percent = passed_time.total_seconds() / self.active_transition["seconds"]
super().progress(equipment=self.name, percent=percent)
if self.progress_percentage == 1.0:
self.content_list = [self.active_transition["result"]]
if isinstance(self.active_transition["result"], Effect):
self.active_transition[
"result"
].item_info.manager.register_active_effect(
self.active_transition["result"], self
)
else:
self.content_list = [self.active_transition["result"]]
self.reset()
self.check_active_transition()
# todo set active transition for fire/burnt?
def check_active_transition(self):
ingredients = collections.Counter(item.name for item in self.content_list)
for result, transition in self.transitions.items():
if transition.type == ItemType.Effect:
if set(ingredients.keys()).issubset(
transition.needs
) and transition.manager.can_start_effect_transition(transition, self):
if transition.seconds == 0:
transition.manager.register_active_effect(
Effect(name=transition.name, item_info=transition), self
)
else:
self.active_transition = {
"seconds": transition.seconds,
"result": Effect(
name=transition.name, item_info=transition
),
}
self.inverse_progress = True
break # ?
else:
if ingredients == transition.recipe:
if transition.seconds == 0:
self.content_ready = Item(name=result, item_info=transition)
else:
self.active_transition = {
"seconds": transition.seconds,
"result": Item(name=result, item_info=transition),
}
self.inverse_progress = transition.type == ItemType.Waste
break
else:
self.content_ready = None
def reset_content(self):
"""Reset the content attributes after the content was picked up from the equipment."""
self.content_list = []
......@@ -268,6 +337,7 @@ class CookingEquipment(Item):
"""Release the content when the player "picks up" the equipment with a plate in the hands"""
content = self.content_list
self.reset_content()
self.reset()
return content
@property
......@@ -340,3 +410,19 @@ class Plate(CookingEquipment):
elif self.clean:
return True
return False
class Effect:
def __init__(self, name: str, item_info: ItemInfo, uid: str = None):
self.uuid: str = uuid.uuid4().hex if uid is None else uid
self.name = name
self.item_info = item_info
self.progres_percentage = 0.0
def to_dict(self) -> dict:
return {
"id": self.uuid,
"type": self.name,
"progress_percentage": self.progres_percentage,
"inverse_progress": True,
}
......@@ -125,6 +125,7 @@ class EnvironmentHandler:
layout_config=environment_config.layout_config,
item_info=environment_config.item_info_config,
as_files=False,
env_name=env_id,
)
player_info = {}
for player_id in range(environment_config.number_players):
......@@ -219,7 +220,9 @@ class EnvironmentHandler:
self.envs[env_id].last_step_time = time.time_ns()
self.envs[env_id].environment.reset_env_time()
def get_state(self, player_hash: str) -> str: # -> StateRepresentation as json
def get_state(
self, player_hash: str
) -> str | int: # -> StateRepresentation as json
"""Get the current state representation of the environment for a player.
Args:
......@@ -235,7 +238,11 @@ class EnvironmentHandler:
):
return self.envs[
self.player_data[player_hash].env_id
].environment.get_json_state()
].environment.get_json_state(self.player_data[player_hash].player_id)
if player_hash not in self.player_data:
return 1
if self.player_data[player_hash].env_id not in self.envs:
return 2
def pause_env(self, manager_id: str, env_id: str, reason: str):
"""Pause the specified environment.
......@@ -586,6 +593,18 @@ def manage_websocket_message(message: str, client_id: str) -> PlayerRequestResul
"player_hash" in message_dict
), "'player_hash' key not in message dictionary'"
match request_type:
case PlayerRequestType.GET_STATE:
state = environment_handler.get_state(message_dict["player_hash"])
if isinstance(state, int):
return {
"request_type": message_dict["type"],
"status": 400,
"msg": "env id of player not in running envs"
if state == 2
else "player hash unknown",
"player_hash": None,
}
return state
case PlayerRequestType.READY:
accepted = environment_handler.set_player_ready(
message_dict["player_hash"]
......@@ -596,10 +615,6 @@ def manage_websocket_message(message: str, client_id: str) -> PlayerRequestResul
"status": 200 if accepted else 400,
"player_hash": message_dict["player_hash"],
}
case PlayerRequestType.GET_STATE:
return environment_handler.get_state(message_dict["player_hash"])
case PlayerRequestType.ACTION:
assert (
"action" in message_dict
......@@ -655,6 +670,13 @@ class CreateEnvironmentConfig(BaseModel):
item_info_config: str # file content
environment_config: str # file content
layout_config: str # file content
seed: int
class ManageEnv(BaseModel):
manager_id: str
env_id: str
reason: str
class AdditionalPlayer(BaseModel):
......@@ -679,8 +701,10 @@ async def additional_player(creation: AdditionalPlayer) -> dict[str, PlayerInfo]
@app.post("/manage/stop_env/")
async def stop_env(manager_id: str, env_id: str, reason: str) -> str:
accept = environment_handler.stop_env(manager_id, env_id, reason)
async def stop_env(manage_env: ManageEnv) -> str:
accept = environment_handler.stop_env(
manage_env.manager_id, manage_env.env_id, manage_env.reason
)
if accept:
raise HTTPException(
status_code=403 if accept == 1 else 409,
......@@ -724,7 +748,10 @@ async def websocket_player_endpoint(websocket: WebSocket, client_id: str):
log.debug(f"Client #{client_id} disconnected")
def main(host: str, port: int, manager_ids: list[str]):
def main(
host: str, port: int, manager_ids: list[str], enable_websocket_logging: bool = False
):
setup_logging(enable_websocket_logging)
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
environment_handler.extend_allowed_manager(manager_ids)
......@@ -745,8 +772,7 @@ if __name__ == "__main__":
disable_websocket_logging_arguments(parser)
add_list_of_manager_ids_arguments(parser)
args = parser.parse_args()
setup_logging(args.enable_websocket_logging)
main(args.url, args.port, args.manager_ids)
main(args.url, args.port, args.manager_ids, args.enable_websocket_logging)
"""
Or in console:
uvicorn overcooked_simulator.fastapi_game_server:app --reload
......
This diff is collapsed.
......@@ -6,7 +6,7 @@
"disabled_bg": "#25292e",
"selected_bg": "#193754",
"dark_bg": "#15191e",
"normal_text": "#c5cbd8",
"normal_text": "#000000",
"hovered_text": "#FFFFFF",
"selected_text": "#FFFFFF",
"disabled_text": "#6d736f",
......@@ -92,5 +92,70 @@
"normal_border": "#000000",
"normal_text": "#000000"
}
},
"#players": {
"colours": {
"dark_bg": "#fffacd",
"normal_border": "#fffacd"
}
},
"#players_players": {
"colours": {
"dark_bg": "#fffacd"
}
},
"#players_bots": {
"colours": {
"dark_bg": "#fffacd"
}
},
"#number_players_label": {
"colours": {
"dark_bg": "#fffacd",
"normal_text": "#000000"
},
"font": {
"size": 14,
"bold": 1
}
},
"#number_bots_label": {
"colours": {
"dark_bg": "#fffacd",
"normal_text": "#000000"
},
"font": {
"size": 14,
"bold": 1,
"colour": "#000000"
}
},
"#multiple_keysets_button": {
"font": {
"size": 12,
"bold": 1,
"colour": "#000000"
}
},
"#split_players_button": {
"font": {
"size": 12,
"bold": 1,
"colour": "#000000"
}
},
"#controller_button": {
"font": {
"size": 12,
"bold": 1,
"colour": "#000000"
}
},
"#quantity_button": {
"font": {
"size": 24,
"bold": 1,
"colour": "#000000"
}
}
}
\ No newline at end of file
overcooked_simulator/gui_2d_vis/images/arrow_right.png

347 KiB | W: | H:

overcooked_simulator/gui_2d_vis/images/arrow_right.png

469 KiB | W: | H:

overcooked_simulator/gui_2d_vis/images/arrow_right.png
overcooked_simulator/gui_2d_vis/images/arrow_right.png
overcooked_simulator/gui_2d_vis/images/arrow_right.png
overcooked_simulator/gui_2d_vis/images/arrow_right.png
  • 2-up
  • Swipe
  • Onion skin
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment