All
This commit is contained in:
parent
7a8ee29dcb
commit
0fe23420ab
800 changed files with 16547 additions and 0 deletions
21
addons/debug_menu/LICENSE.md
Normal file
21
addons/debug_menu/LICENSE.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# MIT License
|
||||
|
||||
Copyright © 2023-present Hugo Locurcio and contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
479
addons/debug_menu/debug_menu.gd
Normal file
479
addons/debug_menu/debug_menu.gd
Normal file
|
|
@ -0,0 +1,479 @@
|
|||
extends CanvasLayer
|
||||
|
||||
@export var fps: Label
|
||||
@export var frame_time: Label
|
||||
@export var frame_number: Label
|
||||
@export var frame_history_total_avg: Label
|
||||
@export var frame_history_total_min: Label
|
||||
@export var frame_history_total_max: Label
|
||||
@export var frame_history_total_last: Label
|
||||
@export var frame_history_cpu_avg: Label
|
||||
@export var frame_history_cpu_min: Label
|
||||
@export var frame_history_cpu_max: Label
|
||||
@export var frame_history_cpu_last: Label
|
||||
@export var frame_history_gpu_avg: Label
|
||||
@export var frame_history_gpu_min: Label
|
||||
@export var frame_history_gpu_max: Label
|
||||
@export var frame_history_gpu_last: Label
|
||||
@export var fps_graph: Panel
|
||||
@export var total_graph: Panel
|
||||
@export var cpu_graph: Panel
|
||||
@export var gpu_graph: Panel
|
||||
@export var information: Label
|
||||
@export var settings: Label
|
||||
|
||||
## The number of frames to keep in history for graph drawing and best/worst calculations.
|
||||
## Currently, this also affects how FPS is measured.
|
||||
const HISTORY_NUM_FRAMES = 150
|
||||
|
||||
const GRAPH_SIZE = Vector2(150, 25)
|
||||
const GRAPH_MIN_FPS = 10
|
||||
const GRAPH_MAX_FPS = 160
|
||||
const GRAPH_MIN_FRAMETIME = 1.0 / GRAPH_MIN_FPS
|
||||
const GRAPH_MAX_FRAMETIME = 1.0 / GRAPH_MAX_FPS
|
||||
|
||||
## Debug menu display style.
|
||||
enum Style {
|
||||
HIDDEN, ## Debug menu is hidden.
|
||||
VISIBLE_COMPACT, ## Debug menu is visible, with only the FPS, FPS cap (if any) and time taken to render the last frame.
|
||||
VISIBLE_DETAILED, ## Debug menu is visible with full information, including graphs.
|
||||
MAX, ## Represents the size of the Style enum.
|
||||
}
|
||||
|
||||
## The style to use when drawing the debug menu.
|
||||
var style := Style.HIDDEN:
|
||||
set(value):
|
||||
style = value
|
||||
match style:
|
||||
Style.HIDDEN:
|
||||
visible = false
|
||||
Style.VISIBLE_COMPACT, Style.VISIBLE_DETAILED:
|
||||
visible = true
|
||||
frame_number.visible = style == Style.VISIBLE_DETAILED
|
||||
$DebugMenu/VBoxContainer/FrameTimeHistory.visible = style == Style.VISIBLE_DETAILED
|
||||
$DebugMenu/VBoxContainer/FPSGraph.visible = style == Style.VISIBLE_DETAILED
|
||||
$DebugMenu/VBoxContainer/TotalGraph.visible = style == Style.VISIBLE_DETAILED
|
||||
$DebugMenu/VBoxContainer/CPUGraph.visible = style == Style.VISIBLE_DETAILED
|
||||
$DebugMenu/VBoxContainer/GPUGraph.visible = style == Style.VISIBLE_DETAILED
|
||||
information.visible = style == Style.VISIBLE_DETAILED
|
||||
settings.visible = style == Style.VISIBLE_DETAILED
|
||||
|
||||
# Value of `Time.get_ticks_usec()` on the previous frame.
|
||||
var last_tick := 0
|
||||
|
||||
var thread := Thread.new()
|
||||
|
||||
## Returns the sum of all values of an array (use as a parameter to `Array.reduce()`).
|
||||
var sum_func := func avg(accum: float, number: float) -> float: return accum + number
|
||||
|
||||
# History of the last `HISTORY_NUM_FRAMES` rendered frames.
|
||||
var frame_history_total: Array[float] = []
|
||||
var frame_history_cpu: Array[float] = []
|
||||
var frame_history_gpu: Array[float] = []
|
||||
var fps_history: Array[float] = [] # Only used for graphs.
|
||||
|
||||
var frametime_avg := GRAPH_MIN_FRAMETIME
|
||||
var frametime_cpu_avg := GRAPH_MAX_FRAMETIME
|
||||
var frametime_gpu_avg := GRAPH_MIN_FRAMETIME
|
||||
var frames_per_second := float(GRAPH_MIN_FPS)
|
||||
var frame_time_gradient := Gradient.new()
|
||||
|
||||
func _init() -> void:
|
||||
# This must be done here instead of `_ready()` to avoid having `visibility_changed` be emitted immediately.
|
||||
visible = false
|
||||
|
||||
if not InputMap.has_action("cycle_debug_menu"):
|
||||
# Create default input action if no user-defined override exists.
|
||||
# We can't do it in the editor plugin's activation code as it doesn't seem to work there.
|
||||
InputMap.add_action("cycle_debug_menu")
|
||||
var event := InputEventKey.new()
|
||||
event.keycode = KEY_F3
|
||||
InputMap.action_add_event("cycle_debug_menu", event)
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
fps_graph.draw.connect(_fps_graph_draw)
|
||||
total_graph.draw.connect(_total_graph_draw)
|
||||
cpu_graph.draw.connect(_cpu_graph_draw)
|
||||
gpu_graph.draw.connect(_gpu_graph_draw)
|
||||
|
||||
fps_history.resize(HISTORY_NUM_FRAMES)
|
||||
frame_history_total.resize(HISTORY_NUM_FRAMES)
|
||||
frame_history_cpu.resize(HISTORY_NUM_FRAMES)
|
||||
frame_history_gpu.resize(HISTORY_NUM_FRAMES)
|
||||
|
||||
# NOTE: Both FPS and frametimes are colored following FPS logic
|
||||
# (red = 10 FPS, yellow = 60 FPS, green = 110 FPS, cyan = 160 FPS).
|
||||
# This makes the color gradient non-linear.
|
||||
# Colors are taken from <https://tailwindcolor.com/>.
|
||||
frame_time_gradient.set_color(0, Color8(239, 68, 68)) # red-500
|
||||
frame_time_gradient.set_color(1, Color8(56, 189, 248)) # light-blue-400
|
||||
frame_time_gradient.add_point(0.3333, Color8(250, 204, 21)) # yellow-400
|
||||
frame_time_gradient.add_point(0.6667, Color8(128, 226, 95)) # 50-50 mix of lime-400 and green-400
|
||||
|
||||
get_viewport().size_changed.connect(update_settings_label)
|
||||
|
||||
# Display loading text while information is being queried,
|
||||
# in case the user toggles the full debug menu just after starting the project.
|
||||
information.text = "Loading hardware information...\n\n "
|
||||
settings.text = "Loading project information..."
|
||||
thread.start(
|
||||
func():
|
||||
# Disable thread safety checks as they interfere with this add-on.
|
||||
# This only affects this particular thread, not other thread instances in the project.
|
||||
# See <https://github.com/godotengine/godot/pull/78000> for details.
|
||||
# Use a Callable so that this can be ignored on Godot 4.0 without causing a script error
|
||||
# (thread safety checks were added in Godot 4.1).
|
||||
if Engine.get_version_info()["hex"] >= 0x040100:
|
||||
Callable(Thread, "set_thread_safety_checks_enabled").call(false)
|
||||
|
||||
# Enable required time measurements to display CPU/GPU frame time information.
|
||||
# These lines are time-consuming operations, so run them in a separate thread.
|
||||
RenderingServer.viewport_set_measure_render_time(get_viewport().get_viewport_rid(), true)
|
||||
update_information_label()
|
||||
update_settings_label()
|
||||
)
|
||||
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if event.is_action_pressed("cycle_debug_menu"):
|
||||
style = wrapi(style + 1, 0, Style.MAX) as Style
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
thread.wait_to_finish()
|
||||
|
||||
|
||||
## Update hardware information label (this can change at runtime based on window
|
||||
## size and graphics settings). This is only called when the window is resized.
|
||||
## To update when graphics settings are changed, the function must be called manually
|
||||
## using `DebugMenu.update_settings_label()`.
|
||||
func update_settings_label() -> void:
|
||||
settings.text = ""
|
||||
if ProjectSettings.has_setting("application/config/version"):
|
||||
settings.text += "Project Version: %s\n" % ProjectSettings.get_setting("application/config/version")
|
||||
|
||||
var rendering_method := str(ProjectSettings.get_setting_with_override("rendering/renderer/rendering_method"))
|
||||
var rendering_method_string := rendering_method
|
||||
match rendering_method:
|
||||
"forward_plus":
|
||||
rendering_method_string = "Forward+"
|
||||
"mobile":
|
||||
rendering_method_string = "Forward Mobile"
|
||||
"gl_compatibility":
|
||||
rendering_method_string = "Compatibility"
|
||||
settings.text += "Rendering Method: %s\n" % rendering_method_string
|
||||
|
||||
var viewport := get_viewport()
|
||||
|
||||
# The size of the viewport rendering, which determines which resolution 3D is rendered at.
|
||||
var viewport_render_size := Vector2i()
|
||||
|
||||
if viewport.content_scale_mode == Window.CONTENT_SCALE_MODE_VIEWPORT:
|
||||
viewport_render_size = viewport.get_visible_rect().size
|
||||
settings.text += "Viewport: %d×%d, Window: %d×%d\n" % [viewport.get_visible_rect().size.x, viewport.get_visible_rect().size.y, viewport.size.x, viewport.size.y]
|
||||
else:
|
||||
# Window size matches viewport size.
|
||||
viewport_render_size = viewport.size
|
||||
settings.text += "Viewport: %d×%d\n" % [viewport.size.x, viewport.size.y]
|
||||
|
||||
# Display 3D settings only if relevant.
|
||||
if viewport.get_camera_3d():
|
||||
var scaling_3d_mode_string := "(unknown)"
|
||||
match viewport.scaling_3d_mode:
|
||||
Viewport.SCALING_3D_MODE_BILINEAR:
|
||||
scaling_3d_mode_string = "Bilinear"
|
||||
Viewport.SCALING_3D_MODE_FSR:
|
||||
scaling_3d_mode_string = "FSR 1.0"
|
||||
Viewport.SCALING_3D_MODE_FSR2:
|
||||
scaling_3d_mode_string = "FSR 2.2"
|
||||
|
||||
var antialiasing_3d_string := ""
|
||||
if viewport.scaling_3d_mode == Viewport.SCALING_3D_MODE_FSR2:
|
||||
# The FSR2 scaling mode includes its own temporal antialiasing implementation.
|
||||
antialiasing_3d_string += (" + " if not antialiasing_3d_string.is_empty() else "") + "FSR 2.2"
|
||||
if viewport.scaling_3d_mode != Viewport.SCALING_3D_MODE_FSR2 and viewport.use_taa:
|
||||
# Godot's own TAA is ignored when using FSR2 scaling mode, as FSR2 provides its own TAA implementation.
|
||||
antialiasing_3d_string += (" + " if not antialiasing_3d_string.is_empty() else "") + "TAA"
|
||||
if viewport.msaa_3d >= Viewport.MSAA_2X:
|
||||
antialiasing_3d_string += (" + " if not antialiasing_3d_string.is_empty() else "") + "%d× MSAA" % pow(2, viewport.msaa_3d)
|
||||
if viewport.screen_space_aa == Viewport.SCREEN_SPACE_AA_FXAA:
|
||||
antialiasing_3d_string += (" + " if not antialiasing_3d_string.is_empty() else "") + "FXAA"
|
||||
|
||||
settings.text += "3D scale (%s): %d%% = %d×%d" % [
|
||||
scaling_3d_mode_string,
|
||||
viewport.scaling_3d_scale * 100,
|
||||
viewport_render_size.x * viewport.scaling_3d_scale,
|
||||
viewport_render_size.y * viewport.scaling_3d_scale,
|
||||
]
|
||||
|
||||
if not antialiasing_3d_string.is_empty():
|
||||
settings.text += "\n3D Antialiasing: %s" % antialiasing_3d_string
|
||||
|
||||
var environment := viewport.get_camera_3d().get_world_3d().environment
|
||||
if environment:
|
||||
if environment.ssr_enabled:
|
||||
settings.text += "\nSSR: %d Steps" % environment.ssr_max_steps
|
||||
|
||||
if environment.ssao_enabled:
|
||||
settings.text += "\nSSAO: On"
|
||||
if environment.ssil_enabled:
|
||||
settings.text += "\nSSIL: On"
|
||||
|
||||
if environment.sdfgi_enabled:
|
||||
settings.text += "\nSDFGI: %d Cascades" % environment.sdfgi_cascades
|
||||
|
||||
if environment.glow_enabled:
|
||||
settings.text += "\nGlow: On"
|
||||
|
||||
if environment.volumetric_fog_enabled:
|
||||
settings.text += "\nVolumetric Fog: On"
|
||||
var antialiasing_2d_string := ""
|
||||
if viewport.msaa_2d >= Viewport.MSAA_2X:
|
||||
antialiasing_2d_string = "%d× MSAA" % pow(2, viewport.msaa_2d)
|
||||
|
||||
if not antialiasing_2d_string.is_empty():
|
||||
settings.text += "\n2D Antialiasing: %s" % antialiasing_2d_string
|
||||
|
||||
|
||||
## Update hardware/software information label (this never changes at runtime).
|
||||
func update_information_label() -> void:
|
||||
var adapter_string := ""
|
||||
# Make "NVIDIA Corporation" and "NVIDIA" be considered identical (required when using OpenGL to avoid redundancy).
|
||||
if RenderingServer.get_video_adapter_vendor().trim_suffix(" Corporation") in RenderingServer.get_video_adapter_name():
|
||||
# Avoid repeating vendor name before adapter name.
|
||||
# Trim redundant suffix sometimes reported by NVIDIA graphics cards when using OpenGL.
|
||||
adapter_string = RenderingServer.get_video_adapter_name().trim_suffix("/PCIe/SSE2")
|
||||
else:
|
||||
adapter_string = RenderingServer.get_video_adapter_vendor() + " - " + RenderingServer.get_video_adapter_name().trim_suffix("/PCIe/SSE2")
|
||||
|
||||
# Graphics driver version information isn't always availble.
|
||||
var driver_info := OS.get_video_adapter_driver_info()
|
||||
var driver_info_string := ""
|
||||
if driver_info.size() >= 2:
|
||||
driver_info_string = driver_info[1]
|
||||
else:
|
||||
driver_info_string = "(unknown)"
|
||||
|
||||
var release_string := ""
|
||||
if OS.has_feature("editor"):
|
||||
# Editor build (implies `debug`).
|
||||
release_string = "editor"
|
||||
elif OS.has_feature("debug"):
|
||||
# Debug export template build.
|
||||
release_string = "debug"
|
||||
else:
|
||||
# Release export template build.
|
||||
release_string = "release"
|
||||
|
||||
var rendering_method := str(ProjectSettings.get_setting_with_override("rendering/renderer/rendering_method"))
|
||||
var rendering_driver := str(ProjectSettings.get_setting_with_override("rendering/rendering_device/driver"))
|
||||
var graphics_api_string := rendering_driver
|
||||
if rendering_method != "gl_compatibility":
|
||||
if rendering_driver == "d3d12":
|
||||
graphics_api_string = "Direct3D 12"
|
||||
elif rendering_driver == "metal":
|
||||
graphics_api_string = "Metal"
|
||||
elif rendering_driver == "vulkan":
|
||||
if OS.has_feature("macos") or OS.has_feature("ios"):
|
||||
graphics_api_string = "Vulkan via MoltenVK"
|
||||
else:
|
||||
graphics_api_string = "Vulkan"
|
||||
else:
|
||||
if rendering_driver == "opengl3_angle":
|
||||
graphics_api_string = "OpenGL via ANGLE"
|
||||
elif OS.has_feature("mobile") or rendering_driver == "opengl3_es":
|
||||
graphics_api_string = "OpenGL ES"
|
||||
elif OS.has_feature("web"):
|
||||
graphics_api_string = "WebGL"
|
||||
elif rendering_driver == "opengl3":
|
||||
graphics_api_string = "OpenGL"
|
||||
|
||||
information.text = (
|
||||
"%s, %d threads\n" % [OS.get_processor_name().replace("(R)", "").replace("(TM)", ""), OS.get_processor_count()]
|
||||
+ "%s %s (%s %s), %s %s\n" % [OS.get_name(), "64-bit" if OS.has_feature("64") else "32-bit", release_string, "double" if OS.has_feature("double") else "single", graphics_api_string, RenderingServer.get_video_adapter_api_version()]
|
||||
+ "%s, %s" % [adapter_string, driver_info_string]
|
||||
)
|
||||
|
||||
|
||||
func _fps_graph_draw() -> void:
|
||||
var fps_polyline := PackedVector2Array()
|
||||
fps_polyline.resize(HISTORY_NUM_FRAMES)
|
||||
for fps_index in fps_history.size():
|
||||
fps_polyline[fps_index] = Vector2(
|
||||
remap(fps_index, 0, fps_history.size(), 0, GRAPH_SIZE.x),
|
||||
remap(clampf(fps_history[fps_index], GRAPH_MIN_FPS, GRAPH_MAX_FPS), GRAPH_MIN_FPS, GRAPH_MAX_FPS, GRAPH_SIZE.y, 0.0)
|
||||
)
|
||||
# Don't use antialiasing to speed up line drawing, but use a width that scales with
|
||||
# viewport scale to keep the line easily readable on hiDPI displays.
|
||||
fps_graph.draw_polyline(fps_polyline, frame_time_gradient.sample(remap(frames_per_second, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)), 1.0)
|
||||
|
||||
|
||||
func _total_graph_draw() -> void:
|
||||
var total_polyline := PackedVector2Array()
|
||||
total_polyline.resize(HISTORY_NUM_FRAMES)
|
||||
for total_index in frame_history_total.size():
|
||||
total_polyline[total_index] = Vector2(
|
||||
remap(total_index, 0, frame_history_total.size(), 0, GRAPH_SIZE.x),
|
||||
remap(clampf(frame_history_total[total_index], GRAPH_MIN_FPS, GRAPH_MAX_FPS), GRAPH_MIN_FPS, GRAPH_MAX_FPS, GRAPH_SIZE.y, 0.0)
|
||||
)
|
||||
# Don't use antialiasing to speed up line drawing, but use a width that scales with
|
||||
# viewport scale to keep the line easily readable on hiDPI displays.
|
||||
total_graph.draw_polyline(total_polyline, frame_time_gradient.sample(remap(1000.0 / frametime_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)), 1.0)
|
||||
|
||||
|
||||
func _cpu_graph_draw() -> void:
|
||||
var cpu_polyline := PackedVector2Array()
|
||||
cpu_polyline.resize(HISTORY_NUM_FRAMES)
|
||||
for cpu_index in frame_history_cpu.size():
|
||||
cpu_polyline[cpu_index] = Vector2(
|
||||
remap(cpu_index, 0, frame_history_cpu.size(), 0, GRAPH_SIZE.x),
|
||||
remap(clampf(frame_history_cpu[cpu_index], GRAPH_MIN_FPS, GRAPH_MAX_FPS), GRAPH_MIN_FPS, GRAPH_MAX_FPS, GRAPH_SIZE.y, 0.0)
|
||||
)
|
||||
# Don't use antialiasing to speed up line drawing, but use a width that scales with
|
||||
# viewport scale to keep the line easily readable on hiDPI displays.
|
||||
cpu_graph.draw_polyline(cpu_polyline, frame_time_gradient.sample(remap(1000.0 / frametime_cpu_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)), 1.0)
|
||||
|
||||
|
||||
func _gpu_graph_draw() -> void:
|
||||
var gpu_polyline := PackedVector2Array()
|
||||
gpu_polyline.resize(HISTORY_NUM_FRAMES)
|
||||
for gpu_index in frame_history_gpu.size():
|
||||
gpu_polyline[gpu_index] = Vector2(
|
||||
remap(gpu_index, 0, frame_history_gpu.size(), 0, GRAPH_SIZE.x),
|
||||
remap(clampf(frame_history_gpu[gpu_index], GRAPH_MIN_FPS, GRAPH_MAX_FPS), GRAPH_MIN_FPS, GRAPH_MAX_FPS, GRAPH_SIZE.y, 0.0)
|
||||
)
|
||||
# Don't use antialiasing to speed up line drawing, but use a width that scales with
|
||||
# viewport scale to keep the line easily readable on hiDPI displays.
|
||||
gpu_graph.draw_polyline(gpu_polyline, frame_time_gradient.sample(remap(1000.0 / frametime_gpu_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)), 1.0)
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if visible:
|
||||
fps_graph.queue_redraw()
|
||||
total_graph.queue_redraw()
|
||||
cpu_graph.queue_redraw()
|
||||
gpu_graph.queue_redraw()
|
||||
|
||||
# Difference between the last two rendered frames in milliseconds.
|
||||
var frametime := (Time.get_ticks_usec() - last_tick) * 0.001
|
||||
|
||||
frame_history_total.push_back(frametime)
|
||||
if frame_history_total.size() > HISTORY_NUM_FRAMES:
|
||||
frame_history_total.pop_front()
|
||||
|
||||
# Frametimes are colored following FPS logic (red = 10 FPS, yellow = 60 FPS, green = 110 FPS, cyan = 160 FPS).
|
||||
# This makes the color gradient non-linear.
|
||||
frametime_avg = frame_history_total.reduce(sum_func) / frame_history_total.size()
|
||||
frame_history_total_avg.text = str(frametime_avg).pad_decimals(2)
|
||||
frame_history_total_avg.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
|
||||
|
||||
var frametime_min: float = frame_history_total.min()
|
||||
frame_history_total_min.text = str(frametime_min).pad_decimals(2)
|
||||
frame_history_total_min.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_min, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
|
||||
|
||||
var frametime_max: float = frame_history_total.max()
|
||||
frame_history_total_max.text = str(frametime_max).pad_decimals(2)
|
||||
frame_history_total_max.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_max, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
|
||||
|
||||
frame_history_total_last.text = str(frametime).pad_decimals(2)
|
||||
frame_history_total_last.modulate = frame_time_gradient.sample(remap(1000.0 / frametime, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
|
||||
|
||||
var viewport_rid := get_viewport().get_viewport_rid()
|
||||
var frametime_cpu := RenderingServer.viewport_get_measured_render_time_cpu(viewport_rid) + RenderingServer.get_frame_setup_time_cpu()
|
||||
frame_history_cpu.push_back(frametime_cpu)
|
||||
if frame_history_cpu.size() > HISTORY_NUM_FRAMES:
|
||||
frame_history_cpu.pop_front()
|
||||
|
||||
frametime_cpu_avg = frame_history_cpu.reduce(sum_func) / frame_history_cpu.size()
|
||||
frame_history_cpu_avg.text = str(frametime_cpu_avg).pad_decimals(2)
|
||||
frame_history_cpu_avg.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_cpu_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
|
||||
|
||||
var frametime_cpu_min: float = frame_history_cpu.min()
|
||||
frame_history_cpu_min.text = str(frametime_cpu_min).pad_decimals(2)
|
||||
frame_history_cpu_min.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_cpu_min, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
|
||||
|
||||
var frametime_cpu_max: float = frame_history_cpu.max()
|
||||
frame_history_cpu_max.text = str(frametime_cpu_max).pad_decimals(2)
|
||||
frame_history_cpu_max.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_cpu_max, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
|
||||
|
||||
frame_history_cpu_last.text = str(frametime_cpu).pad_decimals(2)
|
||||
frame_history_cpu_last.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_cpu, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
|
||||
|
||||
var frametime_gpu := RenderingServer.viewport_get_measured_render_time_gpu(viewport_rid)
|
||||
frame_history_gpu.push_back(frametime_gpu)
|
||||
if frame_history_gpu.size() > HISTORY_NUM_FRAMES:
|
||||
frame_history_gpu.pop_front()
|
||||
|
||||
frametime_gpu_avg = frame_history_gpu.reduce(sum_func) / frame_history_gpu.size()
|
||||
frame_history_gpu_avg.text = str(frametime_gpu_avg).pad_decimals(2)
|
||||
frame_history_gpu_avg.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_gpu_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
|
||||
|
||||
var frametime_gpu_min: float = frame_history_gpu.min()
|
||||
frame_history_gpu_min.text = str(frametime_gpu_min).pad_decimals(2)
|
||||
frame_history_gpu_min.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_gpu_min, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
|
||||
|
||||
var frametime_gpu_max: float = frame_history_gpu.max()
|
||||
frame_history_gpu_max.text = str(frametime_gpu_max).pad_decimals(2)
|
||||
frame_history_gpu_max.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_gpu_max, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
|
||||
|
||||
frame_history_gpu_last.text = str(frametime_gpu).pad_decimals(2)
|
||||
frame_history_gpu_last.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_gpu, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
|
||||
|
||||
frames_per_second = 1000.0 / frametime_avg
|
||||
fps_history.push_back(frames_per_second)
|
||||
if fps_history.size() > HISTORY_NUM_FRAMES:
|
||||
fps_history.pop_front()
|
||||
|
||||
fps.text = str(floor(frames_per_second)) + " FPS"
|
||||
var frame_time_color := frame_time_gradient.sample(remap(frames_per_second, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
|
||||
fps.modulate = frame_time_color
|
||||
|
||||
frame_time.text = str(frametime).pad_decimals(2) + " mspf"
|
||||
frame_time.modulate = frame_time_color
|
||||
|
||||
var vsync_string := ""
|
||||
match DisplayServer.window_get_vsync_mode():
|
||||
DisplayServer.VSYNC_ENABLED:
|
||||
vsync_string = "V-Sync"
|
||||
DisplayServer.VSYNC_ADAPTIVE:
|
||||
vsync_string = "Adaptive V-Sync"
|
||||
DisplayServer.VSYNC_MAILBOX:
|
||||
vsync_string = "Mailbox V-Sync"
|
||||
|
||||
if Engine.max_fps > 0 or OS.low_processor_usage_mode:
|
||||
# Display FPS cap determined by `Engine.max_fps` or low-processor usage mode sleep duration
|
||||
# (the lowest FPS cap is used).
|
||||
var low_processor_max_fps := roundi(1000000.0 / OS.low_processor_usage_mode_sleep_usec)
|
||||
var fps_cap := low_processor_max_fps
|
||||
if Engine.max_fps > 0:
|
||||
fps_cap = mini(Engine.max_fps, low_processor_max_fps)
|
||||
frame_time.text += " (cap: " + str(fps_cap) + " FPS"
|
||||
|
||||
if not vsync_string.is_empty():
|
||||
frame_time.text += " + " + vsync_string
|
||||
|
||||
frame_time.text += ")"
|
||||
else:
|
||||
if not vsync_string.is_empty():
|
||||
frame_time.text += " (" + vsync_string + ")"
|
||||
|
||||
frame_number.text = "Frame: " + str(Engine.get_frames_drawn())
|
||||
|
||||
last_tick = Time.get_ticks_usec()
|
||||
|
||||
|
||||
func _on_visibility_changed() -> void:
|
||||
if visible:
|
||||
# Reset graphs to prevent them from looking strange before `HISTORY_NUM_FRAMES` frames
|
||||
# have been drawn.
|
||||
var frametime_last := (Time.get_ticks_usec() - last_tick) * 0.001
|
||||
fps_history.resize(HISTORY_NUM_FRAMES)
|
||||
fps_history.fill(1000.0 / frametime_last)
|
||||
frame_history_total.resize(HISTORY_NUM_FRAMES)
|
||||
frame_history_total.fill(frametime_last)
|
||||
frame_history_cpu.resize(HISTORY_NUM_FRAMES)
|
||||
var viewport_rid := get_viewport().get_viewport_rid()
|
||||
frame_history_cpu.fill(RenderingServer.viewport_get_measured_render_time_cpu(viewport_rid) + RenderingServer.get_frame_setup_time_cpu())
|
||||
frame_history_gpu.resize(HISTORY_NUM_FRAMES)
|
||||
frame_history_gpu.fill(RenderingServer.viewport_get_measured_render_time_gpu(viewport_rid))
|
||||
1
addons/debug_menu/debug_menu.gd.uid
Normal file
1
addons/debug_menu/debug_menu.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://dpeqyx00y40f6
|
||||
401
addons/debug_menu/debug_menu.tscn
Normal file
401
addons/debug_menu/debug_menu.tscn
Normal file
|
|
@ -0,0 +1,401 @@
|
|||
[gd_scene load_steps=3 format=3 uid="uid://cggqb75a8w8r"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/debug_menu/debug_menu.gd" id="1_p440y"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ki0n8"]
|
||||
bg_color = Color(0, 0, 0, 0.25098)
|
||||
|
||||
[node name="CanvasLayer" type="CanvasLayer" node_paths=PackedStringArray("fps", "frame_time", "frame_number", "frame_history_total_avg", "frame_history_total_min", "frame_history_total_max", "frame_history_total_last", "frame_history_cpu_avg", "frame_history_cpu_min", "frame_history_cpu_max", "frame_history_cpu_last", "frame_history_gpu_avg", "frame_history_gpu_min", "frame_history_gpu_max", "frame_history_gpu_last", "fps_graph", "total_graph", "cpu_graph", "gpu_graph", "information", "settings")]
|
||||
layer = 128
|
||||
script = ExtResource("1_p440y")
|
||||
fps = NodePath("DebugMenu/VBoxContainer/FPS")
|
||||
frame_time = NodePath("DebugMenu/VBoxContainer/FrameTime")
|
||||
frame_number = NodePath("DebugMenu/VBoxContainer/FrameNumber")
|
||||
frame_history_total_avg = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/TotalAvg")
|
||||
frame_history_total_min = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/TotalMin")
|
||||
frame_history_total_max = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/TotalMax")
|
||||
frame_history_total_last = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/TotalLast")
|
||||
frame_history_cpu_avg = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/CPUAvg")
|
||||
frame_history_cpu_min = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/CPUMin")
|
||||
frame_history_cpu_max = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/CPUMax")
|
||||
frame_history_cpu_last = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/CPULast")
|
||||
frame_history_gpu_avg = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/GPUAvg")
|
||||
frame_history_gpu_min = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/GPUMin")
|
||||
frame_history_gpu_max = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/GPUMax")
|
||||
frame_history_gpu_last = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/GPULast")
|
||||
fps_graph = NodePath("DebugMenu/VBoxContainer/FPSGraph/Graph")
|
||||
total_graph = NodePath("DebugMenu/VBoxContainer/TotalGraph/Graph")
|
||||
cpu_graph = NodePath("DebugMenu/VBoxContainer/CPUGraph/Graph")
|
||||
gpu_graph = NodePath("DebugMenu/VBoxContainer/GPUGraph/Graph")
|
||||
information = NodePath("DebugMenu/VBoxContainer/Information")
|
||||
settings = NodePath("DebugMenu/VBoxContainer/Settings")
|
||||
|
||||
[node name="DebugMenu" type="Control" parent="."]
|
||||
custom_minimum_size = Vector2(400, 400)
|
||||
layout_mode = 3
|
||||
anchors_preset = 1
|
||||
anchor_left = 1.0
|
||||
anchor_right = 1.0
|
||||
offset_left = -416.0
|
||||
offset_top = 8.0
|
||||
offset_right = -16.0
|
||||
offset_bottom = 408.0
|
||||
grow_horizontal = 0
|
||||
size_flags_horizontal = 8
|
||||
size_flags_vertical = 4
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="DebugMenu"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 1
|
||||
anchor_left = 1.0
|
||||
anchor_right = 1.0
|
||||
offset_left = -300.0
|
||||
offset_bottom = 374.0
|
||||
grow_horizontal = 0
|
||||
mouse_filter = 2
|
||||
theme_override_constants/separation = 0
|
||||
|
||||
[node name="FPS" type="Label" parent="DebugMenu/VBoxContainer"]
|
||||
modulate = Color(0, 1, 0, 1)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 5
|
||||
theme_override_constants/line_spacing = 0
|
||||
theme_override_font_sizes/font_size = 18
|
||||
text = "60 FPS"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="FrameTime" type="Label" parent="DebugMenu/VBoxContainer"]
|
||||
modulate = Color(0, 1, 0, 1)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 3
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "16.67 mspf (cap: 123 FPS + Adaptive V-Sync)"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="FrameNumber" type="Label" parent="DebugMenu/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 3
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "Frame: 1234"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="FrameTimeHistory" type="GridContainer" parent="DebugMenu/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 8
|
||||
mouse_filter = 2
|
||||
theme_override_constants/h_separation = 0
|
||||
theme_override_constants/v_separation = 0
|
||||
columns = 5
|
||||
|
||||
[node name="Spacer" type="Control" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||
custom_minimum_size = Vector2(60, 0)
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="AvgHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 3
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "Average"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="MinHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 3
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "Best"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="MaxHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 3
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "Worst"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="LastHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 3
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "Last"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="TotalHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 3
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "Total:"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="TotalAvg" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||
modulate = Color(0, 1, 0, 1)
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 3
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "123.45"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="TotalMin" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||
modulate = Color(0, 1, 0, 1)
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 3
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "123.45"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="TotalMax" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||
modulate = Color(0, 1, 0, 1)
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 3
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "123.45"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="TotalLast" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||
modulate = Color(0, 1, 0, 1)
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 3
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "123.45"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="CPUHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 3
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "CPU:"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="CPUAvg" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||
modulate = Color(0, 1, 0, 1)
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 3
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "123.45"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="CPUMin" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||
modulate = Color(0, 1, 0, 1)
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 3
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "12.34"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="CPUMax" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||
modulate = Color(0, 1, 0, 1)
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 3
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "123.45"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="CPULast" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||
modulate = Color(0, 1, 0, 1)
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 3
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "123.45"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="GPUHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 3
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "GPU:"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="GPUAvg" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||
modulate = Color(0, 1, 0, 1)
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 3
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "123.45"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="GPUMin" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||
modulate = Color(0, 1, 0, 1)
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 3
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "1.23"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="GPUMax" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||
modulate = Color(0, 1, 0, 1)
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 3
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "123.45"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="GPULast" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||
modulate = Color(0, 1, 0, 1)
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 3
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "123.45"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="FPSGraph" type="HBoxContainer" parent="DebugMenu/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
alignment = 2
|
||||
|
||||
[node name="Title" type="Label" parent="DebugMenu/VBoxContainer/FPSGraph"]
|
||||
custom_minimum_size = Vector2(0, 27)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 8
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 3
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "FPS: ↑"
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Graph" type="Panel" parent="DebugMenu/VBoxContainer/FPSGraph"]
|
||||
custom_minimum_size = Vector2(150, 25)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
mouse_filter = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_ki0n8")
|
||||
|
||||
[node name="TotalGraph" type="HBoxContainer" parent="DebugMenu/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
alignment = 2
|
||||
|
||||
[node name="Title" type="Label" parent="DebugMenu/VBoxContainer/TotalGraph"]
|
||||
custom_minimum_size = Vector2(0, 27)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 8
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 3
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "Total: ↓"
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Graph" type="Panel" parent="DebugMenu/VBoxContainer/TotalGraph"]
|
||||
custom_minimum_size = Vector2(150, 25)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
mouse_filter = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_ki0n8")
|
||||
|
||||
[node name="CPUGraph" type="HBoxContainer" parent="DebugMenu/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
alignment = 2
|
||||
|
||||
[node name="Title" type="Label" parent="DebugMenu/VBoxContainer/CPUGraph"]
|
||||
custom_minimum_size = Vector2(0, 27)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 8
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 3
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "CPU: ↓"
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Graph" type="Panel" parent="DebugMenu/VBoxContainer/CPUGraph"]
|
||||
custom_minimum_size = Vector2(150, 25)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
mouse_filter = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_ki0n8")
|
||||
|
||||
[node name="GPUGraph" type="HBoxContainer" parent="DebugMenu/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
alignment = 2
|
||||
|
||||
[node name="Title" type="Label" parent="DebugMenu/VBoxContainer/GPUGraph"]
|
||||
custom_minimum_size = Vector2(0, 27)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 8
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 3
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "GPU: ↓"
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Graph" type="Panel" parent="DebugMenu/VBoxContainer/GPUGraph"]
|
||||
custom_minimum_size = Vector2(150, 25)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
mouse_filter = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_ki0n8")
|
||||
|
||||
[node name="Information" type="Label" parent="DebugMenu/VBoxContainer"]
|
||||
modulate = Color(1, 1, 1, 0.752941)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 3
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "12th Gen Intel(R) Core(TM) i0-1234K
|
||||
Windows 12 64-bit (double precision), Vulkan 1.2.34
|
||||
NVIDIA GeForce RTX 1234, 123.45.67"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="Settings" type="Label" parent="DebugMenu/VBoxContainer"]
|
||||
modulate = Color(0.8, 0.84, 1, 0.752941)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 3
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "Project Version: 1.2.3
|
||||
Rendering Method: Forward+
|
||||
Window: 1234×567, Viewport: 1234×567
|
||||
3D Scale (FSR 1.0): 100% = 1234×567
|
||||
3D Antialiasing: TAA + 2× MSAA + FXAA
|
||||
SSR: 123 Steps
|
||||
SSAO: On
|
||||
SSIL: On
|
||||
SDFGI: 1 Cascades
|
||||
Glow: On
|
||||
Volumetric Fog: On
|
||||
2D Antialiasing: 2× MSAA"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"]
|
||||
7
addons/debug_menu/plugin.cfg
Normal file
7
addons/debug_menu/plugin.cfg
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
[plugin]
|
||||
|
||||
name="Debug Menu"
|
||||
description="In-game debug menu displaying performance metrics and hardware information"
|
||||
author="Calinou"
|
||||
version="1.2.0"
|
||||
script="plugin.gd"
|
||||
29
addons/debug_menu/plugin.gd
Normal file
29
addons/debug_menu/plugin.gd
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
@tool
|
||||
extends EditorPlugin
|
||||
|
||||
func _enter_tree() -> void:
|
||||
add_autoload_singleton("DebugMenu", "res://addons/debug_menu/debug_menu.tscn")
|
||||
|
||||
# FIXME: This appears to do nothing.
|
||||
# if not ProjectSettings.has_setting("application/config/version"):
|
||||
# ProjectSettings.set_setting("application/config/version", "1.0.0")
|
||||
#
|
||||
# ProjectSettings.set_initial_value("application/config/version", "1.0.0")
|
||||
# ProjectSettings.add_property_info({
|
||||
# name = "application/config/version",
|
||||
# type = TYPE_STRING,
|
||||
# })
|
||||
#
|
||||
# if not InputMap.has_action("cycle_debug_menu"):
|
||||
# InputMap.add_action("cycle_debug_menu")
|
||||
# var event := InputEventKey.new()
|
||||
# event.keycode = KEY_F3
|
||||
# InputMap.action_add_event("cycle_debug_menu", event)
|
||||
#
|
||||
# ProjectSettings.save()
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
remove_autoload_singleton("DebugMenu")
|
||||
# Don't remove the project setting's value and input map action,
|
||||
# as the plugin may be re-enabled in the future.
|
||||
1
addons/debug_menu/plugin.gd.uid
Normal file
1
addons/debug_menu/plugin.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://cdct5p0xa3k1r
|
||||
21
addons/log/LICENSE
Normal file
21
addons/log/LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2024 Russell Matney
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
33
addons/log/color_theme_dark.tres
Normal file
33
addons/log/color_theme_dark.tres
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
[gd_resource type="Resource" script_class="LogColorTheme" load_steps=2 format=3 uid="uid://c2tfr7nw4y7ll"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://br8socgd1vvih" path="res://addons/log/log_color_theme.gd" id="1_6x53w"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_6x53w")
|
||||
color_src_prefix = Color(0.498039, 1, 0.831373, 1)
|
||||
color_addons_prefix = Color(0.803922, 0.521569, 0.247059, 1)
|
||||
color_test_prefix = Color(0.678431, 1, 0.184314, 1)
|
||||
color_comma = Color(1, 0.411765, 0.705882, 1)
|
||||
color_ampersand = Color(1, 0.498039, 0.313726, 1)
|
||||
color_pipe = Color(1, 0.498039, 0.313726, 1)
|
||||
color_carrot = Color(1, 0.498039, 0.313726, 1)
|
||||
colors_rainbow_delims = Array[Color]([Color(0.862745, 0.0784314, 0.235294, 1), Color(0.392157, 0.584314, 0.929412, 1), Color(1, 0.498039, 0.313726, 1), Color(1, 0.752941, 0.796078, 1), Color(0.803922, 0.521569, 0.247059, 1)])
|
||||
color_nil = Color(1, 0.498039, 0.313726, 1)
|
||||
color_bool = Color(1, 0.752941, 0.796078, 1)
|
||||
color_int = Color(0.392157, 0.584314, 0.929412, 1)
|
||||
color_float = Color(0.392157, 0.584314, 0.929412, 1)
|
||||
color_vectors = Color(0.392157, 0.584314, 0.929412, 1)
|
||||
color_rects = Color(0.392157, 0.584314, 0.929412, 1)
|
||||
color_class_name = Color(0.372549, 0.619608, 0.627451, 1)
|
||||
color_string = Color(0.662745, 0.662745, 0.662745, 1)
|
||||
color_string_name = Color(1, 0.752941, 0.796078, 1)
|
||||
color_node_path = Color(1, 0.752941, 0.796078, 1)
|
||||
color_type_color = Color(1, 0.752941, 0.796078, 1)
|
||||
color_rid = Color(1, 0.752941, 0.796078, 1)
|
||||
color_object = Color(1, 0.752941, 0.796078, 1)
|
||||
color_callable = Color(1, 0.752941, 0.796078, 1)
|
||||
color_signal = Color(1, 0.752941, 0.796078, 1)
|
||||
color_array = Color(1, 0.752941, 0.796078, 1)
|
||||
color_dictionary = Color(1, 0.752941, 0.796078, 1)
|
||||
color_packed_array = Color(1, 0.752941, 0.796078, 1)
|
||||
color_type_max = Color(1, 0.752941, 0.796078, 1)
|
||||
33
addons/log/color_theme_light.tres
Normal file
33
addons/log/color_theme_light.tres
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
[gd_resource type="Resource" script_class="LogColorTheme" load_steps=2 format=3 uid="uid://biby87a3dypvn"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://br8socgd1vvih" path="res://addons/log/log_color_theme.gd" id="1_kcklq"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_kcklq")
|
||||
color_src_prefix = Color(0, 0.545098, 0.545098, 1)
|
||||
color_addons_prefix = Color(0.545098, 0, 0, 1)
|
||||
color_test_prefix = Color(0, 0.392157, 0, 1)
|
||||
color_comma = Color(0.862745, 0.0784314, 0.235294, 1)
|
||||
color_ampersand = Color(1, 0.498039, 0.313726, 1)
|
||||
color_pipe = Color(1, 0.498039, 0.313726, 1)
|
||||
color_carrot = Color(1, 0.498039, 0.313726, 1)
|
||||
colors_rainbow_delims = Array[Color]([Color(0.862745, 0.0784314, 0.235294, 1), Color(0, 0, 0.545098, 1), Color(0.294118, 0, 0.509804, 1), Color(0.501961, 0.501961, 0, 1), Color(0.545098, 0.270588, 0.0745098, 1)])
|
||||
color_nil = Color(1, 0.498039, 0.313726, 1)
|
||||
color_bool = Color(0.545098, 0, 0, 1)
|
||||
color_int = Color(0.6, 0.196078, 0.8, 1)
|
||||
color_float = Color(0.6, 0.196078, 0.8, 1)
|
||||
color_vectors = Color(0.392157, 0.584314, 0.929412, 1)
|
||||
color_rects = Color(0.392157, 0.584314, 0.929412, 1)
|
||||
color_class_name = Color(0.372549, 0.619608, 0.627451, 1)
|
||||
color_string = Color(0.545098, 0, 0, 1)
|
||||
color_string_name = Color(0.545098, 0, 0, 1)
|
||||
color_node_path = Color(0.545098, 0, 0, 1)
|
||||
color_type_color = Color(0.545098, 0, 0, 1)
|
||||
color_rid = Color(0.545098, 0, 0, 1)
|
||||
color_object = Color(0.545098, 0, 0, 1)
|
||||
color_callable = Color(0.545098, 0, 0, 1)
|
||||
color_signal = Color(0.545098, 0, 0, 1)
|
||||
color_array = Color(0.545098, 0, 0, 1)
|
||||
color_dictionary = Color(0.545098, 0, 0, 1)
|
||||
color_packed_array = Color(0.545098, 0, 0, 1)
|
||||
color_type_max = Color(0.545098, 0, 0, 1)
|
||||
767
addons/log/log.gd
Normal file
767
addons/log/log.gd
Normal file
|
|
@ -0,0 +1,767 @@
|
|||
## Log.gd - colorized pretty printing functions
|
||||
##
|
||||
## [code]Log.pr(...)[/code] and [code]Log.prn(...)[/code] are drop-in replacements for [code]print(...)[/code].
|
||||
##
|
||||
## [br][br]
|
||||
## You can also [code]Log.warn(...)[/code] or [code]Log.error(...)[/code] to both print and push_warn/push_error.
|
||||
##
|
||||
## [br][br]
|
||||
## Custom object output is supported by implementing [code]to_pretty()[/code] on the object.
|
||||
##
|
||||
## [br][br]
|
||||
## For objects you don't own (built-ins or addons you don't want to edit),
|
||||
## there is a [code]register_type_overwrite(key, handler)[/code] helper.
|
||||
##
|
||||
## [br][br]
|
||||
## You can find up-to-date docs and examples in the Log.gd repo and docs site:
|
||||
## [br]
|
||||
## - https://github.com/russmatney/log.gd
|
||||
## [br]
|
||||
## - https://russmatney.github.io/log.gd
|
||||
##
|
||||
|
||||
@tool
|
||||
extends Object
|
||||
class_name Log
|
||||
|
||||
# helpers ####################################
|
||||
|
||||
static func assoc(opts: Dictionary, key: String, val: Variant) -> Dictionary:
|
||||
var _opts: Dictionary = opts.duplicate(true)
|
||||
_opts[key] = val
|
||||
return _opts
|
||||
|
||||
# settings helpers ####################################
|
||||
|
||||
static func initialize_setting(key: String, default_value: Variant, type: int, hint: int = PROPERTY_HINT_NONE, hint_string: String = "") -> void:
|
||||
if not ProjectSettings.has_setting(key):
|
||||
ProjectSettings.set(key, default_value)
|
||||
ProjectSettings.set_initial_value(key, default_value)
|
||||
ProjectSettings.add_property_info({name=key, type=type, hint=hint, hint_string=hint_string})
|
||||
|
||||
# settings keys and default ####################################
|
||||
|
||||
const KEY_PREFIX: String = "log_gd/config"
|
||||
static var is_config_setup: bool = false
|
||||
|
||||
# TODO drop this key
|
||||
const KEY_COLOR_THEME_DICT: String = "log_color_theme_dict"
|
||||
const KEY_COLOR_THEME: String = "log_color_theme"
|
||||
const KEY_COLOR_THEME_RESOURCE_PATH: String = "%s/color_resource_path" % KEY_PREFIX
|
||||
const KEY_DISABLE_COLORS: String = "%s/disable_colors" % KEY_PREFIX
|
||||
const KEY_MAX_ARRAY_SIZE: String = "%s/max_array_size" % KEY_PREFIX
|
||||
const KEY_SKIP_KEYS: String = "%s/dictionary_skip_keys" % KEY_PREFIX
|
||||
const KEY_USE_NEWLINES: String = "%s/use_newlines" % KEY_PREFIX
|
||||
const KEY_NEWLINE_MAX_DEPTH: String = "%s/newline_max_depth" % KEY_PREFIX
|
||||
const KEY_LOG_LEVEL: String = "%s/log_level" % KEY_PREFIX
|
||||
const KEY_WARN_TODO: String = "%s/warn_todo" % KEY_PREFIX
|
||||
const KEY_SHOW_LOG_LEVEL_SELECTOR: String = "%s/show_log_level_selector" % KEY_PREFIX
|
||||
const KEY_SHOW_TIMESTAMPS: String = "%s/show_timestamps" % KEY_PREFIX
|
||||
const KEY_TIMESTAMP_TYPE: String = "%s/timestamp_type" % KEY_PREFIX
|
||||
const KEY_HUMAN_READABLE_TIMESTAMP_FORMAT: String = "%s/human_readable_timestamp_format" % KEY_PREFIX
|
||||
|
||||
enum Levels {
|
||||
DEBUG,
|
||||
INFO,
|
||||
WARN,
|
||||
ERROR
|
||||
}
|
||||
|
||||
enum TimestampTypes {
|
||||
UNIX,
|
||||
TICKS_MSEC,
|
||||
TICKS_USEC,
|
||||
HUMAN_12HR,
|
||||
HUMAN_24HR
|
||||
}
|
||||
|
||||
const CONFIG_DEFAULTS := {
|
||||
KEY_COLOR_THEME_RESOURCE_PATH: "res://addons/log/color_theme_dark.tres",
|
||||
KEY_DISABLE_COLORS: false,
|
||||
KEY_MAX_ARRAY_SIZE: 20,
|
||||
KEY_SKIP_KEYS: ["layer_0/tile_data"],
|
||||
KEY_USE_NEWLINES: false,
|
||||
KEY_NEWLINE_MAX_DEPTH: -1,
|
||||
KEY_LOG_LEVEL: Levels.INFO,
|
||||
KEY_WARN_TODO: true,
|
||||
KEY_SHOW_LOG_LEVEL_SELECTOR: false,
|
||||
KEY_SHOW_TIMESTAMPS: false,
|
||||
KEY_TIMESTAMP_TYPE: TimestampTypes.HUMAN_12HR,
|
||||
KEY_HUMAN_READABLE_TIMESTAMP_FORMAT: "{hour}:{minute}:{second}",
|
||||
}
|
||||
|
||||
# settings setup ####################################
|
||||
|
||||
static func setup_settings(opts: Dictionary = {}) -> void:
|
||||
initialize_setting(KEY_COLOR_THEME_RESOURCE_PATH, CONFIG_DEFAULTS[KEY_COLOR_THEME_RESOURCE_PATH], TYPE_STRING, PROPERTY_HINT_FILE)
|
||||
initialize_setting(KEY_DISABLE_COLORS, CONFIG_DEFAULTS[KEY_DISABLE_COLORS], TYPE_BOOL)
|
||||
initialize_setting(KEY_MAX_ARRAY_SIZE, CONFIG_DEFAULTS[KEY_MAX_ARRAY_SIZE], TYPE_INT)
|
||||
initialize_setting(KEY_SKIP_KEYS, CONFIG_DEFAULTS[KEY_SKIP_KEYS], TYPE_PACKED_STRING_ARRAY)
|
||||
initialize_setting(KEY_USE_NEWLINES, CONFIG_DEFAULTS[KEY_USE_NEWLINES], TYPE_BOOL)
|
||||
initialize_setting(KEY_NEWLINE_MAX_DEPTH, CONFIG_DEFAULTS[KEY_NEWLINE_MAX_DEPTH], TYPE_INT)
|
||||
initialize_setting(KEY_LOG_LEVEL, CONFIG_DEFAULTS[KEY_LOG_LEVEL], TYPE_INT, PROPERTY_HINT_ENUM, "DEBUG,INFO,WARN,ERROR")
|
||||
initialize_setting(KEY_WARN_TODO, CONFIG_DEFAULTS[KEY_WARN_TODO], TYPE_BOOL)
|
||||
initialize_setting(KEY_SHOW_LOG_LEVEL_SELECTOR, CONFIG_DEFAULTS[KEY_SHOW_LOG_LEVEL_SELECTOR], TYPE_BOOL)
|
||||
initialize_setting(KEY_SHOW_TIMESTAMPS, CONFIG_DEFAULTS[KEY_SHOW_TIMESTAMPS], TYPE_BOOL)
|
||||
initialize_setting(KEY_TIMESTAMP_TYPE, CONFIG_DEFAULTS[KEY_TIMESTAMP_TYPE], TYPE_INT, PROPERTY_HINT_ENUM, "UNIX,TICKS_MSEC,TICKS_USEC,HUMAN_12HR,HUMAN_24HR")
|
||||
initialize_setting(KEY_HUMAN_READABLE_TIMESTAMP_FORMAT, CONFIG_DEFAULTS[KEY_HUMAN_READABLE_TIMESTAMP_FORMAT], TYPE_STRING)
|
||||
|
||||
# config setup ####################################
|
||||
|
||||
static var config: Dictionary = {}
|
||||
static func rebuild_config(opts: Dictionary = {}) -> void:
|
||||
for key: String in CONFIG_DEFAULTS.keys():
|
||||
# Keep config set in code before to_printable() is called for the first time
|
||||
var val: Variant = Log.config.get(key, ProjectSettings.get_setting(key, CONFIG_DEFAULTS[key]))
|
||||
|
||||
Log.config[key] = val
|
||||
|
||||
# hardcoding a resource-load b/c it seems like custom-resources can't be loaded by the project settings
|
||||
# https://github.com/godotengine/godot/issues/96219
|
||||
if val != null and key == KEY_COLOR_THEME_RESOURCE_PATH:
|
||||
Log.config[KEY_COLOR_THEME] = load(val)
|
||||
Log.config[KEY_COLOR_THEME_DICT] = Log.config[KEY_COLOR_THEME].to_color_dict()
|
||||
|
||||
Log.is_config_setup = true
|
||||
|
||||
# config getters ###################################################################
|
||||
|
||||
static func get_max_array_size() -> int:
|
||||
return Log.config.get(KEY_MAX_ARRAY_SIZE, CONFIG_DEFAULTS[KEY_MAX_ARRAY_SIZE])
|
||||
|
||||
static func get_dictionary_skip_keys() -> Array:
|
||||
return Log.config.get(KEY_SKIP_KEYS, CONFIG_DEFAULTS[KEY_SKIP_KEYS])
|
||||
|
||||
static func get_disable_colors() -> bool:
|
||||
return Log.config.get(KEY_DISABLE_COLORS, CONFIG_DEFAULTS[KEY_DISABLE_COLORS])
|
||||
|
||||
# TODO refactor away from the dict, create a termsafe LogColorTheme fallback
|
||||
static var warned_about_termsafe_fallback := false
|
||||
static func get_config_color_theme_dict() -> Dictionary:
|
||||
var color_theme = Log.config.get(KEY_COLOR_THEME)
|
||||
var color_dict = Log.config.get(KEY_COLOR_THEME_DICT)
|
||||
if color_dict != null:
|
||||
return color_dict
|
||||
if not warned_about_termsafe_fallback:
|
||||
print("Falling back to TERM_SAFE colors")
|
||||
warned_about_termsafe_fallback = true
|
||||
return LogColorTheme.COLORS_TERM_SAFE
|
||||
|
||||
static func get_config_color_theme() -> LogColorTheme:
|
||||
var color_theme = Log.config.get(KEY_COLOR_THEME)
|
||||
# TODO better warnings, fallbacks
|
||||
return color_theme
|
||||
|
||||
static func get_use_newlines() -> bool:
|
||||
return Log.config.get(KEY_USE_NEWLINES, CONFIG_DEFAULTS[KEY_USE_NEWLINES])
|
||||
|
||||
static func get_newline_max_depth() -> int:
|
||||
return Log.config.get(KEY_NEWLINE_MAX_DEPTH, CONFIG_DEFAULTS[KEY_NEWLINE_MAX_DEPTH])
|
||||
|
||||
static func get_log_level() -> int:
|
||||
return Log.config.get(KEY_LOG_LEVEL, CONFIG_DEFAULTS[KEY_LOG_LEVEL])
|
||||
|
||||
static func get_warn_todo() -> int:
|
||||
return Log.config.get(KEY_WARN_TODO, CONFIG_DEFAULTS[KEY_WARN_TODO])
|
||||
|
||||
static func get_show_timestamps() -> bool:
|
||||
return Log.config.get(KEY_SHOW_TIMESTAMPS, CONFIG_DEFAULTS[KEY_SHOW_TIMESTAMPS])
|
||||
|
||||
static func get_timestamp_type() -> TimestampTypes:
|
||||
return Log.config.get(KEY_TIMESTAMP_TYPE, CONFIG_DEFAULTS[KEY_TIMESTAMP_TYPE])
|
||||
|
||||
static func get_timestamp_format() -> String:
|
||||
return Log.config.get(KEY_HUMAN_READABLE_TIMESTAMP_FORMAT, CONFIG_DEFAULTS[KEY_HUMAN_READABLE_TIMESTAMP_FORMAT])
|
||||
|
||||
|
||||
## config setters ###################################################################
|
||||
|
||||
## Disable color-wrapping output.
|
||||
##
|
||||
## [br][br]
|
||||
## Useful to declutter the output if the environment does not support colors.
|
||||
## Note that some environments support only a subset of colors - you may prefer
|
||||
## [code]set_colors_termsafe()[/code].
|
||||
static func disable_colors() -> void:
|
||||
Log.config[KEY_DISABLE_COLORS] = true
|
||||
|
||||
## Re-enable color-wrapping output.
|
||||
static func enable_colors() -> void:
|
||||
Log.config[KEY_DISABLE_COLORS] = false
|
||||
|
||||
## Disable newlines in pretty-print output.
|
||||
##
|
||||
## [br][br]
|
||||
## Useful if you want your log output on a single line, typically for use with
|
||||
## log aggregation tools.
|
||||
static func disable_newlines() -> void:
|
||||
Log.config[KEY_USE_NEWLINES] = false
|
||||
|
||||
## Re-enable newlines in pretty-print output.
|
||||
static func enable_newlines() -> void:
|
||||
Log.config[KEY_USE_NEWLINES] = true
|
||||
|
||||
## Disable warning on Log.todo().
|
||||
static func disable_warn_todo() -> void:
|
||||
Log.config[KEY_WARN_TODO] = false
|
||||
|
||||
## Re-enable warning on Log.todo().
|
||||
static func enable_warn_todo() -> void:
|
||||
Log.config[KEY_WARN_TODO] = true
|
||||
|
||||
## Set the maximum depth of an object that will get its own newline.
|
||||
##
|
||||
## [br][br]
|
||||
## Useful if you have deeply nested objects where you're primarly interested
|
||||
## in easily parsing the information near the root of the object.
|
||||
static func set_newline_max_depth(new_depth: int) -> void:
|
||||
Log.config[KEY_NEWLINE_MAX_DEPTH] = new_depth
|
||||
|
||||
## Resets the maximum object depth for newlines to the default.
|
||||
static func reset_newline_max_depth() -> void:
|
||||
Log.config[KEY_USE_NEWLINES] = CONFIG_DEFAULTS[KEY_NEWLINE_MAX_DEPTH]
|
||||
|
||||
## Set the minimum level of logs that get printed
|
||||
static func set_log_level(new_log_level: int) -> void:
|
||||
Log.config[KEY_LOG_LEVEL] = new_log_level
|
||||
|
||||
## Show timestamps in log lines
|
||||
static func show_timestamps() -> void:
|
||||
Log.config[KEY_SHOW_TIMESTAMPS] = true
|
||||
|
||||
## Don't timestamps in log lines
|
||||
static func hide_timestamps() -> void:
|
||||
Log.config[KEY_SHOW_TIMESTAMPS] = false
|
||||
|
||||
## Use the given timestamp type
|
||||
static func use_timestamp_type(timestamp_type: Log.TimestampTypes) -> void:
|
||||
Log.config[KEY_TIMESTAMP_TYPE] = timestamp_type
|
||||
|
||||
## Use the given timestamp format
|
||||
static func use_timestamp_format(timestamp_format: String) -> void:
|
||||
Log.config[KEY_HUMAN_READABLE_TIMESTAMP_FORMAT] = timestamp_format
|
||||
|
||||
## set color theme ####################################
|
||||
|
||||
## Use the terminal safe color scheme, which should support colors in most tty-like environments.
|
||||
static func set_colors_termsafe() -> void:
|
||||
Log.config[KEY_COLOR_THEME_DICT] = LogColorTheme.COLORS_TERM_SAFE
|
||||
|
||||
## Use prettier colors - i.e. whatever LogColorTheme is configured.
|
||||
static func set_colors_pretty() -> void:
|
||||
var theme_path: Variant = Log.config.get(KEY_COLOR_THEME_RESOURCE_PATH)
|
||||
# TODO proper string, file, resource load check here
|
||||
if theme_path != null:
|
||||
Log.config[KEY_COLOR_THEME] = load(theme_path)
|
||||
Log.config[KEY_COLOR_THEME_DICT] = Log.config[KEY_COLOR_THEME].to_color_dict()
|
||||
else:
|
||||
print("WARNING no color theme resource path to load!")
|
||||
|
||||
## applying colors ####################################
|
||||
|
||||
static func should_use_color(opts: Dictionary = {}) -> bool:
|
||||
if OS.has_feature("ios") or OS.has_feature("web"):
|
||||
# ios and web (and likely others) don't handle colors well
|
||||
return false
|
||||
if Log.get_disable_colors():
|
||||
return false
|
||||
# supports per-print color skipping
|
||||
if opts.get("disable_colors", false):
|
||||
return false
|
||||
return true
|
||||
|
||||
static func color_wrap(s: Variant, opts: Dictionary = {}) -> String:
|
||||
# TODO refactor to use the color theme directly
|
||||
var colors: Dictionary = get_config_color_theme_dict()
|
||||
var color_theme: LogColorTheme = get_config_color_theme()
|
||||
|
||||
if not should_use_color(opts):
|
||||
return str(s)
|
||||
|
||||
var color: Variant = opts.get("color", "")
|
||||
if color == null or (color is String and color == ""):
|
||||
var s_type: Variant = opts.get("typeof", typeof(s))
|
||||
if s_type is String:
|
||||
# type overwrites
|
||||
color = colors.get(s_type)
|
||||
elif s_type is int and s_type == TYPE_STRING:
|
||||
# specific strings/punctuation
|
||||
var s_trimmed: String = str(s).strip_edges()
|
||||
if s_trimmed in colors:
|
||||
color = colors.get(s_trimmed)
|
||||
else:
|
||||
# fallback string color
|
||||
color = colors.get(s_type)
|
||||
else:
|
||||
# all other types
|
||||
color = colors.get(s_type)
|
||||
|
||||
if color is String and color == "" or color == null:
|
||||
print("Log.gd could not determine color for object: %s type: (%s)" % [str(s), typeof(s)])
|
||||
|
||||
if color is Array:
|
||||
# support rainbow delimiters
|
||||
if opts.get("typeof", "") in ["dict_key"]:
|
||||
# subtract 1 for dict_keys
|
||||
# we the keys are 'down' a nesting level, but we want the curly + dict keys to match
|
||||
color = color[opts.get("newline_depth", 0) - 1 % len(color)]
|
||||
else:
|
||||
color = color[opts.get("newline_depth", 0) % len(color)]
|
||||
|
||||
if color is Color:
|
||||
# get the colors back to something bb_code can handle
|
||||
color = color.to_html(false)
|
||||
|
||||
if color_theme and color_theme.has_bg():
|
||||
var bg_color: String = color_theme.get_bg_color(opts.get("newline_depth", 0)).to_html(false)
|
||||
return "[bgcolor=%s][color=%s]%s[/color][/bgcolor]" % [bg_color, color, s]
|
||||
return "[color=%s]%s[/color]" % [color, s]
|
||||
|
||||
## overwrites ###########################################################################
|
||||
|
||||
static var type_overwrites: Dictionary = {}
|
||||
|
||||
## Register a single type overwrite.
|
||||
##
|
||||
## [br][br]
|
||||
## The key should be either obj.get_class() or typeof(var). (Note that using typeof(var) may overwrite more broadly than expected).
|
||||
##
|
||||
## [br][br]
|
||||
## The handler is called with the object and an options dict.
|
||||
## [code]func(obj): return {name=obj.name}[/code]
|
||||
static func register_type_overwrite(key: String, handler: Callable) -> void:
|
||||
# TODO warning on key exists? support multiple handlers for same type?
|
||||
# validate the key/handler somehow?
|
||||
type_overwrites[key] = handler
|
||||
|
||||
## Register a dictionary of type overwrite.
|
||||
##
|
||||
## [br][br]
|
||||
## Expects a Dictionary like [code]{obj.get_class(): func(obj): return {key=obj.get_key()}}[/code].
|
||||
##
|
||||
## [br][br]
|
||||
## It depends on [code]obj.get_class()[/code] then [code]typeof(obj)[/code] for the key.
|
||||
## The handler is called with the object as the only argument. (e.g. [code]func(obj): return {name=obj.name}[/code]).
|
||||
static func register_type_overwrites(overwrites: Dictionary) -> void:
|
||||
type_overwrites.merge(overwrites, true)
|
||||
|
||||
static func clear_type_overwrites() -> void:
|
||||
type_overwrites = {}
|
||||
|
||||
## to_pretty ###########################################################################
|
||||
|
||||
## Returns the passed object as a bb-colorized string.
|
||||
##
|
||||
## [br][br]
|
||||
## The core of Log.gd's functionality.
|
||||
##
|
||||
## [br][br]
|
||||
## Can be useful to feed directly into a RichTextLabel.
|
||||
##
|
||||
static func to_pretty(msg: Variant, opts: Dictionary = {}) -> String:
|
||||
var newlines: bool = opts.get("newlines", Log.get_use_newlines())
|
||||
var newline_depth: int = opts.get("newline_depth", 0)
|
||||
var newline_max_depth: int = opts.get("newline_max_depth", Log.get_newline_max_depth())
|
||||
var indent_level: int = opts.get("indent_level", 0)
|
||||
|
||||
if not newlines:
|
||||
newline_max_depth = 0
|
||||
elif newline_max_depth == 0:
|
||||
newlines = false
|
||||
|
||||
# If newline_max_depth is negative, don't limit the depth
|
||||
if newline_max_depth > 0 and newline_depth >= newline_max_depth:
|
||||
newlines = false
|
||||
|
||||
if not "newline_depth" in opts:
|
||||
opts["newline_depth"] = newline_depth
|
||||
|
||||
if not "indent_level" in opts:
|
||||
opts["indent_level"] = indent_level
|
||||
|
||||
if not is_instance_valid(msg) and typeof(msg) == TYPE_OBJECT:
|
||||
return str("invalid instance: ", msg)
|
||||
|
||||
if msg == null:
|
||||
return Log.color_wrap(msg, opts)
|
||||
|
||||
if msg is Object and (msg as Object).get_class() in type_overwrites:
|
||||
var fn: Callable = type_overwrites.get((msg as Object).get_class())
|
||||
return Log.to_pretty(fn.call(msg), opts)
|
||||
elif typeof(msg) in type_overwrites:
|
||||
var fn: Callable = type_overwrites.get(typeof(msg))
|
||||
return Log.to_pretty(fn.call(msg), opts)
|
||||
|
||||
# objects
|
||||
if msg is Object and (msg as Object).has_method("to_pretty"):
|
||||
# using a cast and `call.("blah")` here it's "type safe"
|
||||
return Log.to_pretty((msg as Object).call("to_pretty"), opts)
|
||||
if msg is Object and (msg as Object).has_method("data"):
|
||||
return Log.to_pretty((msg as Object).call("data"), opts)
|
||||
# DEPRECATED
|
||||
if msg is Object and (msg as Object).has_method("to_printable"):
|
||||
return Log.to_pretty((msg as Object).call("to_printable"), opts)
|
||||
|
||||
# arrays
|
||||
if msg is Array or msg is PackedStringArray:
|
||||
var msg_array: Array = msg
|
||||
if len(msg) > Log.get_max_array_size():
|
||||
pr("[DEBUG]: truncating large array. total:", len(msg))
|
||||
msg_array = msg_array.slice(0, Log.get_max_array_size() - 1)
|
||||
if newlines:
|
||||
msg_array.append("...")
|
||||
|
||||
# shouldn't we be incrementing index_level here?
|
||||
var tmp: String = Log.color_wrap("[ ", opts)
|
||||
opts["newline_depth"] += 1
|
||||
var last: int = len(msg) - 1
|
||||
for i: int in range(len(msg)):
|
||||
if newlines and last > 1:
|
||||
tmp += Log.color_wrap("\n\t", opts)
|
||||
tmp += Log.to_pretty(msg[i],
|
||||
# duplicate here to prevent indenting-per-msg
|
||||
# e.g. when printing an array of dictionaries
|
||||
opts.duplicate(true))
|
||||
if i != last:
|
||||
tmp += Log.color_wrap(", ", opts)
|
||||
opts["newline_depth"] -= 1
|
||||
tmp += Log.color_wrap(" ]", opts)
|
||||
return tmp
|
||||
|
||||
# dictionary
|
||||
elif msg is Dictionary:
|
||||
var tmp: String = Log.color_wrap("{ ", opts)
|
||||
opts["newline_depth"] += 1
|
||||
var ct: int = len(msg)
|
||||
var last: Variant
|
||||
if len(msg) > 0:
|
||||
last = (msg as Dictionary).keys()[-1]
|
||||
var indent_updated = false
|
||||
for k: Variant in (msg as Dictionary).keys():
|
||||
var val: Variant
|
||||
if k in Log.get_dictionary_skip_keys():
|
||||
val = "..."
|
||||
else:
|
||||
if not indent_updated:
|
||||
indent_updated = true
|
||||
opts["indent_level"] += 1
|
||||
val = Log.to_pretty(msg[k], opts)
|
||||
if newlines and ct > 1:
|
||||
tmp += Log.color_wrap("\n\t", opts) \
|
||||
+ Log.color_wrap(range(indent_level)\
|
||||
.map(func(_i: int) -> String: return "\t")\
|
||||
.reduce(func(a: String, b: Variant) -> String: return str(a, b), ""), opts)
|
||||
var key: String = Log.color_wrap('"%s"' % k, Log.assoc(opts, "typeof", "dict_key"))
|
||||
tmp += "%s%s%s" % [key, Log.color_wrap(": ", opts), val]
|
||||
if last and str(k) != str(last):
|
||||
tmp += Log.color_wrap(", ", opts)
|
||||
opts["newline_depth"] -= 1
|
||||
tmp += Log.color_wrap(" }", opts)
|
||||
opts["indent_level"] -= 1 # ugh! updating the dict in-place
|
||||
return tmp
|
||||
|
||||
# strings
|
||||
elif msg is String:
|
||||
if msg == "":
|
||||
return '""'
|
||||
if "[color=" in msg and "[/color]" in msg:
|
||||
# passes through strings that might already be colorized?
|
||||
# can't remember this use-case
|
||||
# perhaps should use a regex and unit tests for something more robust
|
||||
return msg
|
||||
return Log.color_wrap(msg, opts)
|
||||
elif msg is StringName:
|
||||
return str(Log.color_wrap("&", opts), '"%s"' % msg)
|
||||
elif msg is NodePath:
|
||||
return str(Log.color_wrap("^", opts), '"%s"' % msg)
|
||||
|
||||
elif msg is Color:
|
||||
# probably too opinionated, but seeing 4 floats for color is noisey
|
||||
return Log.color_wrap(msg.to_html(false), Log.assoc(opts, "typeof", TYPE_COLOR))
|
||||
|
||||
# vectors
|
||||
elif msg is Vector2 or msg is Vector2i:
|
||||
return '%s%s%s%s%s' % [
|
||||
Log.color_wrap("(", opts),
|
||||
Log.color_wrap(msg.x, Log.assoc(opts, "typeof", "vector_value")),
|
||||
Log.color_wrap(",", opts),
|
||||
Log.color_wrap(msg.y, Log.assoc(opts, "typeof", "vector_value")),
|
||||
Log.color_wrap(")", opts),
|
||||
]
|
||||
|
||||
elif msg is Vector3 or msg is Vector3i:
|
||||
return '%s%s%s%s%s%s%s' % [
|
||||
Log.color_wrap("(", opts),
|
||||
Log.color_wrap(msg.x, Log.assoc(opts, "typeof", "vector_value")),
|
||||
Log.color_wrap(",", opts),
|
||||
Log.color_wrap(msg.y, Log.assoc(opts, "typeof", "vector_value")),
|
||||
Log.color_wrap(",", opts),
|
||||
Log.color_wrap(msg.z, Log.assoc(opts, "typeof", "vector_value")),
|
||||
Log.color_wrap(")", opts),
|
||||
]
|
||||
elif msg is Vector4 or msg is Vector4i:
|
||||
return '%s%s%s%s%s%s%s%s%s' % [
|
||||
Log.color_wrap("(", opts),
|
||||
Log.color_wrap(msg.x, Log.assoc(opts, "typeof", "vector_value")),
|
||||
Log.color_wrap(",", opts),
|
||||
Log.color_wrap(msg.y, Log.assoc(opts, "typeof", "vector_value")),
|
||||
Log.color_wrap(",", opts),
|
||||
Log.color_wrap(msg.z, Log.assoc(opts, "typeof", "vector_value")),
|
||||
Log.color_wrap(",", opts),
|
||||
Log.color_wrap(msg.w, Log.assoc(opts, "typeof", "vector_value")),
|
||||
Log.color_wrap(")", opts),
|
||||
]
|
||||
|
||||
# packed scene
|
||||
elif msg is PackedScene:
|
||||
var msg_ps: PackedScene = msg
|
||||
if msg_ps.resource_path != "":
|
||||
return str(Log.color_wrap("PackedScene:", opts), '%s' % msg_ps.resource_path.get_file())
|
||||
elif msg_ps.get_script() != null and msg_ps.get_script().resource_path != "":
|
||||
var path: String = msg_ps.get_script().resource_path
|
||||
return Log.color_wrap(path.get_file(), Log.assoc(opts, "typeof", "class_name"))
|
||||
else:
|
||||
return Log.color_wrap(msg_ps, opts)
|
||||
|
||||
# resource
|
||||
elif msg is Resource:
|
||||
var msg_res: Resource = msg
|
||||
if msg_res.get_script() != null and msg_res.get_script().resource_path != "":
|
||||
var path: String = msg_res.get_script().resource_path
|
||||
return Log.color_wrap(path.get_file(), Log.assoc(opts, "typeof", "class_name"))
|
||||
elif msg_res.resource_path != "":
|
||||
var path: String = msg_res.resource_path
|
||||
return str(Log.color_wrap("Resource:", opts), '%s' % path.get_file())
|
||||
else:
|
||||
return Log.color_wrap(msg_res, opts)
|
||||
|
||||
# refcounted
|
||||
elif msg is RefCounted:
|
||||
var msg_ref: RefCounted = msg
|
||||
if msg_ref.get_script() != null and msg_ref.get_script().resource_path != "":
|
||||
var path: String = msg_ref.get_script().resource_path
|
||||
return Log.color_wrap(path.get_file(), Log.assoc(opts, "typeof", "class_name"))
|
||||
else:
|
||||
return Log.color_wrap(msg_ref.get_class(), Log.assoc(opts, "typeof", "class_name"))
|
||||
|
||||
# fallback to primitive-type lookup
|
||||
else:
|
||||
return Log.color_wrap(msg, opts)
|
||||
|
||||
## to_printable ###########################################################################
|
||||
|
||||
static func log_prefix(stack: Array) -> String:
|
||||
if len(stack) > 1:
|
||||
var call_site: Dictionary = stack[1]
|
||||
var call_site_source: String = call_site.get("source", "")
|
||||
var basename: String = call_site_source.get_file().get_basename()
|
||||
var line_num: String = str(call_site.get("line", 0))
|
||||
if call_site_source.match("*/test/*"):
|
||||
return "{" + basename + ":" + line_num + "}: "
|
||||
elif call_site_source.match("*/addons/*"):
|
||||
return "<" + basename + ":" + line_num + ">: "
|
||||
else:
|
||||
return "[" + basename + ":" + line_num + "]: "
|
||||
return ""
|
||||
|
||||
static func to_printable(msgs: Array, opts: Dictionary = {}) -> String:
|
||||
if not Log.is_config_setup:
|
||||
rebuild_config()
|
||||
|
||||
if not msgs is Array:
|
||||
msgs = [msgs]
|
||||
var stack: Array = opts.get("stack", [])
|
||||
var pretty: bool = opts.get("pretty", true)
|
||||
var m: String = ""
|
||||
if get_show_timestamps():
|
||||
m = "[%s]" % Log.timestamp()
|
||||
if len(stack) > 0:
|
||||
var prefix: String = Log.log_prefix(stack)
|
||||
var prefix_type: String
|
||||
if prefix != null and prefix[0] == "[":
|
||||
prefix_type = "SRC"
|
||||
elif prefix != null and prefix[0] == "{":
|
||||
prefix_type = "TEST"
|
||||
elif prefix != null and prefix[0] == "<":
|
||||
prefix_type = "ADDONS"
|
||||
if pretty:
|
||||
m += Log.color_wrap(prefix, Log.assoc(opts, "typeof", prefix_type))
|
||||
else:
|
||||
m += prefix
|
||||
for msg: Variant in msgs:
|
||||
# add a space between msgs
|
||||
if pretty:
|
||||
m += "%s " % Log.to_pretty(msg, opts)
|
||||
else:
|
||||
m += "%s " % str(msg)
|
||||
return m.trim_suffix(" ")
|
||||
|
||||
static func timestamp() -> String:
|
||||
match Log.get_timestamp_type():
|
||||
Log.TimestampTypes.UNIX:
|
||||
return "%d" % Time.get_unix_time_from_system()
|
||||
Log.TimestampTypes.TICKS_MSEC:
|
||||
return "%d" % Time.get_ticks_msec()
|
||||
Log.TimestampTypes.TICKS_USEC:
|
||||
return "%d" % Time.get_ticks_usec()
|
||||
Log.TimestampTypes.HUMAN_12HR:
|
||||
var time: Dictionary = Time.get_datetime_dict_from_system()
|
||||
var hour: int = time.hour % 12
|
||||
if hour == 0:
|
||||
hour = 12
|
||||
var meridiem: String = "AM" if time.hour < 12 else "PM"
|
||||
return Log.get_timestamp_format().format({
|
||||
"year": time.year,
|
||||
"month": "%02d" % time.month,
|
||||
"day": "%02d" % time.day,
|
||||
"hour": hour,
|
||||
"minute": "%02d" % time.minute,
|
||||
"second": "%02d" % time.second,
|
||||
"meridiem": meridiem,
|
||||
"dst": time.dst
|
||||
})
|
||||
Log.TimestampTypes.HUMAN_24HR:
|
||||
var time: Dictionary = Time.get_datetime_dict_from_system()
|
||||
return Log.get_timestamp_format().format({
|
||||
"year": time.year,
|
||||
"month": "%02d" % time.month,
|
||||
"day": "%02d" % time.day,
|
||||
"hour": "%02d" % time.hour,
|
||||
"minute": "%02d" % time.minute,
|
||||
"second": "%02d" % time.second,
|
||||
"dst": time.dst
|
||||
})
|
||||
return "%d" % Time.get_unix_time_from_system()
|
||||
|
||||
## public print fns ###########################################################################
|
||||
|
||||
static func is_not_default(v: Variant) -> bool:
|
||||
return not v is String or (v is String and v != "ZZZDEF")
|
||||
|
||||
## Pretty-print the passed arguments in a single line.
|
||||
static func pr(msg: Variant, msg2: Variant = "ZZZDEF", msg3: Variant = "ZZZDEF", msg4: Variant = "ZZZDEF", msg5: Variant = "ZZZDEF", msg6: Variant = "ZZZDEF", msg7: Variant = "ZZZDEF") -> void:
|
||||
var msgs: Array = [msg, msg2, msg3, msg4, msg5, msg6, msg7]
|
||||
msgs = msgs.filter(Log.is_not_default)
|
||||
var m: String = Log.to_printable(msgs, {stack=get_stack()})
|
||||
print_rich(m)
|
||||
|
||||
## Pretty-print the passed arguments, expanding dictionaries and arrays with a newline and indentation.
|
||||
static func prn(msg: Variant, msg2: Variant = "ZZZDEF", msg3: Variant = "ZZZDEF", msg4: Variant = "ZZZDEF", msg5: Variant = "ZZZDEF", msg6: Variant = "ZZZDEF", msg7: Variant = "ZZZDEF") -> void:
|
||||
var msgs: Array = [msg, msg2, msg3, msg4, msg5, msg6, msg7]
|
||||
msgs = msgs.filter(Log.is_not_default)
|
||||
var m: String = Log.to_printable(msgs, {stack=get_stack(), newlines=true, newline_max_depth=1})
|
||||
print_rich(m)
|
||||
|
||||
## Pretty-print the passed arguments, expanding dictionaries and arrays with two newlines and indentation.
|
||||
static func prnn(msg: Variant, msg2: Variant = "ZZZDEF", msg3: Variant = "ZZZDEF", msg4: Variant = "ZZZDEF", msg5: Variant = "ZZZDEF", msg6: Variant = "ZZZDEF", msg7: Variant = "ZZZDEF") -> void:
|
||||
var msgs: Array = [msg, msg2, msg3, msg4, msg5, msg6, msg7]
|
||||
msgs = msgs.filter(Log.is_not_default)
|
||||
var m: String = Log.to_printable(msgs, {stack=get_stack(), newlines=true, newline_max_depth=2})
|
||||
print_rich(m)
|
||||
|
||||
## Pretty-print the passed arguments, expanding dictionaries and arrays with three newlines and indentation.
|
||||
static func prnnn(msg: Variant, msg2: Variant = "ZZZDEF", msg3: Variant = "ZZZDEF", msg4: Variant = "ZZZDEF", msg5: Variant = "ZZZDEF", msg6: Variant = "ZZZDEF", msg7: Variant = "ZZZDEF") -> void:
|
||||
var msgs: Array = [msg, msg2, msg3, msg4, msg5, msg6, msg7]
|
||||
msgs = msgs.filter(Log.is_not_default)
|
||||
var m: String = Log.to_printable(msgs, {stack=get_stack(), newlines=true, newline_max_depth=3})
|
||||
print_rich(m)
|
||||
|
||||
## Pretty-print the passed arguments in a single line.
|
||||
static func log(msg: Variant, msg2: Variant = "ZZZDEF", msg3: Variant = "ZZZDEF", msg4: Variant = "ZZZDEF", msg5: Variant = "ZZZDEF", msg6: Variant = "ZZZDEF", msg7: Variant = "ZZZDEF") -> void:
|
||||
var msgs: Array = [msg, msg2, msg3, msg4, msg5, msg6, msg7]
|
||||
msgs = msgs.filter(Log.is_not_default)
|
||||
var m: String = Log.to_printable(msgs, {stack=get_stack()})
|
||||
print_rich(m)
|
||||
|
||||
## Pretty-print the passed arguments in a single line.
|
||||
static func debug(msg: Variant, msg2: Variant = "ZZZDEF", msg3: Variant = "ZZZDEF", msg4: Variant = "ZZZDEF", msg5: Variant = "ZZZDEF", msg6: Variant = "ZZZDEF", msg7: Variant = "ZZZDEF") -> void:
|
||||
if get_log_level() > Log.Levels.DEBUG:
|
||||
return
|
||||
var msgs: Array = [msg, msg2, msg3, msg4, msg5, msg6, msg7]
|
||||
msgs = msgs.filter(Log.is_not_default)
|
||||
msgs.push_front("[DEBUG]")
|
||||
var m: String = Log.to_printable(msgs, {stack=get_stack()})
|
||||
print_rich(m)
|
||||
|
||||
## Pretty-print the passed arguments in a single line.
|
||||
static func info(msg: Variant, msg2: Variant = "ZZZDEF", msg3: Variant = "ZZZDEF", msg4: Variant = "ZZZDEF", msg5: Variant = "ZZZDEF", msg6: Variant = "ZZZDEF", msg7: Variant = "ZZZDEF") -> void:
|
||||
if get_log_level() > Log.Levels.INFO:
|
||||
return
|
||||
var msgs: Array = [msg, msg2, msg3, msg4, msg5, msg6, msg7]
|
||||
msgs = msgs.filter(Log.is_not_default)
|
||||
msgs.push_front("[INFO]")
|
||||
var m: String = Log.to_printable(msgs, {stack=get_stack()})
|
||||
print_rich(m)
|
||||
|
||||
## Like [code]Log.pr()[/code], but also calls push_warning() with the pretty string.
|
||||
static func warn(msg: Variant, msg2: Variant = "ZZZDEF", msg3: Variant = "ZZZDEF", msg4: Variant = "ZZZDEF", msg5: Variant = "ZZZDEF", msg6: Variant = "ZZZDEF", msg7: Variant = "ZZZDEF") -> void:
|
||||
if get_log_level() > Log.Levels.WARN:
|
||||
return
|
||||
var msgs: Array = [msg, msg2, msg3, msg4, msg5, msg6, msg7]
|
||||
msgs = msgs.filter(Log.is_not_default)
|
||||
var rich_msgs: Array = msgs.duplicate()
|
||||
rich_msgs.push_front("[color=yellow][WARN][/color]")
|
||||
print_rich(Log.to_printable(rich_msgs, {stack=get_stack()}))
|
||||
# skip the 'color' features in warnings to keep them readable in the debugger
|
||||
var m: String = Log.to_printable(msgs, {stack=get_stack(), disable_colors=true})
|
||||
push_warning(m)
|
||||
|
||||
## Like [code]Log.pr()[/code], but prepends a "[TODO]" and calls push_warning() with the pretty string.
|
||||
static func todo(msg: Variant, msg2: Variant = "ZZZDEF", msg3: Variant = "ZZZDEF", msg4: Variant = "ZZZDEF", msg5: Variant = "ZZZDEF", msg6: Variant = "ZZZDEF", msg7: Variant = "ZZZDEF") -> void:
|
||||
if get_warn_todo() and get_log_level() > Log.Levels.WARN:
|
||||
return
|
||||
elif not get_warn_todo() and get_log_level() > Log.Levels.INFO:
|
||||
return
|
||||
var msgs: Array = [msg, msg2, msg3, msg4, msg5, msg6, msg7]
|
||||
msgs = msgs.filter(Log.is_not_default)
|
||||
msgs.push_front("[TODO]")
|
||||
var rich_msgs: Array = msgs.duplicate()
|
||||
if get_warn_todo():
|
||||
rich_msgs.push_front("[color=yellow][WARN][/color]")
|
||||
print_rich(Log.to_printable(rich_msgs, {stack=get_stack()}))
|
||||
if get_warn_todo():
|
||||
var m: String = Log.to_printable(msgs, {stack=get_stack(), disable_colors=true})
|
||||
push_warning(m)
|
||||
|
||||
## Like [code]Log.pr()[/code], but also calls push_error() with the pretty string.
|
||||
static func err(msg: Variant, msg2: Variant = "ZZZDEF", msg3: Variant = "ZZZDEF", msg4: Variant = "ZZZDEF", msg5: Variant = "ZZZDEF", msg6: Variant = "ZZZDEF", msg7: Variant = "ZZZDEF") -> void:
|
||||
var msgs: Array = [msg, msg2, msg3, msg4, msg5, msg6, msg7]
|
||||
msgs = msgs.filter(Log.is_not_default)
|
||||
var rich_msgs: Array = msgs.duplicate()
|
||||
rich_msgs.push_front("[color=red][ERR][/color]")
|
||||
print_rich(Log.to_printable(rich_msgs, {stack=get_stack()}))
|
||||
# skip the 'color' features in errors to keep them readable in the debugger
|
||||
var m: String = Log.to_printable(msgs, {stack=get_stack(), disable_colors=true})
|
||||
push_error(m)
|
||||
|
||||
## Like [code]Log.pr()[/code], but also calls push_error() with the pretty string.
|
||||
static func error(msg: Variant, msg2: Variant = "ZZZDEF", msg3: Variant = "ZZZDEF", msg4: Variant = "ZZZDEF", msg5: Variant = "ZZZDEF", msg6: Variant = "ZZZDEF", msg7: Variant = "ZZZDEF") -> void:
|
||||
var msgs: Array = [msg, msg2, msg3, msg4, msg5, msg6, msg7]
|
||||
msgs = msgs.filter(Log.is_not_default)
|
||||
var rich_msgs: Array = msgs.duplicate()
|
||||
rich_msgs.push_front("[color=red][ERR][/color]")
|
||||
print_rich(Log.to_printable(rich_msgs, {stack=get_stack()}))
|
||||
# skip the 'color' features in errors to keep them readable in the debugger
|
||||
var m: String = Log.to_printable(msgs, {stack=get_stack(), disable_colors=true})
|
||||
push_error(m)
|
||||
|
||||
static func blank() -> void:
|
||||
print()
|
||||
|
||||
|
||||
## Helper that will both print() and print_rich() the enriched string
|
||||
static func _internal_debug(msg: Variant, msg2: Variant = "ZZZDEF", msg3: Variant = "ZZZDEF", msg4: Variant = "ZZZDEF", msg5: Variant = "ZZZDEF", msg6: Variant = "ZZZDEF", msg7: Variant = "ZZZDEF") -> void:
|
||||
var msgs: Array = [msg, msg2, msg3, msg4, msg5, msg6, msg7]
|
||||
msgs = msgs.filter(Log.is_not_default)
|
||||
var m: String = Log.to_printable(msgs, {stack=get_stack()})
|
||||
print("_internal_debug: ", m)
|
||||
print_rich(m)
|
||||
|
||||
|
||||
## DEPRECATED
|
||||
static func merge_theme_overwrites(_opts = {}) -> void:
|
||||
pass
|
||||
|
||||
## DEPRECATED
|
||||
static func clear_theme_overwrites() -> void:
|
||||
pass
|
||||
1
addons/log/log.gd.uid
Normal file
1
addons/log/log.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://bjhtrex267w1y
|
||||
225
addons/log/log_color_theme.gd
Normal file
225
addons/log/log_color_theme.gd
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
## LogColorTheme - Bring Your Own Color Theme to Log.gd!
|
||||
## [br][br]
|
||||
## Create a new resource of this type, and assign this via the Project Settings.
|
||||
## [br][br]
|
||||
## Be sure to enable Log.gd via Project > Plugins to see this type in the editor!
|
||||
##
|
||||
@tool
|
||||
extends Resource
|
||||
class_name LogColorTheme
|
||||
|
||||
## cycles ########################################
|
||||
|
||||
@export var bg_colors: Array[Color] = []
|
||||
|
||||
@export var colors_rainbow_delims: Array[Color] = ["crimson", "cornflower_blue", "coral", "pink", "peru"]
|
||||
|
||||
## delimiters ########################################
|
||||
|
||||
@export var color_comma: Color = "crimson"
|
||||
@export var color_ampersand: Color = "coral"
|
||||
@export var color_pipe: Color = "coral"
|
||||
@export var color_carrot: Color = "coral"
|
||||
|
||||
# @export var colors_dict_keys: Array[Color] = ["coral", "cadet_blue", "pink", "peru"]
|
||||
|
||||
## prefixes ########################################
|
||||
|
||||
@export var color_src_prefix: Color = "aquamarine"
|
||||
@export var color_addons_prefix: Color = "peru"
|
||||
@export var color_test_prefix: Color = "green_yellow"
|
||||
|
||||
## types ############################################
|
||||
|
||||
@export var color_nil: Color = "coral"
|
||||
@export var color_bool: Color = "pink"
|
||||
@export var color_int: Color = "cornflower_blue"
|
||||
@export var color_float: Color = "cornflower_blue"
|
||||
|
||||
@export var color_vectors: Color = "cornflower_blue"
|
||||
@export var color_rects: Color = "cornflower_blue"
|
||||
|
||||
@export var color_class_name: Color = "cadet_blue"
|
||||
@export var color_string: Color = "dark_gray"
|
||||
@export var color_string_name: Color = "pink"
|
||||
@export var color_node_path: Color = "pink"
|
||||
|
||||
@export var color_type_color: Color = "pink"
|
||||
@export var color_rid: Color = "pink"
|
||||
@export var color_object: Color = "pink"
|
||||
@export var color_callable: Color = "pink"
|
||||
@export var color_signal: Color = "pink"
|
||||
|
||||
@export var color_array: Color = "pink"
|
||||
@export var color_dictionary: Color = "pink"
|
||||
@export var color_packed_array: Color = "pink"
|
||||
@export var color_type_max: Color = "pink"
|
||||
|
||||
## to_color_dict ############################################
|
||||
|
||||
func to_color_dict() -> Dictionary:
|
||||
var color_dict = {}
|
||||
|
||||
color_dict["SRC"] = color_src_prefix
|
||||
color_dict["ADDONS"] = color_addons_prefix
|
||||
color_dict["TEST"] = color_test_prefix
|
||||
|
||||
color_dict["|"] = color_pipe
|
||||
color_dict["&"] = color_ampersand
|
||||
color_dict["^"] = color_carrot
|
||||
|
||||
# consider rainbow for commas too
|
||||
# color_dict[","] = colors_rainbow_delims
|
||||
color_dict[","] = color_comma
|
||||
|
||||
color_dict["("] = colors_rainbow_delims
|
||||
color_dict[")"] = colors_rainbow_delims
|
||||
color_dict["["] = colors_rainbow_delims
|
||||
color_dict["]"] = colors_rainbow_delims
|
||||
color_dict["{"] = colors_rainbow_delims
|
||||
color_dict["}"] = colors_rainbow_delims
|
||||
color_dict["<"] = colors_rainbow_delims
|
||||
color_dict[">"] = colors_rainbow_delims
|
||||
|
||||
color_dict["dict_key"] = colors_rainbow_delims
|
||||
color_dict["vector_value"] = color_float
|
||||
color_dict["class_name"] = color_class_name
|
||||
|
||||
color_dict[TYPE_NIL] = color_nil
|
||||
color_dict[TYPE_BOOL] = color_bool
|
||||
color_dict[TYPE_INT] = color_int
|
||||
color_dict[TYPE_FLOAT] = color_float
|
||||
|
||||
color_dict[TYPE_VECTOR2] = color_vectors
|
||||
color_dict[TYPE_VECTOR2I] = color_vectors
|
||||
color_dict[TYPE_RECT2] = color_rects
|
||||
color_dict[TYPE_RECT2I] = color_rects
|
||||
color_dict[TYPE_VECTOR3] = color_vectors
|
||||
color_dict[TYPE_VECTOR3I] = color_vectors
|
||||
color_dict[TYPE_TRANSFORM2D] = color_rects
|
||||
color_dict[TYPE_VECTOR4] = color_vectors
|
||||
color_dict[TYPE_VECTOR4I] = color_vectors
|
||||
color_dict[TYPE_PLANE] = color_rects
|
||||
color_dict[TYPE_QUATERNION] = color_rects
|
||||
color_dict[TYPE_AABB] = color_rects
|
||||
color_dict[TYPE_BASIS] = color_rects
|
||||
color_dict[TYPE_TRANSFORM3D] = color_rects
|
||||
color_dict[TYPE_PROJECTION] = color_rects
|
||||
|
||||
color_dict[TYPE_STRING] = color_string
|
||||
color_dict[TYPE_STRING_NAME] = color_string_name
|
||||
color_dict[TYPE_NODE_PATH] = color_node_path
|
||||
|
||||
color_dict[TYPE_COLOR] = color_type_color
|
||||
color_dict[TYPE_RID] = color_rid
|
||||
color_dict[TYPE_OBJECT] = color_object
|
||||
color_dict[TYPE_CALLABLE] = color_callable
|
||||
color_dict[TYPE_SIGNAL] = color_signal
|
||||
|
||||
# Do these ever get through if we're walking these ourselves?
|
||||
color_dict[TYPE_DICTIONARY] = color_dictionary
|
||||
color_dict[TYPE_ARRAY] = color_array
|
||||
|
||||
# Maybe want a hint/label before array openers?
|
||||
color_dict[TYPE_PACKED_BYTE_ARRAY] = color_packed_array
|
||||
color_dict[TYPE_PACKED_INT32_ARRAY] = color_packed_array
|
||||
color_dict[TYPE_PACKED_INT64_ARRAY] = color_packed_array
|
||||
color_dict[TYPE_PACKED_FLOAT32_ARRAY] = color_packed_array
|
||||
color_dict[TYPE_PACKED_FLOAT64_ARRAY] = color_packed_array
|
||||
color_dict[TYPE_PACKED_STRING_ARRAY] = color_packed_array
|
||||
color_dict[TYPE_PACKED_VECTOR2_ARRAY] = color_packed_array
|
||||
color_dict[TYPE_PACKED_VECTOR3_ARRAY] = color_packed_array
|
||||
color_dict[TYPE_PACKED_COLOR_ARRAY] = color_packed_array
|
||||
|
||||
color_dict[TYPE_MAX] = color_type_max
|
||||
|
||||
|
||||
return color_dict
|
||||
|
||||
## bg color
|
||||
|
||||
func has_bg() -> bool:
|
||||
if bg_colors == null:
|
||||
return false
|
||||
return len(bg_colors) > 0
|
||||
|
||||
func get_bg_color(i: int = 0) -> Color:
|
||||
return bg_colors[i % len(bg_colors)]
|
||||
|
||||
### static term safe helpers
|
||||
|
||||
# terminal safe colors:
|
||||
# - black
|
||||
# - red
|
||||
# - green
|
||||
# - yellow
|
||||
# - blue
|
||||
# - magenta
|
||||
# - pink
|
||||
# - purple
|
||||
# - cyan
|
||||
# - white
|
||||
# - orange
|
||||
# - gray
|
||||
|
||||
static var TERMSAFE_RAINBOW: Array = ["red", "blue", "green", "pink", "orange"]
|
||||
|
||||
static var COLORS_TERM_SAFE: Dictionary = {
|
||||
"SRC": "cyan",
|
||||
"ADDONS": "red",
|
||||
"TEST": "green",
|
||||
",": "red",
|
||||
"(": TERMSAFE_RAINBOW,
|
||||
")": TERMSAFE_RAINBOW,
|
||||
"[": TERMSAFE_RAINBOW,
|
||||
"]": TERMSAFE_RAINBOW,
|
||||
"{": TERMSAFE_RAINBOW,
|
||||
"}": TERMSAFE_RAINBOW,
|
||||
"<": TERMSAFE_RAINBOW,
|
||||
">": TERMSAFE_RAINBOW,
|
||||
"|": TERMSAFE_RAINBOW,
|
||||
"&": "orange",
|
||||
"^": "orange",
|
||||
"dict_key": TERMSAFE_RAINBOW,
|
||||
"vector_value": "green",
|
||||
"class_name": "magenta",
|
||||
TYPE_NIL: "pink",
|
||||
TYPE_BOOL: "pink",
|
||||
TYPE_INT: "green",
|
||||
TYPE_FLOAT: "green",
|
||||
TYPE_STRING: "pink",
|
||||
TYPE_VECTOR2: "green",
|
||||
TYPE_VECTOR2I: "green",
|
||||
TYPE_RECT2: "green",
|
||||
TYPE_RECT2I: "green",
|
||||
TYPE_VECTOR3: "green",
|
||||
TYPE_VECTOR3I: "green",
|
||||
TYPE_TRANSFORM2D: "pink",
|
||||
TYPE_VECTOR4: "green",
|
||||
TYPE_VECTOR4I: "green",
|
||||
TYPE_PLANE: "pink",
|
||||
TYPE_QUATERNION: "pink",
|
||||
TYPE_AABB: "pink",
|
||||
TYPE_BASIS: "pink",
|
||||
TYPE_TRANSFORM3D: "pink",
|
||||
TYPE_PROJECTION: "pink",
|
||||
TYPE_COLOR: "pink",
|
||||
TYPE_STRING_NAME: "pink",
|
||||
TYPE_NODE_PATH: "pink",
|
||||
TYPE_RID: "pink",
|
||||
TYPE_OBJECT: "pink",
|
||||
TYPE_CALLABLE: "pink",
|
||||
TYPE_SIGNAL: "pink",
|
||||
TYPE_DICTIONARY: "pink",
|
||||
TYPE_ARRAY: "pink",
|
||||
TYPE_PACKED_BYTE_ARRAY: "pink",
|
||||
TYPE_PACKED_INT32_ARRAY: "pink",
|
||||
TYPE_PACKED_INT64_ARRAY: "pink",
|
||||
TYPE_PACKED_FLOAT32_ARRAY: "pink",
|
||||
TYPE_PACKED_FLOAT64_ARRAY: "pink",
|
||||
TYPE_PACKED_STRING_ARRAY: "pink",
|
||||
TYPE_PACKED_VECTOR2_ARRAY: "pink",
|
||||
TYPE_PACKED_VECTOR3_ARRAY: "pink",
|
||||
TYPE_PACKED_COLOR_ARRAY: "pink",
|
||||
TYPE_MAX: "pink",
|
||||
}
|
||||
1
addons/log/log_color_theme.gd.uid
Normal file
1
addons/log/log_color_theme.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://br8socgd1vvih
|
||||
14
addons/log/plugin.cfg
Normal file
14
addons/log/plugin.cfg
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
[plugin]
|
||||
|
||||
name="Log.gd"
|
||||
description="A pretty-printing debug logger.
|
||||
|
||||
Log.pr(\"some str\", some_object)
|
||||
|
||||
- Colorizes printed data based on datatype
|
||||
- Recurses through nested data structures (Arrays and Dictionaries)
|
||||
- Prefixes logs with the callsite's source file and line_number
|
||||
"
|
||||
author="Russell Matney"
|
||||
version="v0.2.0"
|
||||
script="plugin.gd"
|
||||
43
addons/log/plugin.gd
Normal file
43
addons/log/plugin.gd
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
@tool
|
||||
extends EditorPlugin
|
||||
|
||||
|
||||
var override_log_level_option_button: OptionButton = OptionButton.new()
|
||||
|
||||
var icon_debug: Texture2D = EditorInterface.get_editor_theme().get_icon("Debug", "EditorIcons")
|
||||
var icon_info: Texture2D = EditorInterface.get_editor_theme().get_icon("NodeInfo", "EditorIcons")
|
||||
var icon_warn: Texture2D = EditorInterface.get_editor_theme().get_icon("NodeWarning", "EditorIcons")
|
||||
var icon_err: Texture2D = EditorInterface.get_editor_theme().get_icon("StatusError", "EditorIcons")
|
||||
|
||||
|
||||
func _enter_tree() -> void:
|
||||
override_log_level_option_button.visible = ProjectSettings.get_setting("log_gd/config/show_log_level_selector", false)
|
||||
override_log_level_option_button.add_icon_item(icon_debug, "DEBUG")
|
||||
override_log_level_option_button.add_icon_item(icon_info, "INFO")
|
||||
override_log_level_option_button.add_icon_item(icon_warn, "WARN")
|
||||
override_log_level_option_button.add_icon_item(icon_err, "ERROR")
|
||||
override_log_level_option_button.select(Log.get_log_level())
|
||||
override_log_level_option_button.item_selected.connect(override_log_level)
|
||||
add_control_to_container(CONTAINER_TOOLBAR, override_log_level_option_button)
|
||||
override_log_level_option_button.get_parent().move_child(override_log_level_option_button, override_log_level_option_button.get_index() - 2)
|
||||
|
||||
Log.setup_settings()
|
||||
Log.rebuild_config()
|
||||
# TODO only run if some log-specific setting has changed
|
||||
ProjectSettings.settings_changed.connect(on_settings_changed)
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
remove_control_from_container(CONTAINER_TOOLBAR, override_log_level_option_button)
|
||||
|
||||
|
||||
func on_settings_changed() -> void:
|
||||
override_log_level_option_button.select(ProjectSettings.get_setting("log_gd/config/log_level"))
|
||||
override_log_level_option_button.visible = ProjectSettings.get_setting("log_gd/config/show_log_level_selector")
|
||||
Log.rebuild_config()
|
||||
|
||||
|
||||
func override_log_level(value: Log.Levels) -> void:
|
||||
Log.set_log_level(value)
|
||||
ProjectSettings.set_setting("log_gd/config/log_level", value)
|
||||
ProjectSettings.save()
|
||||
1
addons/log/plugin.gd.uid
Normal file
1
addons/log/plugin.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://dc3akmw7hy6ok
|
||||
7
addons/reload_current_scene/plugin.cfg
Normal file
7
addons/reload_current_scene/plugin.cfg
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
[plugin]
|
||||
|
||||
name="Reload Current Scene"
|
||||
description="Helper button to reload the current scene."
|
||||
author="Russell Matney"
|
||||
version="v0.1.0"
|
||||
script="plugin.gd"
|
||||
25
addons/reload_current_scene/plugin.gd
Normal file
25
addons/reload_current_scene/plugin.gd
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
@tool
|
||||
extends EditorPlugin
|
||||
|
||||
|
||||
var reload_scene_btn: Button = Button.new()
|
||||
|
||||
|
||||
func _enter_tree() -> void:
|
||||
reload_scene_btn.pressed.connect(reload_scene)
|
||||
reload_scene_btn.text = "Reload Scene"
|
||||
reload_scene_btn.icon = EditorInterface.get_editor_theme().get_icon("Reload", "EditorIcons")
|
||||
add_control_to_container(CONTAINER_TOOLBAR, reload_scene_btn)
|
||||
reload_scene_btn.get_parent().move_child(reload_scene_btn, reload_scene_btn.get_index() - 2)
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
remove_control_from_container(CONTAINER_TOOLBAR, reload_scene_btn)
|
||||
|
||||
|
||||
func reload_scene() -> void:
|
||||
Log.info("[ReloadScene] Reload initialized", Time.get_time_string_from_system())
|
||||
var edited_scene: Node = EditorInterface.get_edited_scene_root()
|
||||
Log.info("[ReloadScene] Edited scene", "%s.scene_file_path" % edited_scene, edited_scene.scene_file_path)
|
||||
EditorInterface.reload_scene_from_path(edited_scene.scene_file_path)
|
||||
Log.info("[ReloadScene] Scene reloaded", Time.get_time_string_from_system())
|
||||
1
addons/reload_current_scene/plugin.gd.uid
Normal file
1
addons/reload_current_scene/plugin.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://df3sf44c3b64a
|
||||
Loading…
Add table
Add a link
Reference in a new issue