Skip to content

Commit e29d80a

Browse files
committed
recursive inst2dict() dict2inst() implemented for subclasses
Fix: godotengine#6533
1 parent 510406a commit e29d80a

File tree

2 files changed

+149
-130
lines changed

2 files changed

+149
-130
lines changed

modules/gdscript/gdscript_functions.cpp

+145-130
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,149 @@ const char *GDScriptFunctions::get_func_name(Function p_func) {
140140
return _names[p_func];
141141
}
142142

143+
Variant GDScriptFunctions::_inst2dict(const Variant &p_arg, Variant::CallError &r_error) {
144+
if (p_arg.get_type() == Variant::NIL) {
145+
return Variant();
146+
} else if (p_arg.get_type() != Variant::OBJECT) {
147+
r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
148+
r_error.argument = 0;
149+
return Variant();
150+
} else {
151+
152+
Object *obj = p_arg;
153+
if (!obj) {
154+
return Variant();
155+
156+
} else if (!obj->get_script_instance() || obj->get_script_instance()->get_language() != GDScriptLanguage::get_singleton()) {
157+
158+
r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
159+
r_error.argument = 0;
160+
r_error.expected = Variant::DICTIONARY;
161+
return RTR("Not a script with an instance");
162+
} else {
163+
164+
GDScriptInstance *ins = static_cast<GDScriptInstance *>(obj->get_script_instance());
165+
Ref<GDScript> base = ins->get_script();
166+
if (base.is_null()) {
167+
168+
r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
169+
r_error.argument = 0;
170+
r_error.expected = Variant::DICTIONARY;
171+
return RTR("Not based on a script");
172+
}
173+
174+
GDScript *p = base.ptr();
175+
Vector<StringName> sname;
176+
177+
while (p->_owner) {
178+
179+
sname.push_back(p->name);
180+
p = p->_owner;
181+
}
182+
sname.invert();
183+
184+
if (!p->path.is_resource_file()) {
185+
r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
186+
r_error.argument = 0;
187+
r_error.expected = Variant::DICTIONARY;
188+
return RTR("Not based on a resource file");
189+
}
190+
191+
NodePath cp(sname, Vector<StringName>(), false);
192+
193+
Dictionary d;
194+
d["@subpath"] = cp;
195+
d["@path"] = p->get_path();
196+
197+
for (Map<StringName, GDScript::MemberInfo>::Element *E = base->member_indices.front(); E; E = E->next()) {
198+
if (!d.has(E->key())) {
199+
if (ins->members[E->get().index].get_type() == Variant::OBJECT) {
200+
Variant::CallError err;
201+
err.error = Variant::CallError::CALL_OK;
202+
Dictionary sub_inst = _inst2dict(ins->members[E->get().index], err);
203+
if (err.error == Variant::CallError::CALL_OK)
204+
d[E->key()] = sub_inst;
205+
} else {
206+
d[E->key()] = ins->members[E->get().index];
207+
}
208+
}
209+
}
210+
return d;
211+
}
212+
}
213+
}
214+
215+
Variant GDScriptFunctions::_dict2inst(const Dictionary &dict, Variant::CallError &r_error) {
216+
217+
if (!dict.has("@path")) {
218+
219+
r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
220+
r_error.argument = 0;
221+
r_error.expected = Variant::OBJECT;
222+
return RTR("Invalid instance dictionary format (missing @path)");
223+
}
224+
225+
Ref<Script> scr = ResourceLoader::load(dict["@path"]);
226+
if (!scr.is_valid()) {
227+
228+
r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
229+
r_error.argument = 0;
230+
r_error.expected = Variant::OBJECT;
231+
return RTR("Invalid instance dictionary format (can't load script at @path)");
232+
}
233+
234+
Ref<GDScript> gdscr = scr;
235+
236+
if (!gdscr.is_valid()) {
237+
238+
r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
239+
r_error.argument = 0;
240+
r_error.expected = Variant::OBJECT;
241+
return RTR("Invalid instance dictionary format (invalid script at @path)");
242+
}
243+
244+
NodePath sub;
245+
if (dict.has("@subpath")) {
246+
sub = dict["@subpath"];
247+
}
248+
249+
for (int i = 0; i < sub.get_name_count(); i++) {
250+
251+
gdscr = gdscr->subclasses[sub.get_name(i)];
252+
if (!gdscr.is_valid()) {
253+
254+
r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
255+
r_error.argument = 0;
256+
r_error.expected = Variant::OBJECT;
257+
return RTR("Invalid instance dictionary (invalid subclasses)");
258+
}
259+
}
260+
261+
Variant ret = gdscr->_new(nullptr, 0, r_error);
262+
263+
GDScriptInstance *ins = static_cast<GDScriptInstance *>(static_cast<Object *>(ret)->get_script_instance());
264+
Ref<GDScript> gd_ref = ins->get_script();
265+
266+
for (Map<StringName, GDScript::MemberInfo>::Element *E = gd_ref->member_indices.front(); E; E = E->next()) {
267+
if (dict.has(E->key())) {
268+
if (dict[E->key()].get_type() == Variant::DICTIONARY && ins->members[E->get().index].get_type() != Variant::DICTIONARY) {
269+
const Dictionary &sub_dict = static_cast<const Dictionary>(dict[E->key()]);
270+
Variant::CallError err;
271+
err.error = Variant::CallError::CALL_OK;
272+
Variant sub_inst = _dict2inst(sub_dict, err);
273+
if (err.error == Variant::CallError::CALL_OK)
274+
ins->members.write[E->get().index] = sub_inst;
275+
else
276+
ins->members.write[E->get().index] = dict[E->key()];
277+
} else {
278+
ins->members.write[E->get().index] = dict[E->key()];
279+
}
280+
}
281+
}
282+
283+
return ret;
284+
}
285+
143286
void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_count, Variant &r_ret, Variant::CallError &r_error) {
144287

145288
r_error.error = Variant::CallError::CALL_OK;
@@ -1069,73 +1212,7 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
10691212

10701213
VALIDATE_ARG_COUNT(1);
10711214

1072-
if (p_args[0]->get_type() == Variant::NIL) {
1073-
r_ret = Variant();
1074-
} else if (p_args[0]->get_type() != Variant::OBJECT) {
1075-
r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
1076-
r_error.argument = 0;
1077-
r_ret = Variant();
1078-
} else {
1079-
1080-
Object *obj = *p_args[0];
1081-
if (!obj) {
1082-
r_ret = Variant();
1083-
1084-
} else if (!obj->get_script_instance() || obj->get_script_instance()->get_language() != GDScriptLanguage::get_singleton()) {
1085-
1086-
r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
1087-
r_error.argument = 0;
1088-
r_error.expected = Variant::DICTIONARY;
1089-
r_ret = RTR("Not a script with an instance");
1090-
return;
1091-
} else {
1092-
1093-
GDScriptInstance *ins = static_cast<GDScriptInstance *>(obj->get_script_instance());
1094-
Ref<GDScript> base = ins->get_script();
1095-
if (base.is_null()) {
1096-
1097-
r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
1098-
r_error.argument = 0;
1099-
r_error.expected = Variant::DICTIONARY;
1100-
r_ret = RTR("Not based on a script");
1101-
return;
1102-
}
1103-
1104-
GDScript *p = base.ptr();
1105-
Vector<StringName> sname;
1106-
1107-
while (p->_owner) {
1108-
1109-
sname.push_back(p->name);
1110-
p = p->_owner;
1111-
}
1112-
sname.invert();
1113-
1114-
if (!p->path.is_resource_file()) {
1115-
r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
1116-
r_error.argument = 0;
1117-
r_error.expected = Variant::DICTIONARY;
1118-
r_ret = Variant();
1119-
1120-
r_ret = RTR("Not based on a resource file");
1121-
1122-
return;
1123-
}
1124-
1125-
NodePath cp(sname, Vector<StringName>(), false);
1126-
1127-
Dictionary d;
1128-
d["@subpath"] = cp;
1129-
d["@path"] = p->get_path();
1130-
1131-
for (Map<StringName, GDScript::MemberInfo>::Element *E = base->member_indices.front(); E; E = E->next()) {
1132-
if (!d.has(E->key())) {
1133-
d[E->key()] = ins->members[E->get().index];
1134-
}
1135-
}
1136-
r_ret = d;
1137-
}
1138-
}
1215+
r_ret = _inst2dict(*p_args[0], r_error);
11391216

11401217
} break;
11411218
case DICT2INST: {
@@ -1152,69 +1229,7 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
11521229
return;
11531230
}
11541231

1155-
Dictionary d = *p_args[0];
1156-
1157-
if (!d.has("@path")) {
1158-
1159-
r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
1160-
r_error.argument = 0;
1161-
r_error.expected = Variant::OBJECT;
1162-
r_ret = RTR("Invalid instance dictionary format (missing @path)");
1163-
1164-
return;
1165-
}
1166-
1167-
Ref<Script> scr = ResourceLoader::load(d["@path"]);
1168-
if (!scr.is_valid()) {
1169-
1170-
r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
1171-
r_error.argument = 0;
1172-
r_error.expected = Variant::OBJECT;
1173-
r_ret = RTR("Invalid instance dictionary format (can't load script at @path)");
1174-
return;
1175-
}
1176-
1177-
Ref<GDScript> gdscr = scr;
1178-
1179-
if (!gdscr.is_valid()) {
1180-
1181-
r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
1182-
r_error.argument = 0;
1183-
r_error.expected = Variant::OBJECT;
1184-
r_ret = Variant();
1185-
r_ret = RTR("Invalid instance dictionary format (invalid script at @path)");
1186-
return;
1187-
}
1188-
1189-
NodePath sub;
1190-
if (d.has("@subpath")) {
1191-
sub = d["@subpath"];
1192-
}
1193-
1194-
for (int i = 0; i < sub.get_name_count(); i++) {
1195-
1196-
gdscr = gdscr->subclasses[sub.get_name(i)];
1197-
if (!gdscr.is_valid()) {
1198-
1199-
r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
1200-
r_error.argument = 0;
1201-
r_error.expected = Variant::OBJECT;
1202-
r_ret = Variant();
1203-
r_ret = RTR("Invalid instance dictionary (invalid subclasses)");
1204-
return;
1205-
}
1206-
}
1207-
1208-
r_ret = gdscr->_new(NULL, 0, r_error);
1209-
1210-
GDScriptInstance *ins = static_cast<GDScriptInstance *>(static_cast<Object *>(r_ret)->get_script_instance());
1211-
Ref<GDScript> gd_ref = ins->get_script();
1212-
1213-
for (Map<StringName, GDScript::MemberInfo>::Element *E = gd_ref->member_indices.front(); E; E = E->next()) {
1214-
if (d.has(E->key())) {
1215-
ins->members.write[E->get().index] = d[E->key()];
1216-
}
1217-
}
1232+
r_ret = _dict2inst(*p_args[0], r_error);
12181233

12191234
} break;
12201235
case VALIDATE_JSON: {

modules/gdscript/gdscript_functions.h

+4
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ class GDScriptFunctions {
133133
static void call(Function p_func, const Variant **p_args, int p_arg_count, Variant &r_ret, Variant::CallError &r_error);
134134
static bool is_deterministic(Function p_func);
135135
static MethodInfo get_info(Function p_func);
136+
137+
private:
138+
static Variant _inst2dict(const Variant &p_arg, Variant::CallError &r_error);
139+
static Variant _dict2inst(const Dictionary &p_arg, Variant::CallError &r_error);
136140
};
137141

138142
#endif // GDSCRIPT_FUNCTIONS_H

0 commit comments

Comments
 (0)