Report a bug in the pause menu (a new Gitea issue filled in with the version, computer, mode, land and where you are; copy the details; the log file), the playtest guide (docs/PLAYTEST.md), and the Cove's 0.6.0 parts in the player guide
This commit is contained in:
88
scripts/core/bug_report.gd
Normal file
88
scripts/core/bug_report.gd
Normal file
@@ -0,0 +1,88 @@
|
||||
extends RefCounted
|
||||
## Report a bug (0.6.0, the playtest; pause menu): opens a new issue on the
|
||||
## Emberwild repo in your browser, already filled in with a title to edit,
|
||||
## a few questions to answer, and the details we'd ask for anyway (version,
|
||||
## computer, single-player or online, which land, the day and time, where you
|
||||
## are). You read it, add what happened, and press Create Issue: nothing is
|
||||
## sent from the game itself. Posting needs an account on the Gitea server.
|
||||
##
|
||||
## The game's log (Godot writes it to user://logs/godot.log) helps with a
|
||||
## crash or an error: the pause menu shows where it is and opens its folder,
|
||||
## to attach the file to the issue.
|
||||
|
||||
const REPO := "https://git.psbailey.uk/Admin/Emberwild"
|
||||
|
||||
## An autoload (Net, Updates), looked up at run time: this script is also
|
||||
## loaded by tests that start before the autoloads are compiled in.
|
||||
static func _al(n: String) -> Node:
|
||||
return (Engine.get_main_loop() as SceneTree).root.get_node_or_null(n)
|
||||
|
||||
## Where this game's log is, on disk.
|
||||
static func log_path() -> String:
|
||||
return ProjectSettings.globalize_path("user://logs/godot.log")
|
||||
|
||||
## The same, with your home folder as "~" (so your user name isn't in an
|
||||
## issue anyone can read).
|
||||
static func log_path_short() -> String:
|
||||
var home := OS.get_environment("HOME") if OS.get_name() != "Windows" else OS.get_environment("USERPROFILE")
|
||||
var p := log_path()
|
||||
if not home.is_empty() and p.begins_with(home):
|
||||
p = "~" + p.substr(home.length())
|
||||
return p
|
||||
|
||||
static func log_dir() -> String:
|
||||
return ProjectSettings.globalize_path("user://logs")
|
||||
|
||||
## How you're playing: "single-player", or online with the server's rules.
|
||||
static func mode(world: Node) -> String:
|
||||
if world == null or not world.has_method("is_online") or not world.is_online():
|
||||
return "single-player"
|
||||
var net := _al("Net")
|
||||
var rules := "PvP" if bool(net.server_info.get("pvp", false)) else "co-op"
|
||||
if bool(net.server_info.get("raiding", false)):
|
||||
rules += ", raiding"
|
||||
return "online (%s%s)" % [rules, ", hosting" if net.hosting else ""]
|
||||
|
||||
## The details, one per line (what's in the issue, and what "Copy" copies).
|
||||
static func details(world: Node) -> Dictionary:
|
||||
var d := {
|
||||
"Version": "Emberwild %s (protocol %d)" % [String(ProjectSettings.get_setting("application/config/version", "?")), int(_al("Net").PROTOCOL)],
|
||||
"Computer": "%s %s" % [OS.get_name(), OS.get_version()],
|
||||
"Graphics": "%s (%s)" % [RenderingServer.get_video_adapter_name(), RenderingServer.get_current_rendering_method()],
|
||||
"Mode": mode(world),
|
||||
}
|
||||
if world != null and is_instance_valid(world) and world.has_method("worldlet_id"):
|
||||
d["Land"] = preload("res://scripts/world/terrain.gd").worldlet_name(String(world.worldlet_id()))
|
||||
if world.get("day_night") != null:
|
||||
var h: float = world.day_night.hours()
|
||||
d["Time"] = "day %d, %02d:%02d" % [int(world.day_night.day), int(h), int(fmod(h, 1.0) * 60.0)]
|
||||
if world.get("player") != null and is_instance_valid(world.player):
|
||||
var p: Vector3 = world.player.global_position
|
||||
d["Where"] = "%.0f, %.0f, %.0f" % [p.x, p.y, p.z]
|
||||
if world.get("terrain") != null:
|
||||
d["Seed"] = str(world.terrain.noise_seed)
|
||||
return d
|
||||
|
||||
static func details_text(world: Node) -> String:
|
||||
var d := details(world)
|
||||
var lines: Array[String] = []
|
||||
for k in d:
|
||||
lines.append("- %s: %s" % [k, d[k]])
|
||||
return "\n".join(lines)
|
||||
|
||||
static func body(world: Node) -> String:
|
||||
return "\n".join([
|
||||
"**What happened?**", "", "",
|
||||
"**What did you expect to happen?**", "", "",
|
||||
"**How can we make it happen again?** (what you did just before)", "1. ", "",
|
||||
"**Screenshot or log** (optional: drag a screenshot here, or the log file from %s)" % log_path_short(), "",
|
||||
"---", details_text(world)])
|
||||
|
||||
## The new-issue page, filled in.
|
||||
static func issue_url(world: Node, title: String = "") -> String:
|
||||
var t := title if not title.is_empty() else "Bug: "
|
||||
return "%s/issues/new?title=%s&body=%s" % [REPO, t.uri_encode(), body(world).uri_encode()]
|
||||
|
||||
## Opens it in the browser. Returns false when the browser couldn't be opened.
|
||||
static func open(world: Node) -> bool:
|
||||
return OS.shell_open(issue_url(world)) == OK
|
||||
1
scripts/core/bug_report.gd.uid
Normal file
1
scripts/core/bug_report.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://tnlssmningwq
|
||||
@@ -1,9 +1,11 @@
|
||||
extends Control
|
||||
## Pause menu (ESC): resume, controls, fullscreen, mouse sensitivity, quit.
|
||||
## Pause menu (ESC): resume, controls, report a bug, fullscreen, mouse
|
||||
## sensitivity, volumes, quit.
|
||||
## Pauses the scene tree while open; runs with PROCESS_MODE_ALWAYS so it still
|
||||
## gets input.
|
||||
|
||||
const UITheme = preload("res://scripts/ui/theme.gd")
|
||||
const BugReport = preload("res://scripts/core/bug_report.gd")
|
||||
|
||||
const CONTROLS := [
|
||||
["W A S D", "Move"], ["Shift", "Sprint"], ["Space", "Jump / swim up"],
|
||||
@@ -23,6 +25,8 @@ const CONTROLS := [
|
||||
var player: CharacterBody3D
|
||||
var _main: VBoxContainer
|
||||
var _controls: VBoxContainer
|
||||
var _report: VBoxContainer
|
||||
var _report_note: Label
|
||||
var _quit_btn: Button
|
||||
var _quit_armed := false
|
||||
var _online := false ## on a server: no pausing, no saving here
|
||||
@@ -60,6 +64,7 @@ func bind(p: CharacterBody3D) -> void:
|
||||
_main.add_child(title)
|
||||
_main.add_child(_button("Resume", close))
|
||||
_main.add_child(_button("Controls", _show_controls.bind(true)))
|
||||
_main.add_child(_button("Report a bug", _show_report.bind(true)))
|
||||
|
||||
var fs := CheckButton.new()
|
||||
fs.text = "Fullscreen"
|
||||
@@ -174,6 +179,31 @@ func bind(p: CharacterBody3D) -> void:
|
||||
_controls.add_child(grid)
|
||||
_controls.add_child(_button("Back", _show_controls.bind(false)))
|
||||
|
||||
# Report a bug page (0.6.0, the playtest: bug_report.gd).
|
||||
_report = VBoxContainer.new()
|
||||
_report.add_theme_constant_override("separation", 10)
|
||||
_report.visible = false
|
||||
stack.add_child(_report)
|
||||
var rt := UITheme.label("Report a bug", 26)
|
||||
rt.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
_report.add_child(rt)
|
||||
var how := UITheme.label("This opens a new issue on Emberwild's Gitea page in your browser, filled in with your version, computer and where you are in the game. Say what happened, then press Create Issue. (You'll need to be signed in there.) Nothing is sent from the game itself.", 15)
|
||||
how.autowrap_mode = TextServer.AUTOWRAP_WORD
|
||||
how.custom_minimum_size = Vector2(380, 0)
|
||||
_report.add_child(how)
|
||||
_report.add_child(_button("Open the bug report page", _on_report_open))
|
||||
_report.add_child(_button("Copy the details", _on_report_copy))
|
||||
_report.add_child(_button("Show the log file", _on_report_log))
|
||||
var lp := UITheme.label("For a crash or an error, attach the log:\n%s" % BugReport.log_path(), 13, UITheme.TEXT_DIM)
|
||||
lp.autowrap_mode = TextServer.AUTOWRAP_ARBITRARY
|
||||
lp.custom_minimum_size = Vector2(380, 0)
|
||||
_report.add_child(lp)
|
||||
_report_note = UITheme.label("", 14, UITheme.SELECTED)
|
||||
_report_note.autowrap_mode = TextServer.AUTOWRAP_WORD
|
||||
_report_note.custom_minimum_size = Vector2(380, 0)
|
||||
_report.add_child(_report_note)
|
||||
_report.add_child(_button("Back", _show_report.bind(false)))
|
||||
|
||||
func _button(text: String, cb: Callable) -> Button:
|
||||
var b := Button.new()
|
||||
b.text = text
|
||||
@@ -189,6 +219,7 @@ func is_open() -> bool:
|
||||
func open() -> void:
|
||||
visible = true
|
||||
_show_controls(false)
|
||||
_show_report(false)
|
||||
_disarm_quit()
|
||||
player.ui_open = true
|
||||
get_tree().paused = not _online # a shared world can't be paused by one player
|
||||
@@ -215,10 +246,12 @@ func close() -> void:
|
||||
get_tree().paused = false
|
||||
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
|
||||
|
||||
## ESC on the controls page goes back; on the main page it resumes.
|
||||
## ESC on the controls or bug report page goes back; on the main page it resumes.
|
||||
func back() -> void:
|
||||
if _controls.visible:
|
||||
_show_controls(false)
|
||||
elif _report.visible:
|
||||
_show_report(false)
|
||||
else:
|
||||
close()
|
||||
|
||||
@@ -226,6 +259,31 @@ func _show_controls(on: bool) -> void:
|
||||
_controls.visible = on
|
||||
_main.visible = not on
|
||||
|
||||
func _show_report(on: bool) -> void:
|
||||
_report.visible = on
|
||||
_main.visible = not on
|
||||
_report_note.text = ""
|
||||
|
||||
func _world() -> Node:
|
||||
return player.get_parent() if player != null else null
|
||||
|
||||
func _on_report_open() -> void:
|
||||
if DisplayServer.window_get_mode() == DisplayServer.WINDOW_MODE_FULLSCREEN:
|
||||
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) # (so the browser isn't hidden behind the game)
|
||||
if BugReport.open(_world()):
|
||||
_report_note.text = "Opened in your browser. (Can't post there? Copy the details and send them to whoever gave you the game.)"
|
||||
else:
|
||||
DisplayServer.clipboard_set(BugReport.issue_url(_world()))
|
||||
_report_note.text = "Couldn't open a browser: the link's on your clipboard to paste into one."
|
||||
|
||||
func _on_report_copy() -> void:
|
||||
DisplayServer.clipboard_set(BugReport.details_text(_world()))
|
||||
_report_note.text = "Copied: paste them into your message."
|
||||
|
||||
func _on_report_log() -> void:
|
||||
OS.shell_open(BugReport.log_dir())
|
||||
_report_note.text = "The log is godot.log in that folder."
|
||||
|
||||
func _on_fullscreen(on: bool) -> void:
|
||||
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN if on else DisplayServer.WINDOW_MODE_WINDOWED)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user