Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wiki: Add detail regarding passing objects via signals #57

Open
robase opened this issue Mar 8, 2025 · 3 comments
Open

Wiki: Add detail regarding passing objects via signals #57

robase opened this issue Mar 8, 2025 · 3 comments
Labels
documentation Improvements or additions to documentation good first issue Good for newcomers improvement

Comments

@robase
Copy link

robase commented Mar 8, 2025

Would be good to add a note explaining that raw ts/js objects cannot be passed via signals, only godot objects i.e. GArray, GDictionary (and primitives) can be passed as valid arguments. Might seem intuitive but turned out to be a gotcha for me when porting a c#/godot_mono project across, hopefully might be able to help other people

@ialex32x ialex32x added documentation Improvements or additions to documentation good first issue Good for newcomers improvement labels Mar 10, 2025
@ialex32x
Copy link
Collaborator

Indeed, it's very easy to make such mistakes when writing this kind of code, and unfortunately, most of these errors can't be detected by the ts compiler. Thanks for the suggestions. I'll improve the docs about it.

@robase
Copy link
Author

robase commented Mar 10, 2025

Should add that I'm very happy to edit the wiki myself, requires some repo perms to edit though I think. Here's some suggested content to copy paste in:

Passing Arguments via Signals

When emitting signals, only Godot-native objects (GArray, GDictionary, and primitives) can be passed as valid arguments. Raw TypeScript/JavaScript objects cannot be used directly.

Incorrect Example:

this.some_signal.emit({ key: "value" }); // ❌ Raw JS object

Correct Example:

import { GDictionary } from "godot";

const data = new GDictionary();
data.set("key", "value");
this.some_signal.emit(data); // ✅ Godot dictionary

If raw JavaScript objects must be passed, consider converting them into GDictionary or GArray before emitting them as arguments.

@robase
Copy link
Author

robase commented Mar 10, 2025

To others reading this, here's a (hacky, llm generated) converter from/to TS objects to Godot objects (not strongly tested, just a quick workaround to get things going)

import { GArray, GDictionary } from "godot";

/**
 * Recursively converts JavaScript objects and arrays to Godot-compatible GDictionary and GArray types
 * @param data Any JavaScript value to convert to a Godot-compatible type
 * @returns The Godot-compatible representation of the input data
 */
export function toGD(data: any) {
  // Handle null/undefined
  if (data === null || data === undefined) {
    return null;
  }
  
  // Handle primitive types (pass through)
  if (typeof data !== 'object') {
    return data;
  }
  
  // Handle arrays
  if (Array.isArray(data)) {
    const gArray = new GArray();
    for (const item of data) {
      gArray.push_back(toGD(item));
    }
    return gArray;
  }
  
  // Handle objects
  if (data instanceof GArray || data instanceof GDictionary) {
    // Already a Godot type, return as is
    return data;
  }
  
  // Convert object to GDictionary
  const gDict = new GDictionary();
  for (const key in data) {
    if (Object.prototype.hasOwnProperty.call(data, key)) {
      gDict.set_keyed(key, toGD(data[key]));
    }
  }
  return gDict;
}

/**
 * Recursively converts Godot GDictionary and GArray types to JavaScript objects and arrays
 * @param data Godot data structure to convert to a JavaScript equivalent
 * @returns The JavaScript representation of the input Godot data
 */
export function fromGD<T = any>(data: any): T {
  // Handle null/undefined
  if (data === null || data === undefined) {
    return null as T;
  }
  
  // Handle GArray
  if (data instanceof GArray) {
    const jsArray: any[] = [];
    for (let i = 0; i < data.size(); i++) {
      jsArray.push(fromGD(data.get_indexed(i)));
    }
    return jsArray as T;
  }
  
  // Handle GDictionary
  if (data instanceof GDictionary) {
    const jsObject: Record<string | number, any> = {};
    const keys = data.keys();
    
    for (let i = 0; i < keys.size(); i++) {
      const key = keys.get_indexed(i);
      jsObject[key] = fromGD(data.get_keyed(key));
    }
    
    return jsObject as T;
  }
  
  // Handle primitive types and anything else (pass through)
  return data as T;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation good first issue Good for newcomers improvement
Projects
None yet
Development

No branches or pull requests

2 participants