Files
Emberwild/scripts/core/bug_report.gd

89 lines
4.0 KiB
GDScript

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