Allow having an arbitrary number of repos in a level

This commit is contained in:
Sebastian Morr 2020-09-30 21:36:11 +02:00
parent 91a57c49d7
commit 065ca2a233
11 changed files with 161 additions and 79 deletions

View file

@ -4,12 +4,7 @@ class_name Level
var slug var slug
var description var description
var congrats var congrats
var start_commands var repos = {}
var goal_commands
var win_commands
var _goal_repository_path = game.tmp_prefix_inside+"/repos/goal/"
var _active_repository_path = game.tmp_prefix_inside+"/repos/active/"
# The path is an outer path. # The path is an outer path.
func load(path): func load(path):
@ -21,41 +16,91 @@ func load(path):
# This is an old-style level. # This is an old-style level.
description = helpers.read_file(path+"/description", "(no description)") description = helpers.read_file(path+"/description", "(no description)")
congrats = helpers.read_file(path+"/congrats", "Good job, you solved the level!\n\nFeel free to try a few more things or click 'Next Level'.") congrats = helpers.read_file(path+"/congrats", "Good job, you solved the level!\n\nFeel free to try a few more things or click 'Next Level'.")
start_commands = helpers.read_file(path+"/start", "")
goal_commands = helpers.read_file(path+"/goal", "") var yours = LevelRepo.new()
win_commands = helpers.read_file(path+"/win", "exit 1\n") yours.setup_commands = helpers.read_file(path+"/start", "")
#goal_commands = helpers.read_file(path+"/goal", "")
yours.win_commands = helpers.read_file(path+"/win", "")
repos["yours"] = yours
elif dir.file_exists(path): elif dir.file_exists(path):
# This is a new-style level. # This is a new-style level.
var config = helpers.parse(path) var config = helpers.parse(path)
description = config.get("description", "(no description)") description = config.get("description", "(no description)")
congrats = config.get("congrats", "Good job, you solved the level!\n\nFeel free to try a few more things or click 'Next Level'.") congrats = config.get("congrats", "Good job, you solved the level!\n\nFeel free to try a few more things or click 'Next Level'.")
start_commands = config.get("setup", "")
goal_commands = "" var keys = config.keys()
win_commands = config.get("win", "exit 1\n") var repo_setups = []
for k in keys:
if k.begins_with("setup"):
repo_setups.push_back(k)
var repo_wins = []
for k in keys:
if k.begins_with("win"):
repo_wins.push_back(k)
for k in repo_setups:
var repo
if " " in k:
repo = Array(k.split(" "))[1]
else:
repo = "yours"
if not repos.has(repo):
repos[repo] = LevelRepo.new()
repos[repo].setup_commands = config[k]
for k in repo_wins:
var repo
if " " in k:
repo = Array(k.split(" "))[1]
else:
repo = "yours"
repos[repo].win_commands = config[k]
else: else:
helpers.crash("Level %s does not exist." % path) helpers.crash("Level %s does not exist." % path)
for repo in repos:
repos[repo].path = game.tmp_prefix_inside+"repos/%s/" % repo
repos[repo].slug = repo
# Surround all lines indented with four spaces with [code] tags. # Surround all lines indented with four spaces with [code] tags.
var monospace_regex = RegEx.new() var monospace_regex = RegEx.new()
monospace_regex.compile("\n (.*)\n") monospace_regex.compile("\n (.*)\n")
description = monospace_regex.sub(description, "\n [code]$1[/code]\n", true) description = monospace_regex.sub(description, "\n [code]$1[/code]\n", true)
func construct(): func construct():
_construct_repo(start_commands +"\n"+ goal_commands, _goal_repository_path) for r in repos:
_construct_repo(start_commands, _active_repository_path) var repo = repos[r]
# We're actually destroying stuff here.
func _construct_repo(script_content, path): # Make sure that active_repository is in a temporary directory.
# We're actually destroying stuff here. helpers.careful_delete(repo.path)
# Make sure that active_repository is in a temporary directory.
helpers.careful_delete(path) game.global_shell.run("mkdir " + repo.path)
game.global_shell.cd(repo.path)
game.global_shell.run("mkdir " + path) game.global_shell.run("git init")
game.global_shell.cd(path) game.global_shell.run("git symbolic-ref HEAD refs/heads/main")
game.global_shell.run("git init")
game.global_shell.run("git symbolic-ref HEAD refs/heads/main") # Add other repos as remotes.
game.global_shell.run(script_content) for r2 in repos:
if r == r2:
continue
game.global_shell.run("git remote add %s %s" % [r2, repos[r2].path])
# Allow receiving a push of the checked-out branch.
game.global_shell.run("git config receive.denyCurrentBranch ignore")
for r in repos:
var repo = repos[r]
game.global_shell.cd(repo.path)
game.global_shell.run(repo.setup_commands)
func check_win(): func check_win():
game.global_shell.cd(_active_repository_path) var won = true
return game.global_shell.run("function win { %s; }; win 2>/dev/null >/dev/null && echo yes || echo no" % win_commands) == "yes\n" for r in repos:
var repo = repos[r]
if repo.win_commands != "":
game.global_shell.cd(repo.path)
if not game.global_shell.run("function win { %s\n}; win 2>/dev/null >/dev/null && echo yes || echo no" % repo.win_commands) == "yes\n":
won = false
return won

View file

@ -0,0 +1,32 @@
title = A pull and a conflict
author = blinry
[description]
You want to push your new commits to the server, but someone has already pushed their own changes.
In this situation, you need to pull first! Try that here - you'll have to resolve a merge conflict. Push your changes afterwards.
[congrats]
Good job! Here's some additional info: banana!
[setup yours]
echo fu > file
git add .
git commit -m "Initial commit"
git push
echo fi > file
git commit -a -m "Fi is good"
[setup origin]
echo fa > file
git add .
git commit -a -m "Fa is good"
[win origin]
test "$(git rev-parse HEAD^1^)" = "$(git rev-parse HEAD^2^)"

30
main.gd
View file

@ -8,8 +8,8 @@ var current_level
onready var terminal = $Columns/RightSide/Terminal onready var terminal = $Columns/RightSide/Terminal
onready var input = terminal.input onready var input = terminal.input
onready var output = terminal.output onready var output = terminal.output
onready var goal_repository = $Columns/Repositories/GoalRepository onready var repositories_node = $Columns/Repositories
onready var active_repository = $Columns/Repositories/ActiveRepository var repositories = {}
onready var level_select = $Columns/RightSide/TopStuff/Menu/LevelSelect onready var level_select = $Columns/RightSide/TopStuff/Menu/LevelSelect
onready var chapter_select = $Columns/RightSide/TopStuff/Menu/ChapterSelect onready var chapter_select = $Columns/RightSide/TopStuff/Menu/ChapterSelect
onready var next_level_button = $Columns/RightSide/TopStuff/Menu/NextLevelButton onready var next_level_button = $Columns/RightSide/TopStuff/Menu/NextLevelButton
@ -57,17 +57,26 @@ func load_level(level_id):
AudioServer.set_bus_mute(AudioServer.get_bus_index("Master"), true) AudioServer.set_bus_mute(AudioServer.get_bus_index("Master"), true)
levels.chapters[current_chapter].levels[current_level].construct() levels.chapters[current_chapter].levels[current_level].construct()
var goal_repository_path = game.tmp_prefix_inside+"/repos/goal/"
var active_repository_path = game.tmp_prefix_inside+"/repos/active/"
var level = levels.chapters[current_chapter].levels[current_level] var level = levels.chapters[current_chapter].levels[current_level]
level_description.bbcode_text = level.description level_description.bbcode_text = level.description
level_congrats.bbcode_text = level.congrats level_congrats.bbcode_text = level.congrats
level_name.text = level.slug level_name.text = level.slug
goal_repository.path = goal_repository_path for r in repositories_node.get_children():
active_repository.path = active_repository_path r.queue_free()
repositories = {}
for r in level.repos:
var repo = level.repos[r]
var new_repo = preload("res://repository.tscn").instance()
new_repo.path = repo.path
new_repo.label = repo.slug
new_repo.size_flags_horizontal = SIZE_EXPAND_FILL
repositories_node.add_child(new_repo)
repositories[r] = new_repo
terminal.repository = repositories[repositories.keys()[0]]
terminal.clear() terminal.clear()
# Unmute the audio after a while, so that player can hear pop sounds for # Unmute the audio after a while, so that player can hear pop sounds for
@ -81,6 +90,7 @@ func load_level(level_id):
# FIXME: Need to clean these up when switching levels somehow. # FIXME: Need to clean these up when switching levels somehow.
func reload_level(): func reload_level():
levels.reload()
load_level(current_level) load_level(current_level)
func load_next_level(): func load_next_level():
@ -104,6 +114,10 @@ func repopulate_chapters():
for c in levels.chapters: for c in levels.chapters:
chapter_select.add_item(c.slug) chapter_select.add_item(c.slug)
func check_win_condition(): func update_repos():
for r in repositories:
var repo = repositories[r]
repo.update_everything()
if levels.chapters[current_chapter].levels[current_level].check_win(): if levels.chapters[current_chapter].levels[current_level].check_win():
show_win_status() show_win_status()

View file

@ -1,8 +1,7 @@
[gd_scene load_steps=10 format=2] [gd_scene load_steps=9 format=2]
[ext_resource path="res://terminal.tscn" type="PackedScene" id=1] [ext_resource path="res://terminal.tscn" type="PackedScene" id=1]
[ext_resource path="res://main.gd" type="Script" id=2] [ext_resource path="res://main.gd" type="Script" id=2]
[ext_resource path="res://repository.tscn" type="PackedScene" id=3]
[ext_resource path="res://styles/alert_button.tres" type="StyleBox" id=4] [ext_resource path="res://styles/alert_button.tres" type="StyleBox" id=4]
[ext_resource path="res://tcp_server.tscn" type="PackedScene" id=5] [ext_resource path="res://tcp_server.tscn" type="PackedScene" id=5]
[ext_resource path="res://styles/theme.tres" type="Theme" id=6] [ext_resource path="res://styles/theme.tres" type="Theme" id=6]
@ -65,26 +64,6 @@ __meta__ = {
"_edit_use_anchors_": false "_edit_use_anchors_": false
} }
[node name="GoalRepository" parent="Columns/Repositories" instance=ExtResource( 3 )]
anchor_right = 0.0
anchor_bottom = 0.0
margin_right = 633.0
margin_bottom = 1070.0
size_flags_horizontal = 3
size_flags_vertical = 3
label = "Goal"
file_browser_active = false
[node name="ActiveRepository" parent="Columns/Repositories" instance=ExtResource( 3 )]
anchor_right = 0.0
anchor_bottom = 0.0
margin_left = 633.0
margin_right = 1267.0
margin_bottom = 1070.0
size_flags_horizontal = 3
size_flags_vertical = 3
label = "Your repository"
[node name="RightSide" type="VSplitContainer" parent="Columns"] [node name="RightSide" type="VSplitContainer" parent="Columns"]
margin_left = 1279.0 margin_left = 1279.0
margin_right = 1910.0 margin_right = 1910.0
@ -192,7 +171,6 @@ margin_top = 541.0
margin_right = 631.0 margin_right = 631.0
margin_bottom = 1070.0 margin_bottom = 1070.0
size_flags_vertical = 3 size_flags_vertical = 3
repository_path = NodePath("../../../Columns/Repositories/ActiveRepository")
[node name="Test" type="Node2D" parent="."] [node name="Test" type="Node2D" parent="."]
visible = false visible = false
@ -213,4 +191,5 @@ __meta__ = {
[connection signal="button_down" from="Columns/RightSide/TopStuff/Menu/LevelSelect" to="." method="repopulate_levels"] [connection signal="button_down" from="Columns/RightSide/TopStuff/Menu/LevelSelect" to="." method="repopulate_levels"]
[connection signal="pressed" from="Columns/RightSide/TopStuff/Menu/ReloadButton" to="." method="reload_level"] [connection signal="pressed" from="Columns/RightSide/TopStuff/Menu/ReloadButton" to="." method="reload_level"]
[connection signal="pressed" from="Columns/RightSide/TopStuff/Menu/NextLevelButton" to="." method="load_next_level"] [connection signal="pressed" from="Columns/RightSide/TopStuff/Menu/NextLevelButton" to="." method="load_next_level"]
[connection signal="command_done" from="Columns/RightSide/Terminal" to="." method="update_repos"]
[connection signal="text_entered" from="Test/LineEdit" to="Test" method="send"] [connection signal="text_entered" from="Test/LineEdit" to="Test" method="send"]

View file

@ -20,6 +20,11 @@ _global_script_classes=[ {
"path": "res://level.gd" "path": "res://level.gd"
}, { }, {
"base": "Node", "base": "Node",
"class": "LevelRepo",
"language": "GDScript",
"path": "res://level_repo.gd"
}, {
"base": "Node",
"class": "Shell", "class": "Shell",
"language": "GDScript", "language": "GDScript",
"path": "res://shell.gd" "path": "res://shell.gd"
@ -27,6 +32,7 @@ _global_script_classes=[ {
_global_script_class_icons={ _global_script_class_icons={
"Chapter": "", "Chapter": "",
"Level": "", "Level": "",
"LevelRepo": "",
"Shell": "" "Shell": ""
} }

View file

@ -27,6 +27,9 @@ func _ready():
set_file_browser_active(file_browser_active) set_file_browser_active(file_browser_active)
set_simplified_view(simplified_view) set_simplified_view(simplified_view)
set_editable_path(editable_path) set_editable_path(editable_path)
set_path(path)
update_everything()
func _process(_delta): func _process(_delta):
nodes.rect_pivot_offset = nodes.rect_size / 2 nodes.rect_pivot_offset = nodes.rect_size / 2
@ -44,7 +47,8 @@ func there_is_a_git():
return shell.run("test -d .git && echo yes || echo no") == "yes\n" return shell.run("test -d .git && echo yes || echo no") == "yes\n"
func update_everything(): func update_everything():
file_browser.update() if file_browser:
file_browser.update()
if there_is_a_git(): if there_is_a_git():
update_head() update_head()
update_refs() update_refs()
@ -52,19 +56,22 @@ func update_everything():
update_objects() update_objects()
remove_gone_stuff() remove_gone_stuff()
else: else:
index.text = "" if index:
index.text = ""
for o in objects: for o in objects:
objects[o].queue_free() objects[o].queue_free()
objects = {} objects = {}
func set_path(new_path): func set_path(new_path):
path = new_path path = new_path
path_node.text = path if path_node:
path_node.text = path
shell.cd(new_path) shell.cd(new_path)
for o in objects.values(): for o in objects.values():
o.queue_free() o.queue_free()
objects = {} objects = {}
update_everything() if is_inside_tree():
update_everything()
func get_path(): func get_path():
return path return path

View file

@ -25,7 +25,7 @@ __meta__ = {
[node name="RepoVis" type="Control" parent="Rows"] [node name="RepoVis" type="Control" parent="Rows"]
margin_right = 1920.0 margin_right = 1920.0
margin_bottom = 994.0 margin_bottom = 925.0
mouse_filter = 1 mouse_filter = 1
size_flags_vertical = 3 size_flags_vertical = 3
__meta__ = { __meta__ = {
@ -110,11 +110,11 @@ __meta__ = {
[node name="FileBrowser" parent="Rows" instance=ExtResource( 4 )] [node name="FileBrowser" parent="Rows" instance=ExtResource( 4 )]
anchor_right = 0.0 anchor_right = 0.0
anchor_bottom = 0.0 anchor_bottom = 0.0
margin_top = 1006.0 margin_top = 937.0
margin_right = 1920.0 margin_right = 1920.0
margin_bottom = 1080.0 margin_bottom = 1080.0
size_flags_vertical = 3 size_flags_vertical = 3
size_flags_stretch_ratio = 0.08 size_flags_stretch_ratio = 0.16
[connection signal="mouse_entered" from="." to="." method="_on_mouse_entered"] [connection signal="mouse_entered" from="." to="." method="_on_mouse_entered"]
[connection signal="mouse_exited" from="." to="." method="_on_mouse_exited"] [connection signal="mouse_exited" from="." to="." method="_on_mouse_exited"]
[connection signal="pressed" from="Rows/RepoVis/Button" to="." method="update_everything"] [connection signal="pressed" from="Rows/RepoVis/Button" to="." method="update_everything"]

View file

@ -49,4 +49,3 @@ margin_left = 961.0
margin_right = 1910.0 margin_right = 1910.0
margin_bottom = 1070.0 margin_bottom = 1070.0
size_flags_horizontal = 3 size_flags_horizontal = 3
repository_path = NodePath("../../Columns/Repository")

View file

@ -1,5 +1,7 @@
extends Control extends Control
signal command_done
var thread var thread
var history_position = 0 var history_position = 0
@ -9,8 +11,8 @@ var git_commands_help = []
onready var input = $Rows/InputLine/Input onready var input = $Rows/InputLine/Input
onready var output = $Rows/TopHalf/Output onready var output = $Rows/TopHalf/Output
onready var completions = $Rows/TopHalf/Completions onready var completions = $Rows/TopHalf/Completions
export(NodePath) var repository_path #export(NodePath) var repository_path
onready var repository = get_node(repository_path) var repository
onready var command_dropdown = $Rows/InputLine/CommandDropdown onready var command_dropdown = $Rows/InputLine/CommandDropdown
onready var main = get_tree().get_root().get_node("Main") onready var main = get_tree().get_root().get_node("Main")
@ -32,13 +34,13 @@ func _ready():
helpers.crash("Could not connect TextEditor's hide signal") helpers.crash("Could not connect TextEditor's hide signal")
input.grab_focus() input.grab_focus()
var all_git_commands = repository.shell.run("git help -a | grep \"^ \\+[a-z-]\\+ \" -o") var all_git_commands = game.global_shell.run("git help -a | grep \"^ \\+[a-z-]\\+ \" -o")
git_commands = Array(all_git_commands.split("\n")) git_commands = Array(all_git_commands.split("\n"))
for i in range(git_commands.size()): for i in range(git_commands.size()):
git_commands[i] = git_commands[i].strip_edges(true, true) git_commands[i] = git_commands[i].strip_edges(true, true)
git_commands.pop_back() git_commands.pop_back()
var all_git_commands_help = repository.shell.run("git help -a | grep \" [A-Z].\\+$\" -o") var all_git_commands_help = game.global_shell.run("git help -a | grep \" [A-Z].\\+$\" -o")
git_commands_help = Array(all_git_commands_help.split("\n")) git_commands_help = Array(all_git_commands_help.split("\n"))
for i in range(git_commands_help.size()): for i in range(git_commands_help.size()):
git_commands_help[i] = git_commands_help[i].strip_edges(true, true) git_commands_help[i] = git_commands_help[i].strip_edges(true, true)
@ -95,18 +97,17 @@ func send_command_async(command):
func run_command_in_a_thread(command): func run_command_in_a_thread(command):
var o = repository.shell.run(command, false) var o = repository.shell.run(command, false)
if main:
main.check_win_condition()
input.text = "" input.text = ""
input.editable = true input.editable = true
if o.length() <= 200: if o.length() <= 1000:
output.text = output.text + "$ " + command + "\n" + o output.text = output.text + "$ " + command + "\n" + o
else: else:
$Pager/Text.text = o $Pager/Text.text = o
$Pager.popup() $Pager.popup()
repository.update_everything()
emit_signal("command_done")
#repository.update_everything()
func receive_output(text): func receive_output(text):
output.text += text output.text += text

View file

@ -110,7 +110,6 @@ syntax_highlighting = false
[node name="TCPServer" parent="." instance=ExtResource( 3 )] [node name="TCPServer" parent="." instance=ExtResource( 3 )]
[node name="Pager" type="WindowDialog" parent="."] [node name="Pager" type="WindowDialog" parent="."]
visible = true
anchor_right = 1.0 anchor_right = 1.0
anchor_bottom = 1.0 anchor_bottom = 1.0
margin_left = 18.0 margin_left = 18.0

View file

@ -15,13 +15,13 @@ func _process(_delta):
_client_connection = _server.take_connection() _client_connection = _server.take_connection()
var length = _client_connection.get_u8() var length = _client_connection.get_u8()
var filename = _client_connection.get_string(length) var filename = _client_connection.get_string(length)
filename = filename.replace("%srepos/active/" % game.tmp_prefix_inside, "") filename = filename.replace("%srepos/" % game.tmp_prefix_inside, "")
open(filename) open(filename)
func open(filename): func open(filename):
path = filename path = filename
var fixme_path = game.tmp_prefix_outside+"/repos/active/" var fixme_path = game.tmp_prefix_outside+"repos/"
var content = helpers.read_file(fixme_path+filename) var content = helpers.read_file(fixme_path+filename)
text = content text = content
@ -29,7 +29,7 @@ func open(filename):
grab_focus() grab_focus()
func save(): func save():
var fixme_path = game.tmp_prefix_outside+"/repos/active/" var fixme_path = game.tmp_prefix_outside+"repos/"
helpers.write_file(fixme_path+path, text) helpers.write_file(fixme_path+path, text)
close() close()