Thank you for contributing to the OpenClaw Godot Plugin!
- Ways to Contribute
- Development Environment Setup
- Code Contributions
- Pull Request Guidelines
- Issue Reporting
- Code Review Process
- Community Guidelines
| Area | Description | Difficulty |
|---|---|---|
| 🐛 Bug Fixes | Resolve issues, improve stability | ⭐ |
| 📝 Documentation | README, examples, translations | ⭐ |
| 🔧 New Tools | Add features to tools.gd | ⭐⭐ |
| ⚡ Performance | Optimization, memory management | ⭐⭐⭐ |
| 🏗️ Architecture | Core structural changes | ⭐⭐⭐ |
Check GitHub for good first issue label:
https://github.com/TomLeeLive/openclaw-godot-plugin/labels/good%20first%20issue
# 1. Fork on GitHub
# 2. Clone
git clone https://github.com/YOUR_USERNAME/openclaw-godot-plugin.git
cd openclaw-godot-plugin
# 3. Add upstream remote
git remote add upstream https://github.com/TomLeeLive/openclaw-godot-plugin.git# Create Godot test project
mkdir -p ~/godot-dev
cp -r addons ~/godot-dev/
# Install gateway extension
cp -r OpenClawPlugin~/* ~/.openclaw/extensions/godot/
openclaw gateway restart# Feature development
git checkout -b feature/audio-tools
# Bug fix
git checkout -b fix/connection-timeout
# Documentation
git checkout -b docs/korean-translation## Feature Request: animation.play tool
### Description
Tool to control AnimationPlayer nodes
### Use Cases
- AI tests character animations
- Preview cinematic sequences
### Proposed API
animation.play {path: "Player/AnimationPlayer", animation: "walk"}
animation.stop {path: "Player/AnimationPlayer"}
animation.list {path: "Player/AnimationPlayer"}addons/openclaw/tools.gd:
# Add to TOOLS array
var TOOLS = [
# ... existing tools ...
{
"name": "animation.play",
"description": "Play an animation on AnimationPlayer",
"inputSchema": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "Path to AnimationPlayer node"},
"animation": {"type": "string", "description": "Animation name to play"},
"speed": {"type": "number", "description": "Playback speed (default: 1.0)"}
},
"required": ["path", "animation"]
}
},
{
"name": "animation.stop",
"description": "Stop animation on AnimationPlayer",
"inputSchema": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "Path to AnimationPlayer node"}
},
"required": ["path"]
}
},
{
"name": "animation.list",
"description": "List available animations",
"inputSchema": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "Path to AnimationPlayer node"}
},
"required": ["path"]
}
}
]
# Add handlers to execute function
func execute(tool_name: String, args: Dictionary) -> Dictionary:
match tool_name:
# ... existing cases ...
"animation.play": return _animation_play(args)
"animation.stop": return _animation_stop(args)
"animation.list": return _animation_list(args)
# Implementation
func _animation_play(args: Dictionary) -> Dictionary:
var path = args.get("path", "")
var animation = args.get("animation", "")
var speed = args.get("speed", 1.0)
var node = _find_node_by_path(path)
if node == null:
return {"success": false, "error": "Node not found: " + path}
if not node is AnimationPlayer:
return {"success": false, "error": "Node is not AnimationPlayer: " + path}
var player = node as AnimationPlayer
if not player.has_animation(animation):
return {"success": false, "error": "Animation not found: " + animation}
player.speed_scale = speed
player.play(animation)
return {
"success": true,
"animation": animation,
"duration": player.get_animation(animation).length,
"speed": speed
}
func _animation_stop(args: Dictionary) -> Dictionary:
var path = args.get("path", "")
var node = _find_node_by_path(path)
if node == null:
return {"success": false, "error": "Node not found: " + path}
if not node is AnimationPlayer:
return {"success": false, "error": "Node is not AnimationPlayer"}
(node as AnimationPlayer).stop()
return {"success": true}
func _animation_list(args: Dictionary) -> Dictionary:
var path = args.get("path", "")
var node = _find_node_by_path(path)
if node == null:
return {"success": false, "error": "Node not found: " + path}
if not node is AnimationPlayer:
return {"success": false, "error": "Node is not AnimationPlayer"}
var player = node as AnimationPlayer
var animations = []
for anim_name in player.get_animation_list():
var anim = player.get_animation(anim_name)
animations.append({
"name": anim_name,
"duration": anim.length,
"loop": anim.loop_mode != Animation.LOOP_NONE
})
return {
"success": true,
"animations": animations,
"current": player.current_animation
}# Add to test_tools.gd
func _test_animation_tools():
_section("Animation Tools")
# Prepare test scene
tools.execute("scene.create", {"rootType": "Node2D", "name": "AnimTest"})
# Test animation.list
var list = tools.execute("animation.list", {
"path": "AnimationPlayer" # Use actual path
})
# _assert(list.success, "animation.list works")Add to README.md:
### Animation Tools (3) - NEW
| Tool | Description |
|------|-------------|
| `animation.play` | Play animation on AnimationPlayer |
| `animation.stop` | Stop current animation |
| `animation.list` | List available animations |## Bug: Connection timeout after 60 seconds of inactivity
### Steps to Reproduce
1. Enable plugin
2. Wait 60 seconds with no commands
3. Execute command - timeout occurs
### Expected Behavior
Connection should be maintained
### Environment
- Godot 4.6
- OpenClaw 2026.2.3connection_manager.gd:
# Problem: heartbeat interval too long
const HEARTBEAT_INTERVAL = 60.0 # 60 seconds (too long)
# Fix: reduce to 30 seconds
const HEARTBEAT_INTERVAL = 30.0 # 30 seconds
# Add: improved reconnection logic
func _on_heartbeat_timeout():
if not is_connected_flag:
# Attempt reconnection
_reconnect()
return
_send_heartbeat()
func _reconnect():
print("[OpenClaw] Attempting to reconnect...")
_register()# Wait 70 seconds then execute command
sleep 70
openclaw godot execute editor.getState
# Should succeed## Changes
<!-- Describe what was changed -->
Added 3 new tools for AnimationPlayer control
## Related Issue
Fixes #42
## Change Type
- [x] New feature
- [ ] Bug fix
- [ ] Documentation
- [ ] Refactoring
## Testing
- [x] Tested on Godot 4.6
- [x] Existing tools work correctly
- [x] New tools work correctly
## Checklist
- [x] Code style followed
- [x] Documentation updated
- [x] Tests added/updated
- [ ] No breaking changes
## Screenshots (if applicable)
<!-- Attach screenshots for UI changes --># Feature addition
feat(tools): add animation.play, animation.stop, animation.list tools
# Bug fix
fix(connection): reduce heartbeat interval to 30s to prevent timeout
# Documentation
docs(readme): add animation tools to feature list
# Refactoring
refactor(tools): extract common node finding logic
# Style/formatting
style(tools): apply consistent indentation<type>(<scope>): <subject>
[optional body]
[optional footer]
Types: feat, fix, docs, style, refactor, test, chore
## Bug Description
<!-- Clearly describe what went wrong -->
## Steps to Reproduce
1. Navigate to '...'
2. Click '...'
3. Scroll '...'
4. Error occurs
## Expected Behavior
<!-- Describe what should happen -->
## Actual Behavior
<!-- Describe what actually happens -->
## Environment
- OS: macOS 15.3
- Godot: 4.6-stable
- OpenClaw: 2026.2.3
- Plugin version: 1.1.0
## Logs
[OpenClaw] Error: ...
## Screenshots
<!-- Attach if applicable -->
## Feature Description
<!-- Describe the desired feature -->
## Use Cases
<!-- Why is this feature needed -->
## Proposed API
<!-- Suggest API format if possible -->
```gdscript
animation.play {path: "...", animation: "walk"}
---
## Code Review Process
### Reviewer Checklist
- [ ] Does the code work for its intended purpose?
- [ ] Are tests sufficient?
- [ ] Is documentation updated?
- [ ] Is code style consistent?
- [ ] Is error handling appropriate?
- [ ] Are there performance concerns?
- [ ] Are there security concerns?
### Review Comment Examples
```markdown
# Positive
✅ LGTM! Clean implementation.
# Suggestion
💡 Using `is_instance_of()` instead of `is` would be safer here.
# Request
⚠️ Null check needed for this case.
# Question
❓ What's the rationale for this constant value?
- 🤝 Communicate respectfully
- 🌍 Respect diverse backgrounds and experiences
- 📚 Provide constructive feedback
- 🚫 No discrimination or harassment
- GitHub Issues: Bugs, feature requests
- GitHub Discussions: Questions, ideas
- Discord: OpenClaw Community
- Issues: 1-3 days
- PRs: Within 1 week for initial review
- Critical bugs: ASAP
Contributed code is distributed under the MIT License.
Every contribution helps. Even small typo fixes are valuable!
If you have questions, please open an issue.
Documentation Updated: 2026-02-08