// // Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved // // 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. // #include #include #include #include #include "JSScene.h" #include "JSComponent.h" #include "JSVM.h" // Refactor so we don't need to include AtomicNET here #include #include #include namespace Atomic { /* /// Attribute type. VariantType type_; /// Name. String name_; /// Byte offset from start of object. unsigned offset_; /// Enum names. const char** enumNames_; /// Helper object for accessor mode. SharedPtr accessor_; /// Default value for network replication. Variant defaultValue_; /// Attribute mode: whether to use for serialization, network replication, or both. unsigned mode_; /// Attribute data pointer if elsewhere than in the Serializable. void* ptr_; */ static int Serializable_SetAttribute(duk_context* ctx) { const char* name = duk_to_string(ctx, 0); Variant v; js_to_variant(ctx, 1, v); duk_push_this(ctx); Serializable* serial = js_to_class_instance(ctx, -1, 0); const Vector* attributes = serial->GetAttributes(); VariantType variantType = VAR_NONE; bool isAttr = false; if (attributes) { for (unsigned i = 0; i < attributes->Size(); i++) { const AttributeInfo* attr = &attributes->At(i); if (!attr->name_.Compare(name)) { isAttr = true; variantType = attr->type_; break; } } } JSComponent* jsc = NULL; CSComponent* csc = NULL; // check dynamic if (!isAttr) { if (serial->GetType() == JSComponent::GetTypeStatic()) { jsc = (JSComponent*) serial; JSComponentFile* file = jsc->GetComponentFile(); if (file) { const HashMap& fields = file->GetFields(); const HashMap>& enums = file->GetEnums(); if (fields.Contains(name)) { HashMap::ConstIterator itr = fields.Find(name); variantType = itr->second_; if (enums.Contains(name)) { int idx = (int) v.GetFloat(); if (idx > 0 && idx < enums[name]->Size()) { VariantMap& values = jsc->GetFieldValues(); values[name] = enums[name]->At(idx).value_; return 0; } } } } } else if (serial->GetType() == CSComponent::GetTypeStatic()) { csc = (CSComponent*) serial; NETAssemblyFile* afile = csc->GetAssemblyFile(); if (afile) { NETComponentClass* componentClass = afile->GetComponentClass(csc->GetComponentClassName()); if (componentClass) { const HashMap& fields = componentClass->GetFields(); const HashMap>& enums = componentClass->GetEnums(); if (fields.Contains(name)) { HashMap::ConstIterator itr = fields.Find(name); variantType = itr->second_; if (enums.Contains(name)) { int idx = (int) v.GetFloat(); if (idx > 0 && idx < enums[name]->Size()) { VariantMap& values = csc->GetFieldValues(); values[name] = enums[name]->At(idx).value_; return 0; } } } } } } } if (variantType == VAR_NONE) return 0; if (variantType == VAR_QUATERNION) { Vector3 v3 = v.GetVector3(); Quaternion q; q.FromEulerAngles(v3.x_, v3.y_, v3.z_); v = q; } else if (variantType == VAR_COLOR) { Vector4 v4 = v.GetVector4(); Color c(v4.x_, v4.y_, v4.z_, v4.w_ ); v = c; } else if (variantType == VAR_INT) { v = (int) v.GetFloat(); } else if (variantType == VAR_RESOURCEREF) { RefCounted* ref = v.GetPtr(); if (ref && ref->IsObject()) { Object* o = (Object*) ref; // TODO: calling code must ensure we are a resource, can this be done here? Resource* resource = (Resource*) o; v = ResourceRef(resource->GetType(), resource->GetName()); } } if (isAttr) { serial->SetAttribute(name, v); return 0; } // check dynamic if (jsc) { VariantMap& values = jsc->GetFieldValues(); values[name] = v; } if (csc) { VariantMap& values = csc->GetFieldValues(); values[name] = v; } return 0; } static int Serializable_GetAttribute(duk_context* ctx) { const char* name = duk_to_string(ctx, 0); duk_push_this(ctx); Serializable* serial = js_to_class_instance(ctx, -1, 0); const Vector* attrs = serial->GetAttributes(); if (attrs) { for (unsigned i = 0; i < attrs->Size(); i++) { const AttributeInfo* attr = &attrs->At(i); if (!attr->name_.Compare(name)) { // FIXME: this is a double lookup js_push_variant(ctx, serial->GetAttribute(name)); return 1; } } } if (serial->GetType() == JSComponent::GetTypeStatic()) { JSComponent* jsc = (JSComponent*) serial; JSComponentFile* file = jsc->GetComponentFile(); if (file) { const HashMap& fields = file->GetFields(); if (fields.Contains(name)) { VariantMap& values = jsc->GetFieldValues(); if (values.Contains(name)) { js_push_variant(ctx, values[name]); return 1; } else { Variant v; file->GetDefaultFieldValue(name, v); js_push_variant(ctx, v); return 1; } } } } if (serial->GetType() == CSComponent::GetTypeStatic()) { CSComponent* csc = (CSComponent*) serial; NETAssemblyFile* afile = csc->GetAssemblyFile(); if (afile) { NETComponentClass* componentClass = afile->GetComponentClass(csc->GetComponentClassName()); if (componentClass) { const HashMap& fields = componentClass->GetFields(); if (fields.Contains(name)) { VariantMap& values = csc->GetFieldValues(); if (values.Contains(name)) { js_push_variant(ctx, values[name]); return 1; } else { Variant v; componentClass->GetDefaultFieldValue(name, v); js_push_variant(ctx, v); return 1; } } } } } duk_push_undefined(ctx); return 1; } static const String& GetResourceRefClassName(Context* context, const ResourceRef& ref) { const HashMap>& factories = context->GetObjectFactories(); HashMap>::ConstIterator itr = factories.Begin(); while (itr != factories.End()) { if (itr->first_ == ref.type_) { return itr->second_->GetTypeName(); } itr++; } return String::EMPTY; } static void GetDynamicAttributes(duk_context* ctx, unsigned& count, const VariantMap& defaultFieldValues, const HashMap& fields, const HashMap>& enums) { if (fields.Size()) { HashMap::ConstIterator itr = fields.Begin(); while (itr != fields.End()) { duk_push_object(ctx); duk_push_number(ctx, (double) itr->second_); duk_put_prop_string(ctx, -2, "type"); if (itr->second_ == VAR_RESOURCEREF && defaultFieldValues.Contains(itr->first_)) { if (defaultFieldValues[itr->first_]->GetType() == VAR_RESOURCEREF) { const ResourceRef& ref = defaultFieldValues[itr->first_]->GetResourceRef(); const String& typeName = GetResourceRefClassName(JSVM::GetJSVM(ctx)->GetContext(), ref); if (typeName.Length()) { duk_push_string(ctx, typeName.CString()); duk_put_prop_string(ctx, -2, "resourceTypeName"); } } } duk_push_string(ctx, itr->first_.CString()); duk_put_prop_string(ctx, -2, "name"); duk_push_number(ctx, (double) AM_DEFAULT); duk_put_prop_string(ctx, -2, "mode"); duk_push_string(ctx,""); duk_put_prop_string(ctx, -2, "defaultValue"); duk_push_boolean(ctx, 1); duk_put_prop_string(ctx, -2, "field"); duk_push_array(ctx); if (enums.Contains(itr->first_)) { unsigned enumCount = 0; const Vector* infos = enums[itr->first_]; Vector::ConstIterator eitr = infos->Begin(); while (eitr != infos->End()) { duk_push_string(ctx, eitr->name_.CString()); duk_put_prop_index(ctx, -2, enumCount++); eitr++; } } duk_put_prop_string(ctx, -2, "enumNames"); // store attr object duk_put_prop_index(ctx, -2, count++); itr++; } } } static int Serializable_GetAttributes(duk_context* ctx) { duk_push_this(ctx); Serializable* serial = js_to_class_instance(ctx, -1, 0); unsigned type = serial->GetType().Value(); duk_get_global_string(ctx, "__atomic_scene_serializable_attributes"); duk_get_prop_index(ctx, -1, type); // return cached array of attrinfo, unless JSComponent which has dynamic fields if (serial->GetType() != JSComponent::GetTypeStatic() && duk_is_object(ctx, -1)) return 1; const Vector* attrs = serial->GetAttributes(); duk_push_array(ctx); duk_dup(ctx, -1); duk_put_prop_index(ctx, -4, type); unsigned count = 0; if (attrs) { count = attrs->Size(); for (unsigned i = 0; i < attrs->Size(); i++) { const AttributeInfo* attr = &attrs->At(i); if (attr->mode_ & AM_NOEDIT) continue; duk_push_object(ctx); duk_push_number(ctx, (double) attr->type_); duk_put_prop_string(ctx, -2, "type"); if (attr->type_ == VAR_RESOURCEREF) { if (attr->defaultValue_.GetType() == VAR_RESOURCEREF) { const ResourceRef& ref = attr->defaultValue_.GetResourceRef(); const String& typeName = GetResourceRefClassName(serial->GetContext(), ref); if (typeName.Length()) { duk_push_string(ctx, typeName.CString()); duk_put_prop_string(ctx, -2, "resourceTypeName"); } } } duk_push_string(ctx, attr->name_.CString()); duk_put_prop_string(ctx, -2, "name"); duk_push_number(ctx, (double) attr->mode_); duk_put_prop_string(ctx, -2, "mode"); duk_push_string(ctx,attr->defaultValue_.ToString().CString()); duk_put_prop_string(ctx, -2, "defaultValue"); duk_push_boolean(ctx, 0); duk_put_prop_string(ctx, -2, "field"); duk_push_array(ctx); const char** enumPtr = attr->enumNames_; unsigned enumCount = 0; if (enumPtr) { while (*enumPtr) { duk_push_string(ctx, *enumPtr); duk_put_prop_index(ctx, -2, enumCount++); enumPtr++; } } duk_put_prop_string(ctx, -2, "enumNames"); // store attr object duk_put_prop_index(ctx, -2, i); } } // dynamic script fields if (serial->GetType() == JSComponent::GetTypeStatic()) { JSComponent* jsc = (JSComponent*) serial; JSComponentFile* file = jsc->GetComponentFile(); if (file) { const VariantMap& defaultFieldValues = file->GetDefaultFieldValues(); const HashMap& fields = file->GetFields(); const HashMap>& enums = file->GetEnums(); GetDynamicAttributes(ctx, count, defaultFieldValues, fields, enums); } } else if (serial->GetType() == CSComponent::GetTypeStatic()) { CSComponent* csc = (CSComponent*) serial; NETAssemblyFile* afile = csc->GetAssemblyFile(); if (afile) { NETComponentClass* componentClass = afile->GetComponentClass(csc->GetComponentClassName()); if (componentClass) { const VariantMap& defaultFieldValues = componentClass->GetDefaultFieldValues(); const HashMap& fields = componentClass->GetFields(); const HashMap>& enums = componentClass->GetEnums(); GetDynamicAttributes(ctx, count, defaultFieldValues, fields, enums); } } } return 1; } void jsapi_init_scene_serializable(JSVM* vm) { duk_context* ctx = vm->GetJSContext(); // cached attr duk_push_object(ctx); duk_put_global_string(ctx, "__atomic_scene_serializable_attributes"); js_class_get_prototype(ctx, "Atomic", "Serializable"); duk_push_c_function(ctx, Serializable_GetAttributes, 0); duk_put_prop_string(ctx, -2, "getAttributes"); duk_push_c_function(ctx, Serializable_GetAttribute, 1); duk_put_prop_string(ctx, -2, "getAttribute"); duk_push_c_function(ctx, Serializable_SetAttribute, 2); duk_put_prop_string(ctx, -2, "setAttribute"); duk_pop(ctx); } }