Mentions légales du service

Skip to content
Snippets Groups Projects
Commit 1f920959 authored by Florent Bouchez Tichadou's avatar Florent Bouchez Tichadou
Browse files

When launching a level, do not do a "reload" but a regular, load.

Separated the  compile_reload_level into compile_level + reloading.
Moved the main level loop into its own method for greater readability/maintenability.
parent 000a805f
No related branches found
No related tags found
1 merge request!72When launching a level, do not do a "reload" but a regular, load.
......@@ -364,15 +364,24 @@ class GameView(EmptyView):
log.debug(f"New command for level: {cmd}")
self.cmd_buttons[cmd].child.start_blinking()
def compile_reload_level(self, event, target='all', force=False):
success = self.compile_level(target, force)
if success:
self.level.reload_program()
self.trigger_restart_level()
self.code_window.reload()
return success
def compile_level(self, target='all', force=False):
print("\n"+message('compiling/loading'))
result = self.level.compile_source(capture_output=True, target=target, force=force)
self.level.reload_program()
self.code_window.reload()
if result.returncode == 0:
print(message('compilation/success'))
self.trigger_restart_level()
return True
# otherwise, show compilation errors
......@@ -720,6 +729,10 @@ class GameView(EmptyView):
# Open or re-open code window if necessary
code_window.check_or_reopen()
## Instantiate the level object, must be done before initializing maps
# as objects will have the reference to the level_output_queue to send
# payloads to the GUI
# Clear the input queue
while not self.level_input_queue.empty():
item = self.level_input_queue.get()
......@@ -738,7 +751,7 @@ class GameView(EmptyView):
)
self.console_input_thread.set_level(self.level)
#Initializing WOP
# Initializing WOP
self.wop = spo.SpWop(self.metadata["arcade_skins"]["wop_main"], self.amap)
log.debug(f"created wop {self.wop}")
......@@ -759,14 +772,18 @@ class GameView(EmptyView):
else:
self.metadata['map_height'] = self.amap.height
# Verify if level needs to be re-compiled
# (e.g., if source more recent than executable)
ret = self.compile_reload_level(None)
ret = self.compile_level()
if not ret:
log.error("Cannot launch level")
return
self.level.load_program()
self.code_window.reload()
self.set_reset_level() # prepare the level with common code used also for restart
# start the thread that will handle easytracker for this level
......
......@@ -767,6 +767,7 @@ class AbstractLevel(ABC, TrackerHelp):
## Putting commands in our own queue
def send_console_command(self, command: str, cmdtype="gdb", prompt=False):
lvl.debug(f"Putting gdb command in level_in queue {command}")
if prompt:
self.print_prompt()
self.in_queue.put_nowait({"type": cmdtype, "command": command})
......@@ -774,6 +775,7 @@ class AbstractLevel(ABC, TrackerHelp):
## Putting commands in our own queue
def send_level_control(self, control_command: str):
lvl.debug(f"Putting level command in level_in queue {control_command}")
self.in_queue.put_nowait({"type": "control", "command": control_command})
def start_record(self):
......
......@@ -275,6 +275,91 @@ class LevelArcade(AbstractLevel, Thread):
lvl.error("Error while trying to add a restart. Not added.")
def in_level_loop(self):
"""
Logic to apply when level is running, i.e., while inferior is executed by gdb.
"""
# We do not initialize to self.tracker.pause_reason as it would be
# a BREAKPOINT (temporary breakpoint at start of program)
pause_type = PauseReasonType.START
self.print_prompt()
lvl.debug("Starting main debug loop")
exit_code = None
do_restart_level = False
do_exit_level = False
while pause_type is not PauseReasonType.EXITED: #, PauseReasonType.SIGNAL]:
lvl.debug(f"Inferior paused for reason {self.tracker.pause_reason}")
if pause_type == PauseReasonType.SIGNAL:
if 'SIGSEGV' in self.tracker.pause_reason.args:
exit_code = 139
lvl.info(f"Inferior got a segmentation fault")
self.send_print_thread_error("SEGMENTATION FAULT\n")
# TODO: better error message: notify GUI
break
if 'SIGINT' in self.tracker.pause_reason.args:
# regular interruption
lvl.info(f"Inferior interrupted")
else:
lvl.error(f"Inferior received an unknown signal")
self.send_print_thread_error(f"SIGNAL ERROR {self.tracker.pause_reason}\n")
exit_code = 255
break
payload = self.in_queue.get()
lvl.debug(f"Payload received: {payload}")
if payload is None:
# if the payload is None we terminate the level
assert False, "Should not use None to terminate tracker anymore"
# Now, a payload of type 'control' and command 'exit' must
# be sent
payload_type = payload["type"]
if payload_type == "control":
ret = self.execute_agdbentures_control(payload)
elif payload_type == "gdb":
ret = self.execute_gdb_command(payload)
else:
lvl.error(f"Unknown payload type {payload_type} in {payload}")
if ret == 'exit':
do_exit_level = True
exit_code = -1
break
if ret == 'restart':
do_restart_level = True
exit_code = -1
break
if ret == 'validate': # early launch validation, pretend inferior exited successfully
exit_code = 0
break
# We do not allow asking gdb to quit directly
# => must exit the level if we want to do so
assert ret != 'gdb_exit' # gdb was asked to quit
# exit_code = 1
# break
pause_type = self.tracker.pause_reason.type
if exit_code is None:
assert pause_type is PauseReasonType.EXITED, f"Wrong pause reason {pause_type}"
exit_code = self.tracker.exit_code
return do_exit_level, do_restart_level, exit_code
def run(self):
"""
This method is called when the level thread is started.
......@@ -299,77 +384,21 @@ class LevelArcade(AbstractLevel, Thread):
'action': "show_game",
})
self.do_start()
start_time_level = time.time()
# We do not initialize to self.tracker.pause_reason as it would be
# a BREAKPOINT (temporary breakpoint at start of program)
pause_type = PauseReasonType.START
self.print_prompt()
lvl.debug("Starting main debug loop")
do_restart_level = False
exit_code = None
while pause_type is not PauseReasonType.EXITED: #, PauseReasonType.SIGNAL]:
lvl.debug(f"Inferior paused for reason {self.tracker.pause_reason}")
if pause_type == PauseReasonType.SIGNAL:
if 'SIGSEGV' in self.tracker.pause_reason.args:
exit_code = 139
lvl.info(f"Inferior got a segmentation fault")
self.send_print_thread_error("SEGMENTATION FAULT\n")
# TODO: better error message: notify GUI
break
if 'SIGINT' in self.tracker.pause_reason.args:
# regular interruption
lvl.info(f"Inferior interrupted")
else:
lvl.error(f"Inferior received an unknown signal")
self.send_print_thread_error(f"SIGNAL ERROR {self.tracker.pause_reason}\n")
exit_code = 255
break
payload = self.in_queue.get()
lvl.debug(f"Payload received: {payload}")
if payload is None:
# if the payload is None we terminate the level
assert False, "Should not use None to terminate tracker anymore"
# Now, a payload of type 'control' and command 'exit' must
# be sent
payload_type = payload["type"]
if payload_type == "control":
ret = self.execute_agdbentures_control(payload)
elif payload_type == "gdb":
ret = self.execute_gdb_command(payload)
else:
lvl.error(f"Unknown payload type {payload_type} in {payload}")
if ret == 'exit':
self.update_time_played(start_time_level)
return
if ret == 'restart':
do_restart_level = True
break
if ret == 'validate': # early launch validation
exit_code = 0
break
# We do not allow asking gdb to quit directly
# => must exit the level if we want to do so
assert ret != 'gdb_exit' # gdb was asked to quit
# exit_code = 1
# break
self.do_start()
start_time_level = time.time()
pause_type = self.tracker.pause_reason.type
# The main loop playing the level
do_exit_level, do_restart_level, exit_code = self.in_level_loop()
self.update_time_played(start_time_level)
if do_exit_level:
lvl.debug(f"Exiting level thread {self}")
return
if do_restart_level:
# inferior has not exited, user has asked to restart
# the level, either by using the GUI or by typing 'start'
......@@ -382,11 +411,11 @@ class LevelArcade(AbstractLevel, Thread):
# tracker!
# Need to know if the level is validated or not
if exit_code is None:
exit_code = self.tracker.exit_code
lvl.info(f"Inferior exited with {exit_code} as exit code.")
self.inferior_started = False
assert exit_code >= 0
if exit_code != 0:
# failed
payload = {'topic': "inferior_exit", 'status': 'failed'}
......@@ -408,8 +437,11 @@ class LevelArcade(AbstractLevel, Thread):
self.send_to_gui(payload)
# self.end_level_loop() => TODO move rest into its own method?
# Waiting loop to know whether we restart the level or terminate it
while True:
# print("In END OF LEVEL LOOP")
payload = self.in_queue.get()
if payload is None:
self.terminate_tracker()
......@@ -437,7 +469,7 @@ class LevelArcade(AbstractLevel, Thread):
if payload["command"] == "exit":
lvl.debug(f"Exiting from waiting loop after level run in {self}")
return
if payload['command'] == 'restart':
self.update_number_restarts()
break
......
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