refactor: 拆分 claude-dev-stack 为 windows-dev-stack 和 wsl-dev-stack
将原 claude-dev-stack 目录拆分为独立的 Windows 和 WSL 部署栈,便于分别维护和使用。 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,239 @@
|
||||
@tool
|
||||
extends "res://addons/godot_mcp/commands/base_command.gd"
|
||||
|
||||
|
||||
func get_commands() -> Dictionary:
|
||||
return {
|
||||
"create_shader": _create_shader,
|
||||
"read_shader": _read_shader,
|
||||
"edit_shader": _edit_shader,
|
||||
"assign_shader_material": _assign_shader_material,
|
||||
"set_shader_param": _set_shader_param,
|
||||
"get_shader_params": _get_shader_params,
|
||||
}
|
||||
|
||||
|
||||
func _create_shader(params: Dictionary) -> Dictionary:
|
||||
var result := require_string(params, "path")
|
||||
if result[1] != null:
|
||||
return result[1]
|
||||
var path: String = result[0]
|
||||
|
||||
var content: String = optional_string(params, "content", "")
|
||||
var shader_type: String = optional_string(params, "shader_type", "spatial")
|
||||
var force: bool = optional_bool(params, "force", false)
|
||||
|
||||
var guard := guard_text_resource_write(path, force)
|
||||
if not guard.is_empty():
|
||||
return guard
|
||||
|
||||
if content.is_empty():
|
||||
match shader_type:
|
||||
"spatial":
|
||||
content = "shader_type spatial;\n\nvoid vertex() {\n\t// Called for every vertex\n}\n\nvoid fragment() {\n\t// Called for every pixel\n\tALBEDO = vec3(1.0);\n}\n"
|
||||
"canvas_item":
|
||||
content = "shader_type canvas_item;\n\nvoid vertex() {\n\t// Called for every vertex\n}\n\nvoid fragment() {\n\t// Called for every pixel\n\tCOLOR = vec4(1.0);\n}\n"
|
||||
"particles":
|
||||
content = "shader_type particles;\n\nvoid start() {\n\t// Called when particle spawns\n}\n\nvoid process() {\n\t// Called every frame per particle\n}\n"
|
||||
"sky":
|
||||
content = "shader_type sky;\n\nvoid sky() {\n\tCOLOR = vec3(0.3, 0.5, 0.8);\n}\n"
|
||||
|
||||
# Ensure directory exists
|
||||
var dir_path := path.get_base_dir()
|
||||
if not DirAccess.dir_exists_absolute(dir_path):
|
||||
DirAccess.make_dir_recursive_absolute(dir_path)
|
||||
|
||||
var file := FileAccess.open(path, FileAccess.WRITE)
|
||||
if file == null:
|
||||
return error_internal("Cannot create shader: %s" % error_string(FileAccess.get_open_error()))
|
||||
|
||||
file.store_string(content)
|
||||
file.close()
|
||||
|
||||
_refresh_loaded_shader(path, content)
|
||||
|
||||
return success({"path": path, "shader_type": shader_type, "created": true})
|
||||
|
||||
|
||||
func _read_shader(params: Dictionary) -> Dictionary:
|
||||
var result := require_string(params, "path")
|
||||
if result[1] != null:
|
||||
return result[1]
|
||||
var path: String = result[0]
|
||||
|
||||
if not FileAccess.file_exists(path):
|
||||
return error_not_found("Shader '%s'" % path)
|
||||
|
||||
var file := FileAccess.open(path, FileAccess.READ)
|
||||
if file == null:
|
||||
return error_internal("Cannot read shader: %s" % error_string(FileAccess.get_open_error()))
|
||||
|
||||
var content := file.get_as_text()
|
||||
file.close()
|
||||
|
||||
return success({"path": path, "content": content, "size": content.length()})
|
||||
|
||||
|
||||
func _refresh_loaded_shader(path: String, content: String) -> void:
|
||||
var normalized := normalize_project_path(path)
|
||||
if normalized.is_empty():
|
||||
return
|
||||
if ResourceLoader.has_cached(normalized):
|
||||
var shader := Shader.new()
|
||||
shader.code = content
|
||||
shader.take_over_path(normalized)
|
||||
shader.emit_changed()
|
||||
EditorInterface.get_resource_filesystem().update_file(normalized)
|
||||
|
||||
|
||||
func _edit_shader(params: Dictionary) -> Dictionary:
|
||||
var result := require_string(params, "path")
|
||||
if result[1] != null:
|
||||
return result[1]
|
||||
var path: String = result[0]
|
||||
|
||||
if not FileAccess.file_exists(path):
|
||||
return error_not_found("Shader '%s'" % path)
|
||||
|
||||
var force: bool = optional_bool(params, "force", false)
|
||||
var guard := guard_text_resource_write(path, force)
|
||||
if not guard.is_empty():
|
||||
return guard
|
||||
|
||||
var changes_made := 0
|
||||
var content := ""
|
||||
|
||||
if params.has("content"):
|
||||
content = str(params["content"])
|
||||
changes_made = 1
|
||||
elif params.has("replacements") and params["replacements"] is Array:
|
||||
# Read current
|
||||
var file := FileAccess.open(path, FileAccess.READ)
|
||||
if file == null:
|
||||
return error_internal("Cannot read shader")
|
||||
content = file.get_as_text()
|
||||
file.close()
|
||||
|
||||
for replacement in params["replacements"]:
|
||||
if replacement is Dictionary:
|
||||
var search: String = replacement.get("search", "")
|
||||
var replace: String = replacement.get("replace", "")
|
||||
if not search.is_empty() and content.contains(search):
|
||||
content = content.replace(search, replace)
|
||||
changes_made += 1
|
||||
|
||||
if changes_made > 0:
|
||||
var file := FileAccess.open(path, FileAccess.WRITE)
|
||||
if file == null:
|
||||
return error_internal("Cannot write shader: %s" % error_string(FileAccess.get_open_error()))
|
||||
file.store_string(content)
|
||||
file.close()
|
||||
_refresh_loaded_shader(path, content)
|
||||
|
||||
return success({"path": path, "changes_made": changes_made})
|
||||
|
||||
|
||||
func _assign_shader_material(params: Dictionary) -> Dictionary:
|
||||
var result := require_string(params, "node_path")
|
||||
if result[1] != null:
|
||||
return result[1]
|
||||
var node_path: String = result[0]
|
||||
|
||||
var result2 := require_string(params, "shader_path")
|
||||
if result2[1] != null:
|
||||
return result2[1]
|
||||
var shader_path: String = result2[0]
|
||||
|
||||
var node := find_node_by_path(node_path)
|
||||
if node == null:
|
||||
return error_not_found("Node at '%s'" % node_path)
|
||||
|
||||
if not ResourceLoader.exists(shader_path):
|
||||
return error_not_found("Shader '%s'" % shader_path)
|
||||
|
||||
var shader: Shader = load(shader_path)
|
||||
if shader == null:
|
||||
return error_internal("Failed to load shader")
|
||||
|
||||
var material := ShaderMaterial.new()
|
||||
material.shader = shader
|
||||
|
||||
if node is CanvasItem:
|
||||
set_property_with_undo(node, "material", material, "MCP: Assign shader material")
|
||||
elif node is MeshInstance3D:
|
||||
set_property_with_undo(node, "material_override", material, "MCP: Assign shader material")
|
||||
else:
|
||||
# Try generic material property
|
||||
if "material" in node:
|
||||
set_property_with_undo(node, "material", material, "MCP: Assign shader material")
|
||||
else:
|
||||
return error_invalid_params("Node '%s' (%s) does not support materials" % [node_path, node.get_class()])
|
||||
|
||||
return success({"node_path": node_path, "shader_path": shader_path, "assigned": true})
|
||||
|
||||
|
||||
func _set_shader_param(params: Dictionary) -> Dictionary:
|
||||
var result := require_string(params, "node_path")
|
||||
if result[1] != null:
|
||||
return result[1]
|
||||
var node_path: String = result[0]
|
||||
|
||||
var result2 := require_string(params, "param")
|
||||
if result2[1] != null:
|
||||
return result2[1]
|
||||
var param_name: String = result2[0]
|
||||
|
||||
var node := find_node_by_path(node_path)
|
||||
if node == null:
|
||||
return error_not_found("Node at '%s'" % node_path)
|
||||
|
||||
var material: ShaderMaterial = null
|
||||
if node is CanvasItem and (node as CanvasItem).material is ShaderMaterial:
|
||||
material = (node as CanvasItem).material
|
||||
elif node is MeshInstance3D and (node as MeshInstance3D).material_override is ShaderMaterial:
|
||||
material = (node as MeshInstance3D).material_override
|
||||
|
||||
if material == null:
|
||||
return error(-32000, "Node has no ShaderMaterial")
|
||||
|
||||
var value = params.get("value")
|
||||
if value is String:
|
||||
var s: String = value
|
||||
var expr := Expression.new()
|
||||
if expr.parse(s) == OK:
|
||||
var parsed = expr.execute()
|
||||
if parsed != null:
|
||||
value = parsed
|
||||
|
||||
material.set_shader_parameter(param_name, value)
|
||||
|
||||
return success({"node_path": node_path, "param": param_name, "value": str(value)})
|
||||
|
||||
|
||||
func _get_shader_params(params: Dictionary) -> Dictionary:
|
||||
var result := require_string(params, "node_path")
|
||||
if result[1] != null:
|
||||
return result[1]
|
||||
var node_path: String = result[0]
|
||||
|
||||
var node := find_node_by_path(node_path)
|
||||
if node == null:
|
||||
return error_not_found("Node at '%s'" % node_path)
|
||||
|
||||
var material: ShaderMaterial = null
|
||||
if node is CanvasItem and (node as CanvasItem).material is ShaderMaterial:
|
||||
material = (node as CanvasItem).material
|
||||
elif node is MeshInstance3D and (node as MeshInstance3D).material_override is ShaderMaterial:
|
||||
material = (node as MeshInstance3D).material_override
|
||||
|
||||
if material == null:
|
||||
return error(-32000, "Node has no ShaderMaterial")
|
||||
|
||||
var shader_params: Dictionary = {}
|
||||
for prop in material.get_property_list():
|
||||
var pname: String = prop["name"]
|
||||
if pname.begins_with("shader_parameter/"):
|
||||
var key := pname.substr(17)
|
||||
shader_params[key] = str(material.get(pname))
|
||||
|
||||
return success({"node_path": node_path, "params": shader_params})
|
||||
Reference in New Issue
Block a user