Integrate GDevelop.js (emscripten bindings of Core/Extensions to JS) inside the repository

This commit is contained in:
Florian Rival
2019-04-23 16:09:15 +01:00
committed by Florian Rival
parent ccc29ef416
commit 1693d863d3
28 changed files with 15014 additions and 21 deletions
+5 -9
View File
@@ -6,17 +6,13 @@
-DMACOS
-DRELEASE
-D_FILE_OFFSET_BITS=64
-DwxDEBUG_LEVEL=0
-D_WCHAR_H_CPLUSPLUS_98_CONFORMANCE_
-Wno-potentially-evaluated-expression
-std=gnu++11
-I/usr/local/Cellar/wxmac/3.0.2/include/wx-3.0
-DNDEBUG
-fPIC
-I/Users/florian/Projects/F/GD/ExtLibs/SFML/include
-I/Users/florian/Projects/F/GD/Core
-I/usr/local/Cellar/wxmac/3.0.2/lib/wx/include/osx_cocoa-unicode-3.0
-I/usr/local/Cellar/wxmac/3.0.2/include/wx-3.0
-I/Users/florian/Projects/F/GD/GDCpp/.
-I/Users/florian/Projects/F/GD/GDJS/.
-F/Users/florian/Projects/F/GD/ExtLibs/SFML/extlibs/libs-osx/Frameworks
-I./ExtLibs/SFML/include
-I./Core
-I./GDCpp/.
-I./GDJS/.
-F./ExtLibs/SFML/extlibs/libs-osx/Frameworks
-2
View File
@@ -18,7 +18,6 @@
/Binaries/Releases/**/*.tar.lzma
/Binaries/Releases/**/*.zip
/Binaries/Releases/**/*.deb
/GDevelop.js
*.depend
*.layout
*.xgdwe
@@ -37,7 +36,6 @@
/Binaries/Output/Release_Linux/**
/Binaries/Output/Debug_Darwin/**
/Binaries/Output/Release_Darwin/**
/Binaries/Output/Frameworks/
!/Binaries/Output/Release_Linux/StartGDevelop.sh
!/Binaries/Output/Release_Linux/CppPlatform/
/Binaries/**/MinGW32
+35 -10
View File
@@ -17,15 +17,20 @@ env:
- GCC_VERSION="4.8"
addons:
artifacts:
s3_region: "us-east-1"
paths:
- Binaries/Output/libGD.js/Release
apt:
sources:
- ubuntu-toolchain-r-test
packages:
#Build dependencies:
- cmake
# Build dependencies:
# (Cmake 3+ is required by Emscripten)
- cmake3
- p7zip-full
- g++-4.8
#SFML dependencies:
# SFML dependencies:
- libopenal-dev
- libjpeg-dev
- libglew-dev
@@ -35,11 +40,14 @@ addons:
- libglu1-mesa-dev
- libfreetype6-dev
#Activate X Virtual Framebuffer to allow tests to
#use SFML.
before_install:
#Activate X Virtual Framebuffer to allow tests to
#use SFML.
- "export DISPLAY=:99.0"
- "sh -e /etc/init.d/xvfb start"
# This workaround is required to avoid libstdc++ errors (Emscripten requires a recent version of libstdc++)
- wget -q -O libstdc++6 http://security.ubuntu.com/ubuntu/pool/main/g/gcc-5/libstdc++6_5.4.0-6ubuntu1~16.04.10_amd64.deb
- sudo dpkg --force-all -i libstdc++6
install:
#Install newIDE tests dependencies
@@ -50,28 +58,45 @@ install:
- cd ../..
#Get the correct version of gcc/g++
- if [ "$CXX" = "g++" ]; then export CXX="g++-${GCC_VERSION}" CC="gcc-${GCC_VERSION}"; fi
#Compile the tests only
#Compile the tests only for GDCore and GDCpp
- mkdir .build-tests
- cd .build-tests
- cmake -DBUILD_GDJS=FALSE -DBUILD_TESTS=TRUE -DCMAKE_CXX_COMPILER=$(which $CXX) -DCMAKE_C_COMPILER=$(which $CC) ..
- make -j 4
- cd ..
# Install Emscripten (for GDevelop.js)
- git clone https://github.com/juj/emsdk.git
- cd emsdk
- ./emsdk install sdk-1.37.37-64bit
- ./emsdk activate sdk-1.37.37-64bit
- source ./emsdk_env.sh
- cd ..
# Install GDevelop.js dependencies
- cd GDevelop.js
- npm install -g grunt-cli
- npm install
- npm run build
- cd ..
script:
#GDCore and GDCpp game engine tests:
# GDCore and GDCpp game engine tests:
- cd .build-tests
- Core/GDCore_tests
- GDCpp/GDCpp_tests
- Extensions/PathfindingBehavior/PathfindingBehavior_Runtime_tests
- Extensions/LinkedObjects/LinkedObjects_Runtime_tests
- cd ..
#newIDE tests:
# GDevelop.js tests
- cd GDevelop.js
- npm test
- cd ..
# newIDE tests:
- cd newIDE/app
- npm test
- npm run flow
- npm run check-format
- cd ../..
#GDJS game engine tests, disabled on Travis CI because ChromeHeadless can't be started.
#See them running on Semaphore-CI instead: https://semaphoreci.com/4ian/gd
# GDJS game engine tests, disabled on Travis CI because ChromeHeadless can't be started.
# See them running on Semaphore-CI instead: https://semaphoreci.com/4ian/gd
# - cd GDJS/tests && npm test
# - cd ../..
+11
View File
@@ -0,0 +1,11 @@
/node_modules
Bindings/parser.out
Bindings/WebIDLGrammar.pkl
Bindings/glue.cpp
Bindings/glue.js
parser.out
WebIDLGrammar.pkl
.cpp.old
examples/demo-generated-code.js
examples/demo-generated-game.json
**/.DS_Store
+4
View File
@@ -0,0 +1,4 @@
{
"singleQuote": true,
"trailingComma": "es5"
}
@@ -0,0 +1,94 @@
#include "BehaviorJsImplementation.h"
#include <GDCore/IDE/Dialogs/PropertyDescriptor.h>
#include <GDCore/Project/Behavior.h>
#include <GDCore/Project/Project.h>
#include <GDCore/Serialization/Serializer.h>
#include <GDCore/Serialization/SerializerElement.h>
#include <emscripten.h>
#include <map>
using namespace gd;
BehaviorJsImplementation* BehaviorJsImplementation::Clone() const {
BehaviorJsImplementation* clone = new BehaviorJsImplementation(*this);
// Copy the references to the JS implementations of the functions (because we
// want an object cloned from C++ to retain the functions implemented in JS).
EM_ASM_INT(
{
var clone =
Module['wrapPointer']($0, Module['BehaviorJsImplementation']);
var self =
Module['wrapPointer']($1, Module['BehaviorJsImplementation']);
clone['getProperties'] = self['getProperties'];
clone['updateProperty'] = self['updateProperty'];
},
(int)clone,
(int)this);
return clone;
}
std::map<gd::String, gd::PropertyDescriptor>
BehaviorJsImplementation::GetProperties(gd::Project&) const {
std::map<gd::String, gd::PropertyDescriptor>* jsCreatedProperties = nullptr;
std::map<gd::String, gd::PropertyDescriptor> copiedProperties;
jsCreatedProperties = (std::map<gd::String, gd::PropertyDescriptor>*)EM_ASM_INT(
{
var self = Module['getCache'](Module['BehaviorJsImplementation'])[$0];
if (!self.hasOwnProperty('getProperties'))
throw 'getProperties is not defined on a BehaviorJsImplementation.';
var objectContent = JSON.parse(Pointer_stringify($1));
var newProperties = self['getProperties'](objectContent);
if (!newProperties)
throw 'getProperties returned nothing in a gd::BehaviorJsImplementation.';
return getPointer(newProperties);
},
(int)this,
jsonContent.c_str());
copiedProperties = *jsCreatedProperties;
delete jsCreatedProperties;
return copiedProperties;
}
bool BehaviorJsImplementation::UpdateProperty(const gd::String& arg0,
const gd::String& arg1,
Project&) {
jsonContent = (const char*)EM_ASM_INT(
{
var self = Module['getCache'](Module['BehaviorJsImplementation'])[$0];
if (!self.hasOwnProperty('updateProperty'))
throw 'updateProperty is not defined on a BehaviorJsImplementation.';
var objectContent = JSON.parse(Pointer_stringify($1));
self['updateProperty'](
objectContent, Pointer_stringify($2), Pointer_stringify($3));
return ensureString(JSON.stringify(objectContent));
},
(int)this,
jsonContent.c_str(),
arg0.c_str(),
arg1.c_str());
return true;
}
void BehaviorJsImplementation::SerializeTo(SerializerElement& arg0) const {
arg0.AddChild("content") = gd::Serializer::FromJSON(jsonContent);
}
void BehaviorJsImplementation::UnserializeFrom(const SerializerElement& arg1) {
jsonContent = gd::Serializer::ToJSON(arg1.GetChild("content"));
}
void BehaviorJsImplementation::__destroy__() { // Useless?
EM_ASM_INT(
{
var self = Module['getCache'](Module['BehaviorJsImplementation'])[$0];
if (!self.hasOwnProperty('__destroy__'))
throw 'a JSImplementation must implement all functions, you forgot BehaviorJsImplementation::__destroy__.';
self['__destroy__']();
},
(int)this);
}
@@ -0,0 +1,38 @@
#include <GDCore/IDE/Dialogs/PropertyDescriptor.h>
#include <GDCore/Project/Behavior.h>
#include <GDCore/Project/Project.h>
#include <GDCore/Serialization/Serializer.h>
#include <GDCore/Serialization/SerializerElement.h>
#include <emscripten.h>
using namespace gd;
/**
* \brief A gd::Behavior that stores its content in JSON and forward the properties related
* functions to Javascript with Emscripten.
*/
class BehaviorJsImplementation : public Behavior {
public:
BehaviorJsImplementation() : Behavior(), jsonContent("{}") {}
virtual BehaviorJsImplementation* Clone() const override;
virtual std::map<gd::String, gd::PropertyDescriptor> GetProperties(
gd::Project& project) const override;
virtual bool UpdateProperty(const gd::String& name,
const gd::String& value,
gd::Project& project) override;
void __destroy__();
virtual void SerializeTo(SerializerElement& arg0) const override;
virtual void UnserializeFrom(const SerializerElement& arg1) override;
const gd::String& GetRawJSONContent() const { return jsonContent; };
BehaviorJsImplementation& SetRawJSONContent(const gd::String& newContent) {
jsonContent = newContent;
return *this;
};
private:
gd::String jsonContent;
};
@@ -0,0 +1,101 @@
#include "BehaviorSharedDataJsImplementation.h"
#include <GDCore/IDE/Dialogs/PropertyDescriptor.h>
#include <GDCore/Project/Behavior.h>
#include <GDCore/Project/Project.h>
#include <GDCore/Serialization/Serializer.h>
#include <GDCore/Serialization/SerializerElement.h>
#include <emscripten.h>
#include <map>
using namespace gd;
std::shared_ptr<gd::BehaviorsSharedData>
BehaviorSharedDataJsImplementation::Clone() const {
BehaviorSharedDataJsImplementation* clone =
new BehaviorSharedDataJsImplementation(*this);
// Copy the references to the JS implementations of the functions (because we
// want an object cloned from C++ to retain the functions implemented in JS).
EM_ASM_INT(
{
var clone = Module['wrapPointer'](
$0, Module['BehaviorSharedDataJsImplementation']);
var self = Module['wrapPointer'](
$1, Module['BehaviorSharedDataJsImplementation']);
clone['getProperties'] = self['getProperties'];
clone['updateProperty'] = self['updateProperty'];
},
(int)clone,
(int)this);
return std::shared_ptr<gd::BehaviorsSharedData>(clone);
}
std::map<gd::String, gd::PropertyDescriptor>
BehaviorSharedDataJsImplementation::GetProperties(gd::Project&) const {
std::map<gd::String, gd::PropertyDescriptor>* jsCreatedProperties = nullptr;
std::map<gd::String, gd::PropertyDescriptor> copiedProperties;
jsCreatedProperties = (std::map<gd::String, gd::PropertyDescriptor>*)EM_ASM_INT(
{
var self = Module['getCache'](
Module['BehaviorSharedDataJsImplementation'])[$0];
if (!self.hasOwnProperty('getProperties'))
throw 'getProperties is not defined on a BehaviorSharedDataJsImplementation.';
var objectContent = JSON.parse(Pointer_stringify($1));
var newProperties = self['getProperties'](objectContent);
if (!newProperties)
throw 'getProperties returned nothing in a gd::BehaviorSharedDataJsImplementation.';
return getPointer(newProperties);
},
(int)this,
jsonContent.c_str());
copiedProperties = *jsCreatedProperties;
delete jsCreatedProperties;
return copiedProperties;
}
bool BehaviorSharedDataJsImplementation::UpdateProperty(const gd::String& arg0,
const gd::String& arg1,
Project&) {
jsonContent = (const char*)EM_ASM_INT(
{
var self = Module['getCache'](
Module['BehaviorSharedDataJsImplementation'])[$0];
if (!self.hasOwnProperty('updateProperty'))
throw 'updateProperty is not defined on a BehaviorSharedDataJsImplementation.';
var objectContent = JSON.parse(Pointer_stringify($1));
self['updateProperty'](
objectContent, Pointer_stringify($2), Pointer_stringify($3));
return ensureString(JSON.stringify(objectContent));
},
(int)this,
jsonContent.c_str(),
arg0.c_str(),
arg1.c_str());
return true;
}
void BehaviorSharedDataJsImplementation::SerializeTo(
SerializerElement& arg0) const {
arg0.AddChild("content") = gd::Serializer::FromJSON(jsonContent);
}
void BehaviorSharedDataJsImplementation::UnserializeFrom(
const SerializerElement& arg1) {
jsonContent = gd::Serializer::ToJSON(arg1.GetChild("content"));
}
void BehaviorSharedDataJsImplementation::__destroy__() { // Useless?
EM_ASM_INT(
{
var self = Module['getCache'](
Module['BehaviorSharedDataJsImplementation'])[$0];
if (!self.hasOwnProperty('__destroy__'))
throw 'a JSImplementation must implement all functions, you forgot BehaviorSharedDataJsImplementation::__destroy__.';
self['__destroy__']();
},
(int)this);
}
@@ -0,0 +1,38 @@
#include <GDCore/IDE/Dialogs/PropertyDescriptor.h>
#include <GDCore/Project/BehaviorsSharedData.h>
#include <GDCore/Project/Project.h>
#include <GDCore/Serialization/Serializer.h>
#include <GDCore/Serialization/SerializerElement.h>
#include <emscripten.h>
using namespace gd;
/**
* \brief A gd::BehaviorsSharedData that stores its content in JSON and forward the properties related
* functions to Javascript with Emscripten.
*/
class BehaviorSharedDataJsImplementation : public BehaviorsSharedData {
public:
BehaviorSharedDataJsImplementation() : BehaviorsSharedData(), jsonContent("{}") {}
virtual std::shared_ptr<gd::BehaviorsSharedData> Clone() const override;
virtual std::map<gd::String, gd::PropertyDescriptor> GetProperties(
gd::Project& project) const override;
virtual bool UpdateProperty(const gd::String& name,
const gd::String& value,
gd::Project& project) override;
void __destroy__();
virtual void SerializeTo(SerializerElement& arg0) const override;
virtual void UnserializeFrom(const SerializerElement& arg1) override;
const gd::String& GetRawJSONContent() const { return jsonContent; };
BehaviorSharedDataJsImplementation& SetRawJSONContent(const gd::String& newContent) {
jsonContent = newContent;
return *this;
};
private:
gd::String jsonContent;
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,162 @@
#include "ObjectJsImplementation.h"
#include <GDCore/IDE/Dialogs/PropertyDescriptor.h>
#include <GDCore/Project/Object.h>
#include <GDCore/Project/Project.h>
#include <GDCore/Serialization/Serializer.h>
#include <GDCore/Serialization/SerializerElement.h>
#include <emscripten.h>
#include <map>
using namespace gd;
std::unique_ptr<gd::Object> ObjectJsImplementation::Clone() const {
ObjectJsImplementation* clone = new ObjectJsImplementation(*this);
// Copy the references to the JS implementations of the functions (because we
// want an object cloned from C++ to retain the functions implemented in JS).
EM_ASM_INT(
{
var clone = Module['wrapPointer']($0, Module['ObjectJsImplementation']);
var self = Module['wrapPointer']($1, Module['ObjectJsImplementation']);
clone['getProperties'] = self['getProperties'];
clone['updateProperty'] = self['updateProperty'];
clone['getInitialInstanceProperties'] =
self['getInitialInstanceProperties'];
clone['updateInitialInstanceProperty'] =
self['updateInitialInstanceProperty'];
},
(int)clone,
(int)this);
return std::unique_ptr<gd::Object>(clone);
}
std::map<gd::String, gd::PropertyDescriptor>
ObjectJsImplementation::GetProperties(gd::Project&) const {
std::map<gd::String, gd::PropertyDescriptor>* jsCreatedProperties = nullptr;
std::map<gd::String, gd::PropertyDescriptor> copiedProperties;
jsCreatedProperties = (std::map<gd::String, gd::PropertyDescriptor>*)EM_ASM_INT(
{
var self = Module['getCache'](Module['ObjectJsImplementation'])[$0];
if (!self.hasOwnProperty('getProperties'))
throw 'getProperties is not defined on a ObjectJsImplementation.';
var objectContent = JSON.parse(Pointer_stringify($1));
var newProperties = self['getProperties'](objectContent);
if (!newProperties)
throw 'getProperties returned nothing in a gd::ObjectJsImplementation.';
return getPointer(newProperties);
},
(int)this,
jsonContent.c_str());
copiedProperties = *jsCreatedProperties;
delete jsCreatedProperties;
return copiedProperties;
}
bool ObjectJsImplementation::UpdateProperty(const gd::String& arg0,
const gd::String& arg1,
Project&) {
jsonContent = (const char*)EM_ASM_INT(
{
var self = Module['getCache'](Module['ObjectJsImplementation'])[$0];
if (!self.hasOwnProperty('updateProperty'))
throw 'updateProperty is not defined on a ObjectJsImplementation.';
var objectContent = JSON.parse(Pointer_stringify($1));
self['updateProperty'](
objectContent, Pointer_stringify($2), Pointer_stringify($3));
return ensureString(JSON.stringify(objectContent));
},
(int)this,
jsonContent.c_str(),
arg0.c_str(),
arg1.c_str());
return true;
}
std::map<gd::String, gd::PropertyDescriptor>
ObjectJsImplementation::GetInitialInstanceProperties(
const gd::InitialInstance& instance,
gd::Project& project,
gd::Layout& scene) {
std::map<gd::String, gd::PropertyDescriptor>* jsCreatedProperties = nullptr;
std::map<gd::String, gd::PropertyDescriptor> copiedProperties;
jsCreatedProperties = (std::map<gd::String, gd::PropertyDescriptor>*)EM_ASM_INT(
{
var self = Module['getCache'](Module['ObjectJsImplementation'])[$0];
if (!self.hasOwnProperty('getInitialInstanceProperties'))
throw 'getInitialInstanceProperties is not defined on a ObjectJsImplementation.';
var objectContent = JSON.parse(Pointer_stringify($1));
var newProperties = self['getInitialInstanceProperties'](
objectContent,
wrapPointer($2, Module['InitialInstance']),
wrapPointer($3, Module['Project']),
wrapPointer($4, Module['Layout']));
if (!newProperties)
throw 'getInitialInstanceProperties returned nothing in a gd::ObjectJsImplementation.';
return getPointer(newProperties);
},
(int)this,
jsonContent.c_str(),
(int)&instance,
(int)&project,
(int)&scene);
copiedProperties = *jsCreatedProperties;
delete jsCreatedProperties;
return copiedProperties;
}
bool ObjectJsImplementation::UpdateInitialInstanceProperty(
gd::InitialInstance& instance,
const gd::String& name,
const gd::String& value,
gd::Project& project,
gd::Layout& scene) {
return EM_ASM_INT(
{
var self = Module['getCache'](Module['ObjectJsImplementation'])[$0];
if (!self.hasOwnProperty('updateInitialInstanceProperty'))
throw 'updateInitialInstanceProperty is not defined on a ObjectJsImplementation.';
var objectContent = JSON.parse(Pointer_stringify($1));
return self['updateInitialInstanceProperty'](
objectContent,
wrapPointer($2, Module['InitialInstance']),
Pointer_stringify($3),
Pointer_stringify($4),
wrapPointer($5, Module['Project']),
wrapPointer($6, Module['Layout']));
},
(int)this,
jsonContent.c_str(),
(int)&instance,
name.c_str(),
value.c_str(),
(int)&project,
(int)&scene);
}
void ObjectJsImplementation::DoSerializeTo(SerializerElement& arg0) const {
arg0.AddChild("content") = gd::Serializer::FromJSON(jsonContent);
}
void ObjectJsImplementation::DoUnserializeFrom(Project& arg0,
const SerializerElement& arg1) {
jsonContent = gd::Serializer::ToJSON(arg1.GetChild("content"));
}
void ObjectJsImplementation::__destroy__() { // Useless?
EM_ASM_INT(
{
var self = Module['getCache'](Module['ObjectJsImplementation'])[$0];
if (!self.hasOwnProperty('__destroy__'))
throw 'a JSImplementation must implement all functions, you forgot ObjectJsImplementation::__destroy__.';
self['__destroy__']();
},
(int)this);
}
@@ -0,0 +1,48 @@
#include <GDCore/IDE/Dialogs/PropertyDescriptor.h>
#include <GDCore/Project/Object.h>
#include <GDCore/Project/Project.h>
#include <GDCore/Serialization/Serializer.h>
#include <GDCore/Serialization/SerializerElement.h>
#include <emscripten.h>
using namespace gd;
/**
* \brief A gd::Object that stores its content in JSON and forward the
* properties related functions to Javascript with Emscripten.
*/
class ObjectJsImplementation : public gd::Object {
public:
ObjectJsImplementation(gd::String name_) : Object(name_), jsonContent("{}") {}
virtual std::unique_ptr<gd::Object> Clone() const override;
virtual std::map<gd::String, gd::PropertyDescriptor> GetProperties(
gd::Project& project) const override;
virtual bool UpdateProperty(const gd::String& name,
const gd::String& value,
gd::Project& project) override;
std::map<gd::String, gd::PropertyDescriptor> GetInitialInstanceProperties(
const gd::InitialInstance& instance,
gd::Project& project,
gd::Layout& scene) override;
bool UpdateInitialInstanceProperty(gd::InitialInstance& instance,
const gd::String& name,
const gd::String& value,
gd::Project& project,
gd::Layout& scene) override;
void __destroy__();
const gd::String& GetRawJSONContent() const { return jsonContent; };
ObjectJsImplementation& SetRawJSONContent(const gd::String& newContent) {
jsonContent = newContent;
return *this;
};
protected:
virtual void DoSerializeTo(SerializerElement& arg0) const override;
virtual void DoUnserializeFrom(Project& arg0,
const SerializerElement& arg1) override;
gd::String jsonContent;
};
+215
View File
@@ -0,0 +1,215 @@
#include "GDCore/Extensions/Platform.h"
#include "GDCore/IDE/PlatformManager.h"
#include "GDCore/Project/InitialInstancesContainer.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/TinyXml/tinyxml.h"
#include "GDCore/Tools/VersionWrapper.h"
#include "GDJS/Extensions/JsPlatform.h"
using namespace gdjs;
using namespace gd;
/**
* \brief Helper functions related to projects and initialization.
*/
class ProjectHelper {
public:
static gd::Project& CreateNewGDJSProject() {
Project* project = new Project;
project->AddPlatform(JsPlatform::Get());
return *project;
}
/**
* \brief Initialize the JS platform.
*/
static void InitializePlatforms() {
static bool initialized = false;
if (!initialized) {
std::cout << "libGD.js based on GDevelop " << VersionWrapper::FullString()
<< std::endl;
} else {
std::cout << "ERROR: You're calling initializePlatforms again, but "
"initialization was already done!"
<< std::endl;
return;
}
initialized = true;
std::cout << "Initializing GDJS platform..." << std::endl;
std::shared_ptr<gd::Platform> platform(&JsPlatform::Get());
gd::PlatformManager::Get()->AddPlatform(platform);
std::cout << "Platform initialization ended." << std::endl;
}
/**
* \brief This check that the given gd::Behavior can be properly cloned
* and have the given property updated.
*/
static gd::String SanityCheckBehaviorProperty(gd::Behavior* behavior,
const gd::String& propertyName,
const gd::String& newValue) {
if (!behavior) return "FAIL: behavior passed is null";
gd::Project project;
project.AddPlatform(JsPlatform::Get());
gd::String originalValue =
behavior->GetProperties(project)[propertyName].GetValue();
gd::Behavior* copiedBehavior = behavior->Clone();
if (copiedBehavior->GetProperties(project)[propertyName].GetValue() !=
originalValue) {
return "FAIL: Cloning the behavior does not copy properly the property";
}
behavior->UpdateProperty(propertyName, newValue, project);
gd::String updatedValue =
behavior->GetProperties(project)[propertyName].GetValue();
if (updatedValue != newValue) {
return "FAIL: expected the newValue to be set for the property, but "
"received:" +
updatedValue;
}
gd::String copiedBehaviorValue =
copiedBehavior->GetProperties(project)[propertyName].GetValue();
if (copiedBehaviorValue != originalValue) {
return "FAIL: Updating the property of the behavior will change the "
"property of the cloned behavior. Clone behavior property is "
"now: " +
copiedBehaviorValue + ". Should have been:" + originalValue;
}
delete copiedBehavior;
return "";
}
/**
* \brief This check that the given gd::BehaviorsSharedData can be properly cloned
* and have the given property updated.
*/
static gd::String SanityCheckBehaviorsSharedDataProperty(gd::BehaviorsSharedData* sharedData,
const gd::String& propertyName,
const gd::String& newValue) {
if (!sharedData) return "FAIL: sharedData passed is null";
gd::Project project;
project.AddPlatform(JsPlatform::Get());
gd::String originalValue =
sharedData->GetProperties(project)[propertyName].GetValue();
std::shared_ptr<gd::BehaviorsSharedData> copiedSharedData = sharedData->Clone();
if (copiedSharedData->GetProperties(project)[propertyName].GetValue() !=
originalValue) {
return "FAIL: Cloning the sharedData does not copy properly the property";
}
sharedData->UpdateProperty(propertyName, newValue, project);
gd::String updatedValue =
sharedData->GetProperties(project)[propertyName].GetValue();
if (updatedValue != newValue) {
return "FAIL: expected the newValue to be set for the property, but "
"received:" +
updatedValue;
}
gd::String copiedSharedDataValue =
copiedSharedData->GetProperties(project)[propertyName].GetValue();
if (copiedSharedDataValue != originalValue) {
return "FAIL: Updating the property of the sharedData will change the "
"property of the cloned sharedData. Clone sharedData property is "
"now: " +
copiedSharedDataValue + ". Should have been:" + originalValue;
}
return "";
}
/**
* \brief This check that the given gd::Object can be properly cloned
* and have the given property updated.
*/
static gd::String SanityCheckObjectProperty(gd::Object* object,
const gd::String& propertyName,
const gd::String& newValue) {
if (!object) return "FAIL: object passed is null";
gd::Project project;
project.AddPlatform(JsPlatform::Get());
gd::String originalValue =
object->GetProperties(project)[propertyName].GetValue();
std::unique_ptr<gd::Object> copiedObject = object->Clone();
if (copiedObject->GetProperties(project)[propertyName].GetValue() !=
originalValue) {
return "FAIL: Cloning the object does not copy properly the property";
}
object->UpdateProperty(propertyName, newValue, project);
gd::String updatedValue =
object->GetProperties(project)[propertyName].GetValue();
if (updatedValue != newValue) {
return "FAIL: expected the newValue to be set for the property, but "
"received:" +
updatedValue;
}
gd::String copiedObjectValue =
copiedObject->GetProperties(project)[propertyName].GetValue();
if (copiedObjectValue != originalValue) {
return "FAIL: Updating the property of the object will change the "
"property of the cloned object. Clone object property is "
"now: " +
copiedObjectValue + ". Should have been:" + originalValue;
}
return "";
}
/**
* \brief This check that the given gd::Object can be properly cloned
* and return/set the properties of a gd::InitialInstance.
*/
static gd::String SanityCheckObjectInitialInstanceProperty(
gd::Object* object,
const gd::String& propertyName,
const gd::String& newValue) {
if (!object) return "FAIL: object passed is null";
gd::Project project;
project.AddPlatform(JsPlatform::Get());
gd::Layout layout;
gd::InitialInstance instance;
gd::String originalValue = object
->GetInitialInstanceProperties(
instance, project, layout)[propertyName]
.GetValue();
std::unique_ptr<gd::Object> copiedObject = object->Clone();
if (copiedObject
->GetInitialInstanceProperties(
instance, project, layout)[propertyName]
.GetValue() != originalValue) {
return "FAIL: Cloned object does not return the same initial value for "
"the instance property";
}
copiedObject->UpdateInitialInstanceProperty(
instance, propertyName, newValue, project, layout);
gd::String updatedValue = copiedObject
->GetInitialInstanceProperties(
instance, project, layout)[propertyName]
.GetValue();
if (updatedValue != newValue) {
return "FAIL: expected the newValue to be set for the instance property "
"using the copied object, but "
"received:" +
updatedValue;
}
return "";
}
};
+560
View File
@@ -0,0 +1,560 @@
#include <map>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include <GDCore/Extensions/Platform.h>
#include <GDCore/Project/Behavior.h>
#include <GDCore/Project/ExternalEvents.h>
#include <GDCore/Project/ExternalLayout.h>
#include <GDCore/Project/InitialInstance.h>
#include <GDCore/Project/InitialInstancesContainer.h>
#include <GDCore/Project/Layout.h>
#include <GDCore/Project/Object.h>
#include <GDCore/Project/Project.h>
#include <GDCore/Project/Variable.h>
#include <GDCore/Project/VariablesContainer.h>
#include <GDCore/Serialization/Serializer.h>
#include <GDCore/Serialization/SerializerElement.h>
#include <GDCore/Events/Parsers/ExpressionParser.h>
#include <GDCore/Events/Parsers/ExpressionParser2.h>
#include <GDCore/Events/Parsers/ExpressionParser2Node.h>
#include <GDCore/IDE/Events/ExpressionValidator.h>
#include <GDCore/Events/CodeGeneration/ExpressionCodeGenerator.h>
#include <GDCore/Extensions/Metadata/MetadataProvider.h>
#include <GDCore/Extensions/Metadata/ParameterMetadataTools.h>
#include <GDCore/Project/EventsFunction.h>
#include <GDCore/Project/EventsFunctionsExtension.h>
#include <GDCore/IDE/AbstractFileSystem.h>
#include <GDCore/IDE/EventsFunctionTools.h>
#include <GDCore/IDE/Dialogs/LayoutEditorCanvas/LayoutEditorCanvasOptions.h>
#include <GDCore/IDE/Dialogs/PropertyDescriptor.h>
#include <GDCore/IDE/Events/ArbitraryEventsWorker.h>
#include <GDCore/IDE/Events/InstructionsTypeRenamer.h>
#include <GDCore/IDE/Events/EventsContextAnalyzer.h>
#include <GDCore/IDE/Events/EventsParametersLister.h>
#include <GDCore/IDE/Events/EventsTypesLister.h>
#include <GDCore/IDE/Events/EventsRefactorer.h>
#include <GDCore/IDE/Events/EventsRemover.h>
#include <GDCore/IDE/Events/ExpressionsCorrectnessTesting.h>
#include <GDCore/IDE/Events/InstructionSentenceFormatter.h>
#include <GDCore/IDE/Events/TextFormatting.h>
#include <GDCore/IDE/Events/EventsListUnfolder.h>
#include <GDCore/IDE/Project/ArbitraryResourceWorker.h>
#include <GDCore/IDE/Project/ResourcesMergingHelper.h>
#include <GDCore/IDE/Project/ResourcesInUseHelper.h>
#include <GDCore/IDE/Project/ProjectResourcesAdder.h>
#include <GDCore/IDE/WholeProjectRefactorer.h>
#include <GDCore/Events/Builtin/CommentEvent.h>
#include <GDCore/Events/Builtin/ForEachEvent.h>
#include <GDCore/Events/Builtin/GroupEvent.h>
#include <GDCore/Events/Builtin/LinkEvent.h>
#include <GDCore/Events/Builtin/RepeatEvent.h>
#include <GDCore/Events/Builtin/StandardEvent.h>
#include <GDCore/Events/Builtin/WhileEvent.h>
#include <GDCore/Extensions/Builtin/SpriteExtension/Animation.h>
#include <GDCore/Extensions/Builtin/SpriteExtension/Direction.h>
#include <GDCore/Extensions/Builtin/SpriteExtension/Sprite.h>
#include <GDCore/Extensions/Builtin/SpriteExtension/SpriteObject.h>
#include "../../Extensions/PanelSpriteObject/PanelSpriteObject.h"
#include "../../Extensions/ParticleSystem/ParticleEmitterObject.h"
#include "../../Extensions/PrimitiveDrawing/ShapePainterObject.h"
#include "../../Extensions/TextEntryObject/TextEntryObject.h"
#include "../../Extensions/TextObject/TextObject.h"
#include "../../Extensions/TiledSpriteObject/TiledSpriteObject.h"
#include <GDJS/Events/Builtin/JsCodeEvent.h>
#include <GDJS/Events/CodeGeneration/EventsCodeGenerator.h>
#include <GDJS/IDE/Exporter.h>
#include <emscripten.h>
#include "ProjectHelper.h"
#include "BehaviorJsImplementation.h"
#include "BehaviorSharedDataJsImplementation.h"
#include "ObjectJsImplementation.h"
/**
* \brief Manual binding of gd::ArbitraryResourceWorker to allow overriding
* methods that are using std::string
*/
class ArbitraryResourceWorkerJS : public ArbitraryResourceWorker {
public:
void ExposeImage(gd::String &arg0) {
arg0 = (const char *)EM_ASM_INT(
{
var self =
Module['getCache'](Module['ArbitraryResourceWorkerJS'])[$0];
if (!self.hasOwnProperty('exposeImage'))
throw 'a JSImplementation must implement all functions, you forgot ArbitraryResourceWorkerJS::exposeImage.';
return ensureString(self.exposeImage(Pointer_stringify($1)));
},
(int)this,
arg0.c_str());
}
void ExposeShader(gd::String &arg0) {
arg0 = (const char *)EM_ASM_INT(
{
var self =
Module['getCache'](Module['ArbitraryResourceWorkerJS'])[$0];
if (!self.hasOwnProperty('exposeShader'))
throw 'a JSImplementation must implement all functions, you forgot ArbitraryResourceWorkerJS::exposeShader.';
return ensureString(self.exposeShader(Pointer_stringify($1)));
},
(int)this,
arg0.c_str());
}
void ExposeFile(gd::String &arg0) {
arg0 = (const char *)EM_ASM_INT(
{
var self =
Module['getCache'](Module['ArbitraryResourceWorkerJS'])[$0];
if (!self.hasOwnProperty('exposeFile'))
throw 'a JSImplementation must implement all functions, you forgot ArbitraryResourceWorkerJS::exposeFile.';
return ensureString(self.exposeFile(Pointer_stringify($1)));
},
(int)this,
arg0.c_str());
}
};
/**
* \brief Manual binding of gd::AbstractFileSystem to allow overriding methods
* that are using std::string
*/
class AbstractFileSystemJS : public AbstractFileSystem {
public:
virtual void MkDir(const gd::String &path) {
EM_ASM_INT(
{
var self = Module['getCache'](Module['AbstractFileSystemJS'])[$0];
if (!self.hasOwnProperty('mkDir'))
throw 'a JSImplementation must implement all functions, you forgot AbstractFileSystemJS::mkDir.';
self.mkDir(Pointer_stringify($1));
},
(int)this,
path.c_str());
}
virtual bool DirExists(const gd::String &path) {
return EM_ASM_INT(
{
var self = Module['getCache'](Module['AbstractFileSystemJS'])[$0];
if (!self.hasOwnProperty('dirExists'))
throw 'a JSImplementation must implement all functions, you forgot AbstractFileSystemJS::dirExists.';
return self.dirExists(Pointer_stringify($1));
},
(int)this,
path.c_str());
}
virtual bool FileExists(const gd::String &path) {
return EM_ASM_INT(
{
var self = Module['getCache'](Module['AbstractFileSystemJS'])[$0];
if (!self.hasOwnProperty('fileExists'))
throw 'a JSImplementation must implement all functions, you forgot AbstractFileSystemJS::fileExists.';
return self.fileExists(Pointer_stringify($1));
},
(int)this,
path.c_str());
}
virtual gd::String FileNameFrom(const gd::String &file) {
return (const char *)EM_ASM_INT(
{
var self = Module['getCache'](Module['AbstractFileSystemJS'])[$0];
if (!self.hasOwnProperty('fileNameFrom'))
throw 'a JSImplementation must implement all functions, you forgot AbstractFileSystemJS::fileNameFrom.';
return ensureString(self.fileNameFrom(Pointer_stringify($1)));
},
(int)this,
file.c_str());
}
virtual gd::String DirNameFrom(const gd::String &file) {
return (const char *)EM_ASM_INT(
{
var self = Module['getCache'](Module['AbstractFileSystemJS'])[$0];
if (!self.hasOwnProperty('dirNameFrom'))
throw 'a JSImplementation must implement all functions, you forgot AbstractFileSystemJS::dirNameFrom.';
return ensureString(self.dirNameFrom(Pointer_stringify($1)));
},
(int)this,
file.c_str());
}
virtual bool MakeAbsolute(gd::String &filename,
const gd::String &baseDirectory) {
filename = (const char *)EM_ASM_INT(
{
var self = Module['getCache'](Module['AbstractFileSystemJS'])[$0];
if (!self.hasOwnProperty('makeAbsolute'))
throw 'a JSImplementation must implement all functions, you forgot AbstractFileSystemJS::makeAbsolute.';
return ensureString(
self.makeAbsolute(Pointer_stringify($1), Pointer_stringify($2)));
},
(int)this,
filename.c_str(),
baseDirectory.c_str());
return true;
}
virtual bool MakeRelative(gd::String &filename,
const gd::String &baseDirectory) {
filename = (const char *)EM_ASM_INT(
{
var self = Module['getCache'](Module['AbstractFileSystemJS'])[$0];
if (!self.hasOwnProperty('makeRelative'))
throw 'a JSImplementation must implement all functions, you forgot AbstractFileSystemJS::makeRelative.';
return ensureString(
self.makeRelative(Pointer_stringify($1), Pointer_stringify($2)));
},
(int)this,
filename.c_str(),
baseDirectory.c_str());
return true;
}
virtual bool IsAbsolute(const gd::String &filename) {
return (bool)EM_ASM_INT(
{
var self = Module['getCache'](Module['AbstractFileSystemJS'])[$0];
if (!self.hasOwnProperty('isAbsolute'))
throw 'a JSImplementation must implement all functions, you forgot AbstractFileSystemJS::isAbsolute.';
return self.isAbsolute(Pointer_stringify($1));
},
(int)this,
filename.c_str());
};
virtual bool CopyFile(const gd::String &file, const gd::String &destination) {
return (bool)EM_ASM_INT(
{
var self = Module['getCache'](Module['AbstractFileSystemJS'])[$0];
if (!self.hasOwnProperty('copyFile'))
throw 'a JSImplementation must implement all functions, you forgot AbstractFileSystemJS::copyFile.';
return self.copyFile(Pointer_stringify($1), Pointer_stringify($2));
},
(int)this,
file.c_str(),
destination.c_str());
}
virtual bool ClearDir(const gd::String &directory) {
return (bool)EM_ASM_INT(
{
var self = Module['getCache'](Module['AbstractFileSystemJS'])[$0];
if (!self.hasOwnProperty('clearDir'))
throw 'a JSImplementation must implement all functions, you forgot AbstractFileSystemJS::clearDir.';
return self.clearDir(Pointer_stringify($1));
},
(int)this,
directory.c_str());
}
virtual bool CopyDir(const gd::String &source,
const gd::String &destination) {
return (bool)EM_ASM_INT(
{
var self = Module['getCache'](Module['AbstractFileSystemJS'])[$0];
if (!self.hasOwnProperty('copyDir'))
throw 'a JSImplementation must implement all functions, you forgot AbstractFileSystemJS::copyDir.';
return self.copyDir(Pointer_stringify($1), Pointer_stringify($2));
},
(int)this,
source.c_str(),
destination.c_str());
}
virtual bool WriteToFile(const gd::String &file, const gd::String &content) {
return (bool)EM_ASM_INT(
{
var self = Module['getCache'](Module['AbstractFileSystemJS'])[$0];
if (!self.hasOwnProperty('writeToFile'))
throw 'a JSImplementation must implement all functions, you forgot AbstractFileSystemJS::writeToFile.';
return self.writeToFile(Pointer_stringify($1), Pointer_stringify($2));
},
(int)this,
file.c_str(),
content.c_str());
}
virtual gd::String ReadFile(const gd::String &file) {
return (const char *)EM_ASM_INT(
{
var self = Module['getCache'](Module['AbstractFileSystemJS'])[$0];
if (!self.hasOwnProperty('readFile'))
throw 'a JSImplementation must implement all functions, you forgot AbstractFileSystemJS::readFile.';
return ensureString(self.readFile(Pointer_stringify($1)));
},
(int)this,
file.c_str());
}
virtual gd::String GetTempDir() {
return (const char *)EM_ASM_INT(
{
var self = Module['getCache'](Module['AbstractFileSystemJS'])[$0];
if (!self.hasOwnProperty('getTempDir'))
throw 'a JSImplementation must implement all functions, you forgot AbstractFileSystemJS::getTempDir.';
return ensureString(self.getTempDir());
},
(int)this);
}
virtual std::vector<gd::String> ReadDir(const gd::String &path,
const gd::String &extension = "") {
std::vector<gd::String> directories = *(std::vector<gd::String> *)EM_ASM_INT(
{
var self = Module['getCache'](Module['AbstractFileSystemJS'])[$0];
if (!self.hasOwnProperty('readDir'))
throw 'a JSImplementation must implement all functions, you forgot AbstractFileSystemJS::readDir.';
return self.readDir(Pointer_stringify($1), Pointer_stringify($2)).ptr;
},
(int)this,
path.c_str(),
extension.c_str());
return directories;
}
AbstractFileSystemJS(){};
virtual ~AbstractFileSystemJS(){};
};
class InitialInstanceJSFunctorWrapper : public gd::InitialInstanceFunctor {
public:
InitialInstanceJSFunctorWrapper(){};
virtual void operator()(gd::InitialInstance &instance) { invoke(&instance); };
virtual void invoke(gd::InitialInstance *instance){};
};
// Implement some std::vector<*> erase methods as free functions as there is no
// easy way to properly expose the erase method :'(
void removeFromVectorPolygon2d(std::vector<Polygon2d> &vec, size_t pos) {
vec.erase(vec.begin() + pos);
}
void removeFromVectorVector2f(std::vector<sf::Vector2f> &vec, size_t pos) {
vec.erase(vec.begin() + pos);
}
void removeFromVectorParameterMetadata(std::vector<gd::ParameterMetadata> &vec, size_t pos) {
vec.erase(vec.begin() + pos);
}
// Implement a conversion from std::set<gd::String> to std::vector<gd::String>
// as there is no easy way to properly expose iterators :/
std::vector<gd::String> toNewVectorString(const std::set<gd::String> &set) {
std::vector<gd::String> output(set.begin(), set.end());
return output;
}
// Declares typedef for std::vector and templatized types
typedef std::vector<gd::String> VectorString;
typedef std::vector<std::shared_ptr<gd::PlatformExtension>>
VectorPlatformExtension;
typedef std::pair<gd::String, gd::Variable> PairStringVariable;
typedef std::pair<gd::String, TextFormatting> PairStringTextFormatting;
typedef std::vector<std::pair<gd::String, TextFormatting>>
VectorPairStringTextFormatting;
typedef std::vector<gd::ObjectGroup> VectorObjectGroup;
typedef std::map<gd::String, gd::String> MapStringString;
typedef std::map<gd::String, bool> MapStringBoolean;
typedef std::map<gd::String, gd::ExpressionMetadata>
MapStringExpressionMetadata;
typedef std::map<gd::String, gd::InstructionMetadata>
MapStringInstructionMetadata;
typedef std::map<gd::String, gd::EventMetadata> MapStringEventMetadata;
typedef std::map<gd::String, gd::Variable> MapStringVariable;
typedef std::map<gd::String, gd::PropertyDescriptor>
MapStringPropertyDescriptor;
typedef std::set<gd::String> SetString;
typedef std::vector<Point> VectorPoint;
typedef std::vector<Polygon2d> VectorPolygon2d;
typedef std::vector<sf::Vector2f> VectorVector2f;
typedef std::vector<EventsSearchResult> VectorEventsSearchResult;
typedef std::vector<gd::ParameterMetadata> VectorParameterMetadata;
typedef std::vector<gd::EventsFunction> VectorEventsFunction;
typedef gd::Object gdObject; // To avoid clashing javascript Object in glue.js
typedef ParticleEmitterObject::RendererType ParticleEmitterObject_RendererType;
typedef EventsFunction::FunctionType EventsFunction_FunctionType;
typedef std::unique_ptr<gd::Object> UniquePtrObject;
typedef std::unique_ptr<ExpressionNode> UniquePtrExpressionNode;
typedef std::vector<gd::ExpressionParserDiagnostic*> VectorExpressionParserDiagnostic;
typedef ExtensionAndMetadata<BehaviorMetadata> ExtensionAndBehaviorMetadata;
typedef ExtensionAndMetadata<ObjectMetadata> ExtensionAndObjectMetadata;
typedef ExtensionAndMetadata<InstructionMetadata> ExtensionAndInstructionMetadata;
typedef ExtensionAndMetadata<InstructionMetadata> ExtensionAndInstructionMetadata;
typedef ExtensionAndMetadata<ExpressionMetadata> ExtensionAndExpressionMetadata;
typedef ExtensionAndMetadata<ExpressionMetadata> ExtensionAndExpressionMetadata;
typedef ExtensionAndMetadata<ExpressionMetadata> ExtensionAndExpressionMetadata;
typedef ExtensionAndMetadata<ExpressionMetadata> ExtensionAndExpressionMetadata;
typedef ExtensionAndMetadata<ExpressionMetadata> ExtensionAndExpressionMetadata;
typedef ExtensionAndMetadata<ExpressionMetadata> ExtensionAndExpressionMetadata;
// Customize some functions implementation thanks to WRAPPED_* macros
// The original names will be reconstructed in the js file (see postjs.js)
#define WRAPPED_set(a, b) at(a) = b
#define WRAPPED_GetString(i) at(i).first
#define WRAPPED_GetComment() com1
#define WRAPPED_SetComment(str) com1 = str
#define WRAPPED_GetTextFormatting(i) at(i).second
#define WRAPPED_GetName() first
#define WRAPPED_GetVariable() second
#define WRAPPED_SetBool(v) SetValue(v)
#define WRAPPED_SetString(v) SetValue(gd::String(v))
#define WRAPPED_SetInt(v) SetValue(v)
#define WRAPPED_SetDouble(v) SetValue(v)
#define WRAPPED_SetChild(name, child) GetChild(name) = child
// Wrappers to avoid dealing with shared_ptr in the methods interface:
#define WRAPPED_AddBehavior(name, \
fullname, \
defaultName, \
description, \
group, \
icon24x24, \
className, \
instance, \
sharedDatasInstance) \
AddBehavior(name, \
fullname, \
defaultName, \
description, \
group, \
icon24x24, \
className, \
std::shared_ptr<gd::Behavior>(instance), \
std::shared_ptr<gd::BehaviorsSharedData>(sharedDatasInstance))
#define WRAPPED_AddObject(name, fullname, description, icon24x24, instance) \
AddObject(name, \
fullname, \
description, \
icon24x24, \
std::shared_ptr<gd::Object>(instance))
#define WRAPPED_Get() Get().get()
#define WRAPPED_GetSharedDataInstance() GetSharedDataInstance().get()
#define WRAPPED_at(a) at(a).get()
#define MAP_get(a) find(a)->second
#define MAP_set(key, value) [key] = value
#define MAP_has(key) find(key) != self->end()
#define STATIC_CreateNewGDJSProject CreateNewGDJSProject
#define STATIC_InitializePlatforms InitializePlatforms
#define STATIC_ValidateObjectName ValidateObjectName
#define STATIC_ToJSON ToJSON
#define STATIC_FromJSON(x) FromJSON(gd::String(x))
#define STATIC_IsObject IsObject
#define STATIC_IsBehavior IsBehavior
#define STATIC_Get Get
#define STATIC_AddAllMissing AddAllMissing
#define STATIC_GetAllUseless GetAllUseless
#define STATIC_RemoveAllUseless RemoveAllUseless
#define STATIC_GetExtensionAndBehaviorMetadata GetExtensionAndBehaviorMetadata
#define STATIC_GetExtensionAndObjectMetadata GetExtensionAndObjectMetadata
#define STATIC_GetExtensionAndActionMetadata GetExtensionAndActionMetadata
#define STATIC_GetExtensionAndConditionMetadata GetExtensionAndConditionMetadata
#define STATIC_GetExtensionAndExpressionMetadata GetExtensionAndExpressionMetadata
#define STATIC_GetExtensionAndObjectExpressionMetadata GetExtensionAndObjectExpressionMetadata
#define STATIC_GetExtensionAndBehaviorExpressionMetadata GetExtensionAndBehaviorExpressionMetadata
#define STATIC_GetExtensionAndStrExpressionMetadata GetExtensionAndStrExpressionMetadata
#define STATIC_GetExtensionAndObjectStrExpressionMetadata GetExtensionAndObjectStrExpressionMetadata
#define STATIC_GetExtensionAndBehaviorStrExpressionMetadata GetExtensionAndBehaviorStrExpressionMetadata
#define STATIC_HasCondition HasCondition
#define STATIC_HasAction HasAction
#define STATIC_HasObjectAction HasObjectAction
#define STATIC_HasObjectCondition HasObjectCondition
#define STATIC_HasBehaviorAction HasBehaviorAction
#define STATIC_HasBehaviorCondition HasBehaviorCondition
#define STATIC_HasExpression HasExpression
#define STATIC_HasObjectExpression HasObjectExpression
#define STATIC_HasBehaviorExpression HasBehaviorExpression
#define STATIC_HasStrExpression HasStrExpression
#define STATIC_HasObjectStrExpression HasObjectStrExpression
#define STATIC_HasBehaviorStrExpression HasBehaviorStrExpression
#define STATIC_RenameObjectInEvents RenameObjectInEvents
#define STATIC_RemoveObjectInEvents RemoveObjectInEvents
#define STATIC_ReplaceStringInEvents ReplaceStringInEvents
#define STATIC_ExposeProjectEvents ExposeProjectEvents
#define STATIC_GetBehaviorMetadata GetBehaviorMetadata
#define STATIC_GetObjectMetadata GetObjectMetadata
#define STATIC_GetActionMetadata GetActionMetadata
#define STATIC_GetConditionMetadata GetConditionMetadata
#define STATIC_GetExpressionMetadata GetExpressionMetadata
#define STATIC_GetObjectExpressionMetadata GetObjectExpressionMetadata
#define STATIC_GetBehaviorExpressionMetadata GetBehaviorExpressionMetadata
#define STATIC_GetStrExpressionMetadata GetStrExpressionMetadata
#define STATIC_GetObjectStrExpressionMetadata GetObjectStrExpressionMetadata
#define STATIC_GetBehaviorStrExpressionMetadata GetBehaviorStrExpressionMetadata
#define STATIC_GenerateSceneEventsCompleteCode GenerateSceneEventsCompleteCode
#define STATIC_GenerateEventsFunctionCode GenerateEventsFunctionCode
#define STATIC_Major Major
#define STATIC_Minor Minor
#define STATIC_Build Build
#define STATIC_Revision Revision
#define STATIC_FullString FullString
#define STATIC_Status Status
#define STATIC_Year Year
#define STATIC_Month Month
#define STATIC_Date Date
#define STATIC_ObjectRenamedInLayout ObjectRenamedInLayout
#define STATIC_ObjectRemovedInLayout ObjectRemovedInLayout
#define STATIC_GlobalObjectRenamed GlobalObjectRenamed
#define STATIC_GlobalObjectRemoved GlobalObjectRemoved
#define STATIC_CreateRectangle CreateRectangle
#define STATIC_SanityCheckBehaviorProperty SanityCheckBehaviorProperty
#define STATIC_SanityCheckObjectProperty SanityCheckObjectProperty
#define STATIC_SanityCheckObjectInitialInstanceProperty \
SanityCheckObjectInitialInstanceProperty
#define STATIC_SanityCheckBehaviorsSharedDataProperty \
SanityCheckBehaviorsSharedDataProperty
#define STATIC_SearchInEvents SearchInEvents
#define STATIC_UnfoldWhenContaining UnfoldWhenContaining
#define STATIC_EventsFunctionToObjectsContainer EventsFunctionToObjectsContainer
#define STATIC_ParametersToObjectsContainer ParametersToObjectsContainer
#define STATIC_GetObjectParameterIndexFor GetObjectParameterIndexFor
#define STATIC_GetNamespaceSeparator GetNamespaceSeparator
#define STATIC_RenameEventsFunctionsExtension RenameEventsFunctionsExtension
#define STATIC_RenameEventsFunction RenameEventsFunction
#define STATIC_UseOldExpressionParser UseOldExpressionParser
#define STATIC_IsUsingOldExpressionParser IsUsingOldExpressionParser
// We postfix some methods with "At" as Javascript does not support overloading
#define GetLayoutAt GetLayout
#define GetExternalEventsAt GetExternalEvents
#define GetExternalLayoutAt GetExternalLayout
#define GetEventsFunctionsExtensionAt GetEventsFunctionsExtension
#define GetLayerAt GetLayer
#define GetObjectAt GetObject
#define GetAt Get
#define GetEventAt GetEvent
#define RemoveEventAt RemoveEvent
#define RemoveAt Remove
#define GetEventsFunctionAt GetEventsFunction
// We don't use prefix in .idl file to workaround a webidl_binder.py bug
// that can't find in its list of interfaces a class which has a prefix.
using namespace gd;
using namespace std;
#include "glue.cpp"
+175
View File
@@ -0,0 +1,175 @@
/*Emscripten generated-code for Module will be inserted here*/
return Module;
};
// Make sure that the naming convention for methods of GDevelop
// classes is camelCase (instead of CamelCase) and rename methods
// with special names (like `WRAPPED_`, `STATIC_`...).
var adaptNamingConventions = function(gd) {
function uncapitalizeFirstLetter(method) {
return method.charAt(0).toLowerCase() + method.slice(1);
}
function removePrefix(method, prefix) {
if (method.indexOf(prefix) !== 0)
return method;
return method.replace(prefix, "");
}
function adaptClassMethods(object) {
var proto = object.prototype;
for (var method in proto) {
if (method && proto.hasOwnProperty(method)) {
var newName = method;
var addToModule = false;
var addToObject = false;
//Detect static methods
if (method.indexOf("STATIC_") === 0) {
newName = removePrefix(newName, "STATIC_");
addToObject = true;
}
//Detect free functions
if (method.indexOf("FREE_") === 0) {
newName = removePrefix(newName, "FREE_");
addToModule = true;
}
//Remove prefix used for custom code generation
newName = removePrefix(newName, "MAP_");
newName = removePrefix(newName, "WRAPPED_");
if (newName.indexOf("CLONE_") === 0) {
newName = "clone";
}
//Normalize method name
newName = uncapitalizeFirstLetter(newName);
if (newName !== method) {
proto[newName] = proto[method];
delete proto[method];
}
if (addToObject) {
object[newName] = proto[newName];
}
if (addToModule) {
gd[newName] = (function(fct) {
return function() { //Simulate a free function
if (arguments.length === 0) return fct();
var args = [];
Array.prototype.push.apply(args, arguments);
args.shift();
return fct.apply(arguments[0], args);
};
})(proto[newName]);
}
}
}
//Offer a delete method that does what gd.destroy does.
proto.delete = function() { gd.destroy(this) };
}
for(var gdClass in gd) {
if (gd.hasOwnProperty(gdClass)) {
if (typeof gd[gdClass] !== "function") continue;
if (!gd[gdClass].prototype) continue;
if (!gd[gdClass].prototype.hasOwnProperty("__class__")) continue;
adaptClassMethods(gd[gdClass]);
}
}
gd.Object = gd.gdObject; //Renaming was done to avoid clashing with javascript Object.
gd.initializePlatforms = gd.ProjectHelper.prototype.initializePlatforms;
//Provide shortcuts for casts:
gd.asStandardEvent = function(evt) { return gd.castObject(evt, gd.StandardEvent); }
gd.asRepeatEvent = function(evt) { return gd.castObject(evt, gd.RepeatEvent); }
gd.asWhileEvent = function(evt) { return gd.castObject(evt, gd.WhileEvent); }
gd.asForEachEvent = function(evt) { return gd.castObject(evt, gd.ForEachEvent); }
gd.asCommentEvent = function(evt) { return gd.castObject(evt, gd.CommentEvent); }
gd.asGroupEvent = function(evt) { return gd.castObject(evt, gd.GroupEvent); }
gd.asLinkEvent = function(evt) { return gd.castObject(evt, gd.LinkEvent); }
gd.asJsCodeEvent = function(evt) { return gd.castObject(evt, gd.JsCodeEvent); }
gd.asPlatform = function(evt) { return gd.castObject(evt, gd.Platform); }
gd.asSpriteObject = function(evt) { return gd.castObject(evt, gd.SpriteObject); }
gd.asTiledSpriteObject = function(evt) { return gd.castObject(evt, gd.TiledSpriteObject); }
gd.asPanelSpriteObject = function(evt) { return gd.castObject(evt, gd.PanelSpriteObject); }
gd.asTextObject = function(evt) { return gd.castObject(evt, gd.TextObject); }
gd.asShapePainterObject = function(evt) { return gd.castObject(evt, gd.ShapePainterObject); }
gd.asAdMobObject = function(evt) { return gd.castObject(evt, gd.AdMobObject); }
gd.asTextEntryObject = function(evt) { return gd.castObject(evt, gd.TextEntryObject); }
gd.asParticleEmitterObject = function(evt) { return gd.castObject(evt, gd.ParticleEmitterObject); }
gd.asObjectJsImplementation = function(evt) { return gd.castObject(evt, gd.ObjectJsImplementation); }
gd.asImageResource = function(evt) { return gd.castObject(evt, gd.ImageResource); }
//Convenience methods:
gd.VectorString.prototype.toJSArray = function() {
var arr = [];
var size = this.size();
for(var i = 0;i < size; ++i) {
arr.push(this.at(i));
}
return arr;
}
// Add gd.Serializer.fromJSObject which is much faster than manually parsing
// JSON with gd.Serializer.fromJSON.
gd.Serializer._fromJSObject = function(object, element) {
if (typeof object === 'number') {
element.setDouble(object);
} else if (typeof object === 'string') {
element.setString(object);
} else if (typeof object === 'boolean') {
element.setBool(object);
} else if (Array.isArray(object)) {
element.considerAsArray();
for(var i = 0;i<object.length;++i) {
var item = element.addChild("");
gd.Serializer._fromJSObject(object[i], item);
}
} else {
for(var childName in object) {
if (object.hasOwnProperty(childName)) {
var child = element.addChild(childName);
gd.Serializer._fromJSObject(object[childName], child);
}
}
}
};
gd.Serializer.fromJSObject = function(object) {
var element = new gd.SerializerElement();
if (object) gd.Serializer._fromJSObject(object, element);
return element;
};
//Preserve backward compatibility with some alias for methods:
gd.VectorString.prototype.get = gd.VectorString.prototype.at;
gd.VectorPlatformExtension.prototype.get = gd.VectorPlatformExtension.prototype.at;
gd.InstructionsList.prototype.push_back = function(e) {
this.insert(e, this.size() - 1);
};
return gd;
};
//Expose gd module.
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
module.exports = function(options) {
return adaptNamingConventions(createModule(options));
};
} else {
window.gd = adaptNamingConventions(createModule());
}
})();
//# sourceMappingURL=libGD.raw.js.map;
+19
View File
@@ -0,0 +1,19 @@
(function() {
var createModule = function(options) {
var Module = options || {};
// Setup 80Mb for memory by default.
Module.TOTAL_MEMORY = Module.TOTAL_MEMORY || 83886080;
//Prevent Emscripten to bind its Module to Node's module. We take care of exposing
//the Module by ourselves (see post.js).
var module;
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
var module = {
exports: {}
};
} else {
module = undefined;
}
/*Emscripten generated-code for Module will be inserted here*/
+101
View File
@@ -0,0 +1,101 @@
cmake_minimum_required(VERSION 2.6)
cmake_policy(SET CMP0015 NEW)
project(libGD.js)
#Sanity checks
IF ("${CMAKE_BUILD_TYPE}" STREQUAL "")
message( "CMAKE_BUILD_TYPE is empty, assuming build type is Release" )
set(CMAKE_BUILD_TYPE Release)
ENDIF()
if(NOT EMSCRIPTEN)
error("You're trying to compile libGD.js without emscripten.")
endif()
SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --memory-init-file 0" ) #Less efficient, but output a single JS file
# SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ASSERTIONS=2 -s SAFE_HEAP=1" ) #Debug
# SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g4 -s DEMANGLE_SUPPORT=1" ) #Debug
SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -s ALLOW_MEMORY_GROWTH=1 -s ASM_JS=2" ) #Release
# SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -s WASM=1" ) # WASM
# Note: "-s ASM_JS=2" results in 2x faster library (noticeable on operations like export)
# on Chrome (see https://github.com/kripken/emscripten/wiki/Chrome-Perf-Issues)
#Common directories:
###
set(GDCpp_include_dir ${GD_base_dir}/GDCpp)
set(GDCpp_lib_dir ${GD_base_dir}/Binaries/Output/${CMAKE_BUILD_TYPE}_${CMAKE_SYSTEM_NAME})
set(GDJS_include_dir ${GD_base_dir}/GDJS)
set(GDJS_lib_dir ${GD_base_dir}/Binaries/Output/${CMAKE_BUILD_TYPE}_${CMAKE_SYSTEM_NAME}/JsPlatform)
set(GDCpp_include_dir ${GDCpp_include_dir} PARENT_SCOPE) #Expose GDCpp directories to extensions for includes.
set(GDCpp_lib_dir ${GDCpp_lib_dir} PARENT_SCOPE)
#Dependencies on external libraries:
###
include_directories(${sfml_include_dir})
include_directories(${GDCORE_include_dir})
include_directories(${GDCpp_include_dir})
include_directories(${GDJS_include_dir})
#Defines
###
add_definitions( -DGD_IDE_ONLY )
add_definitions( -DGD_NO_WX_GUI ) #When compiling for the web, we do not want any GUI related feature.
IF(CMAKE_BUILD_TYPE MATCHES DEBUG)
add_definitions( -DDEBUG )
IF(WIN32)
add_definitions( __WXDEBUG__ )
ENDIF(WIN32)
ELSEIF ("${CMAKE_BUILD_TYPE}" STREQUAL "Release")
add_definitions( -DRELEASE )
add_definitions( -DBOOST_DISABLE_ASSERTS )
ELSE()
add_definitions( -DDEV )
add_definitions( -DBOOST_DISABLE_ASSERTS )
ENDIF(CMAKE_BUILD_TYPE MATCHES DEBUG)
add_definitions( -Dlinux )
add_definitions( -DLINUX )
add_definitions( -DGD_API= )
add_definitions( -DGD_CORE_API= )
add_definitions( -DGD_EXTENSION_API= )
#The target
###
include_directories(.)
add_library(
GD
SHARED
"Bindings/Wrapper.cpp"
"Bindings/BehaviorJsImplementation.cpp"
"Bindings/ObjectJsImplementation.cpp"
"Bindings/BehaviorSharedDataJsImplementation.cpp"
)
set_target_properties(GD PROPERTIES SUFFIX ".raw.js")
set(LIBRARY_OUTPUT_PATH ${GD_base_dir}/Binaries/Output/libGD.js/${CMAKE_BUILD_TYPE})
set(ARCHIVE_OUTPUT_PATH ${GD_base_dir}/Binaries/Output/libGD.js/${CMAKE_BUILD_TYPE})
set(RUNTIME_OUTPUT_PATH ${GD_base_dir}/Binaries/Output/libGD.js/${CMAKE_BUILD_TYPE})
#Linker files
###
target_link_libraries(GD GDCore)
target_link_libraries(GD GDJS)
target_link_libraries(GD PlatformBehavior)
target_link_libraries(GD DestroyOutsideBehavior)
target_link_libraries(GD TiledSpriteObject)
target_link_libraries(GD DraggableBehavior)
target_link_libraries(GD TopDownMovementBehavior)
target_link_libraries(GD TextObject)
target_link_libraries(GD PanelSpriteObject)
target_link_libraries(GD AnchorBehavior)
target_link_libraries(GD PrimitiveDrawing)
target_link_libraries(GD TextEntryObject)
target_link_libraries(GD Inventory)
target_link_libraries(GD LinkedObjects)
target_link_libraries(GD SystemInfo)
target_link_libraries(GD Shopify)
target_link_libraries(GD PathfindingBehavior)
target_link_libraries(GD PhysicsBehavior)
# target_link_libraries(GD FacebookInstantGames) - Pure JS extension
target_link_libraries(GD ParticleSystem)
+172
View File
@@ -0,0 +1,172 @@
module.exports = function(grunt) {
var emscriptenPath = process.env.EMSCRIPTEN;
var emscriptenMemoryProfiler = emscriptenPath + '/src/memoryprofiler.js';
var cmakeToolchainpath =
emscriptenPath + '/cmake/Modules/Platform/Emscripten.cmake';
var buildOutputPath = '../Binaries/Output/libGD.js/Release/';
var buildPath = '../Binaries/embuild';
var isWin = /^win/.test(process.platform);
var cmakeBinary = isWin
? '"C:\\Program Files (x86)\\CMake\\bin\\cmake"'
: 'emconfigure cmake';
var cmakeArgs = isWin ? '-G "MinGW Makefiles"' : '';
var makeBinary = isWin ? 'mingw32-make' : 'emmake make';
//Sanity checks
var fs = require('fs');
if (!process.env.EMSCRIPTEN) {
console.error('🔴 EMSCRIPTEN env. variable is not set');
console.log(
'⚠️ Please set Emscripten environment by launching `emsdk_env` script'
);
}
if (!fs.existsSync(emscriptenMemoryProfiler)) {
console.error(
'🔴 Unable to find memoryprofiler.js inside Emscripten sources'
);
console.log(
"⚠️ Building with profiler (build:with-profiler task) won't work"
);
}
grunt.initConfig({
concat: {
options: {
separator: ';',
},
'without-profiler': {
src: [
'Bindings/prejs.js',
buildOutputPath + 'libGD.raw.js',
'Bindings/glue.js',
'Bindings/postjs.js',
],
dest: buildOutputPath + 'libGD.js',
},
'with-profiler': {
src: [
'Bindings/prejs.js',
buildOutputPath + 'libGD.raw.js',
'Bindings/glue.js',
emscriptenMemoryProfiler,
'Bindings/postjs.js',
],
dest: buildOutputPath + 'libGD.js',
},
},
mkdir: {
embuild: {
options: {
create: [buildPath],
},
},
},
shell: {
//Launch CMake if needed
cmake: {
src: [buildPath + '/CMakeCache.txt', 'CMakeLists.txt'],
command:
cmakeBinary +
' ' +
cmakeArgs +
' ../.. -DFULL_VERSION_NUMBER=FALSE',
options: {
execOptions: {
cwd: buildPath,
env: process.env,
maxBuffer: Infinity,
},
},
},
//Generate glue.cpp and glue.js file using Bindings.idl, and patch them
updateGDBindings: {
src: 'Bindings/Bindings.idl',
command: 'node update-bindings.js',
},
//Compile GDevelop with emscripten
make: {
command: makeBinary + ' -j 4',
options: {
execOptions: {
cwd: buildPath,
env: process.env,
},
},
},
},
uglify: {
build: {
files: [
{
src: [buildOutputPath + 'libGD.js'],
dest: buildOutputPath + 'libGD.min.js',
},
],
},
},
clean: {
options: { force: true },
build: {
src: [buildOutputPath + 'libGD.js', buildOutputPath + 'libGD.min.js'],
},
},
compress: {
main: {
options: {
mode: 'gzip',
},
files: [
{
expand: true,
src: [buildOutputPath + '/libGD.js'],
dest: '.',
ext: '.js.gz',
},
],
},
},
copy: {
newIDE: {
files: [
{
expand: true,
src: [buildOutputPath + '/libGD.js'],
dest: '../newIDE/app/public',
flatten: true,
},
],
},
},
});
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-compress');
grunt.loadNpmTasks('grunt-string-replace');
grunt.loadNpmTasks('grunt-shell');
grunt.loadNpmTasks('grunt-newer');
grunt.loadNpmTasks('grunt-mkdir');
grunt.registerTask('build:raw', [
'clean',
'mkdir:embuild',
'newer:shell:cmake',
'newer:shell:updateGDBindings',
'shell:make',
]);
grunt.registerTask('build', [
'build:raw',
'concat:without-profiler',
'compress',
'copy:newIDE',
]);
grunt.registerTask('build:with-profiler', [
'build:raw',
'concat:with-profiler',
'compress',
'copy:newIDE',
]);
};
+74
View File
@@ -0,0 +1,74 @@
# GDevelop.js
This is a port of some parts of **GDevelop** to Javascript using **[Emscripten]**.
GDevelop is a full featured, cross-platform, open-source game creator software requiring no programming skills. Download it on [the official website](https://gdevelop-app.com).
## How to build
- Make sure you have [CMake 3.5+](http://www.cmake.org/)
- Install [Emscripten](https://github.com/kripken/emscripten), as explained on the [Emscripten installation instructions](http://kripken.github.io/emscripten-site/docs/getting_started/downloads.html):
```shell
git clone https://github.com/juj/emsdk.git
cd emsdk
./emsdk update
./emsdk install sdk-1.37.37-64bit
./emsdk activate sdk-1.37.37-64bit
source ./emsdk_env.sh
```
(on Windows run `emsdk` instead of `./emsdk`, and `emsdk_env.bat` instead of `source ./emsdk_env.sh`. For up-to-date information, check again [Emscripten installation instructions](http://kripken.github.io/emscripten-site/docs/getting_started/downloads.html)).
- Make sure you have Node.js installed and grunt:
```shell
npm install -g grunt-cli
```
- Launch the build from GDevelop.js folder:
```shell
cd GDevelop.js
npm install
npm run build
```
Output is created in _/path/to/GD/Binaries/Output/libGD.js/_.
- You can then launch GDevelop 5 that will use your build of Gdevelop.js:
```shell
cd ..
cd newIDE/app
npm install
npm start
```
More information in [GDevelop 5 readme](https://github.com/4ian/GD/blob/master/newIDE/README.md).
### Tests
```
npm test
```
### About the internal steps of compilation
The grunt _build_ task:
- create `Binaries/embuild` directory,
- patch SFML `Config.hpp` file to make Emscripten recognized as a linux target,
- launch CMake inside to compile GDevelop with _Emscripten toolchain file_,
- update the glue.cpp and glue.js from Bindings.idl using _Emscripten WebIDL Binder_,
- launch the compilation with _make_ and wrap the generated `libGD.js.raw` into the final `libGD.js` file.
It also create a compressed `libGD.js.gz` file which is handy for distributing the library pre-compressed to web browsers.
## Documentation
- The file [Bindings.idl](https://github.com/4ian/GDevelop.js/blob/master/Bindings/Bindings.idl) describes all the classes available in GDevelop.js.
- Refer to [GDevelop documentation](http://4ian.github.io/GD-Documentation/GDCore%20Documentation/) for detailed documentation of the original C++ classes.
[emscripten]: https://github.com/kripken/emscripten
File diff suppressed because it is too large Load Diff
+308
View File
@@ -0,0 +1,308 @@
const initGDevelopJS = require('../../Binaries/Output/libGD.js/Release/libGD.js');
describe('libGD.js - GDJS related tests', function() {
let gd = null;
beforeAll(() => (gd = initGDevelopJS()));
describe('EventsCodeGenerator', function() {
it('can generate code for a layout with generateSceneEventsCompleteCode', function() {
const project = gd.ProjectHelper.createNewGDJSProject();
const layout = project.insertNewLayout('Scene', 0);
// Create a repeat event with a trigger once
const evt = layout
.getEvents()
.insertNewEvent(project, 'BuiltinCommonInstructions::Repeat', 0);
gd.asRepeatEvent(evt).setRepeatExpression('5+4+3+2+1');
const condition = new gd.Instruction();
condition.setType('BuiltinCommonInstructions::Once');
gd.asRepeatEvent(evt)
.getConditions()
.insert(condition, 0);
const code = gd.EventsCodeGenerator.generateSceneEventsCompleteCode(
project,
layout,
layout.getEvents(),
new gd.SetString(),
true
);
// The loop is using a counter somewhere
expect(code).toMatch('repeatCount');
// Trigger once is used in a condition
expect(code).toMatch('runtimeScene.getOnceTriggers().triggerOnce');
condition.delete();
});
it('can generate code for a layout with generateEventsFunctionCode', function() {
const project = new gd.ProjectHelper.createNewGDJSProject();
const includeFiles = new gd.SetString();
const eventsFunction = new gd.EventsFunction();
// Create a function accepting 4 parameters:
const parameters = eventsFunction.getParameters();
const parameter1 = new gd.ParameterMetadata();
parameter1.setType('objectList');
parameter1.setName('MyObject');
parameter1.setDescription('The first object to be used');
const parameter2 = new gd.ParameterMetadata();
parameter2.setType('expression');
parameter2.setName('MyNumber');
parameter2.setDescription('Some number');
const parameter3 = new gd.ParameterMetadata();
parameter3.setType('objectList');
parameter3.setName('MySprite');
parameter3.setDescription('The second object to be used, a sprite');
parameter3.setExtraInfo('Sprite');
const parameter4 = new gd.ParameterMetadata();
parameter4.setType('string');
parameter4.setName('MyString');
parameter4.setDescription('Some string');
parameters.push_back(parameter1);
parameters.push_back(parameter2);
parameters.push_back(parameter3);
parameters.push_back(parameter4);
// Create a repeat event with...
const eventsList = eventsFunction.getEvents();
const evt = eventsList.insertNewEvent(
project,
'BuiltinCommonInstructions::Repeat',
0
);
gd.asRepeatEvent(evt).setRepeatExpression('5+4+3+2+1');
// ...a trigger once condition...
const condition = new gd.Instruction();
condition.setType('BuiltinCommonInstructions::Once');
gd.asRepeatEvent(evt)
.getConditions()
.insert(condition, 0);
// ...and an action to update a variable of MyObject
const action = new gd.Instruction();
action.setType('ModVarObjet');
action.setParametersCount(4);
action.setParameter(0, 'MyObject');
action.setParameter(1, 'ObjectVariable');
action.setParameter(2, '+');
action.setParameter(3, '42');
gd.asRepeatEvent(evt)
.getActions()
.insert(action, 0);
const action2 = new gd.Instruction();
action2.setType('ModVarObjet');
action2.setParametersCount(4);
action2.setParameter(0, 'MyObject');
action2.setParameter(1, 'ObjectVariable2');
action2.setParameter(2, '=');
action2.setParameter(
3,
'GetArgumentAsNumber("MyNumber") + ToNumber(GetArgumentAsString("MyString"))'
);
gd.asRepeatEvent(evt)
.getActions()
.insert(action2, 1);
const namespace = 'gdjs.eventsFunction.myTest';
const code = gd.EventsCodeGenerator.generateEventsFunctionCode(
project,
eventsFunction,
namespace,
includeFiles,
true
);
// Check that the function name is properly generated
expect(code).toMatch(namespace + '.func = function(');
// Check that the context for the events function is here...
expect(code).toMatch('function(runtimeScene, eventsFunctionContext)');
expect(code).toMatch('var eventsFunctionContext =');
// Check that the parameters, with the (optional) context of the parent function,
// are all here
expect(code).toMatch(
'function(runtimeScene, MyObject, MyNumber, MySprite, MyString, parentEventsFunctionContext)'
);
// ...and objects should be able to get queried...
expect(code).toMatch('\"MyObject\": MyObject');
expect(code).toMatch('\"MySprite\": MySprite');
// ...and arguments should be able to get queried too:
expect(code).toMatch('if (argName === "MyNumber") return MyNumber;');
expect(code).toMatch('if (argName === "MyString") return MyString;');
// GetArgumentAsString("MyString") should be generated code to query and cast as a string
// the argument
expect(code).toMatch(
'(typeof eventsFunctionContext !== \'undefined\' ? "" + eventsFunctionContext.getArgument("MyString") : "")'
);
// GetArgumentAsNumber("MyNumber") should be generated code to query and cast as a string
// the argument
expect(code).toMatch(
'(typeof eventsFunctionContext !== \'undefined\' ? Number(eventsFunctionContext.getArgument("MyNumber")) || 0 : 0)'
);
// The loop is using a counter somewhere
expect(code).toMatch('repeatCount');
// Trigger once is used in a condition
expect(code).toMatch('runtimeScene.getOnceTriggers().triggerOnce');
// A variable have 42 added to it
expect(code).toMatch('getVariables().get("ObjectVariable")).add(42)');
condition.delete();
action.delete();
});
it('can generate code for a layout with generateEventsFunctionCode, with groups', function() {
const project = new gd.ProjectHelper.createNewGDJSProject();
const includeFiles = new gd.SetString();
const eventsFunction = new gd.EventsFunction();
// Create a function accepting 2 parameters:
const parameters = eventsFunction.getParameters();
const parameter1 = new gd.ParameterMetadata();
parameter1.setType('objectList');
parameter1.setName('MyObject');
parameter1.setDescription('The first object to be used');
const parameter2 = new gd.ParameterMetadata();
parameter2.setType('objectList');
parameter2.setName('MySprite');
parameter2.setDescription('The second object to be used, a sprite');
parameter2.setExtraInfo('Sprite');
parameters.push_back(parameter1);
parameters.push_back(parameter2);
// And with a group:
const group = eventsFunction.getObjectGroups().insertNew("MyGroup", 0);
group.addObject("MyObject");
group.addObject("MySprite");
// Create a repeat event with...
const eventsList = eventsFunction.getEvents();
const evt = eventsList.insertNewEvent(
project,
'BuiltinCommonInstructions::Repeat',
0
);
gd.asRepeatEvent(evt).setRepeatExpression('5+4+3+2+1');
// ...an action to update a variable of the group of objects
const action = new gd.Instruction();
action.setType('ModVarObjet');
action.setParametersCount(4);
action.setParameter(0, 'MyGroup');
action.setParameter(1, 'ObjectVariable');
action.setParameter(2, '+');
action.setParameter(3, '42');
gd.asRepeatEvent(evt)
.getActions()
.insert(action, 0);
const namespace = 'gdjs.eventsFunction.myTest';
const code = gd.EventsCodeGenerator.generateEventsFunctionCode(
project,
eventsFunction,
namespace,
includeFiles,
true
);
// Check that the function name is properly generated
expect(code).toMatch(namespace + '.func = function(');
// Check that the context for the events function is here...
expect(code).toMatch('function(runtimeScene, eventsFunctionContext)');
expect(code).toMatch('var eventsFunctionContext =');
// Check that the parameters, with the (optional) context of the parent function,
// are all here
expect(code).toMatch(
'function(runtimeScene, MyObject, MySprite, parentEventsFunctionContext)'
);
// ...and objects should be able to get queried...
expect(code).toMatch('\"MyObject\": MyObject');
expect(code).toMatch('\"MySprite\": MySprite');
// Variables of both MyObject and MySprite should have 42 added:
expect(code).toMatch('GDMyObjectObjects2[i].getVariables().get("ObjectVariable")).add(42)');
expect(code).toMatch('GDMySpriteObjects2[i].getVariables().get("ObjectVariable")).add(42)');
action.delete();
});
});
describe('TextObject', function() {
it('should expose TextObject specific methods', function() {
var object = new gd.TextObject('MyTextObject');
object.setString('Hello');
object.setFontName('Hello.ttf');
object.setCharacterSize(10);
object.setBold(true);
object.setColor(1, 2, 3);
expect(object.getString()).toBe('Hello');
expect(object.getFontName()).toBe('Hello.ttf');
expect(object.getCharacterSize()).toBe(10);
expect(object.isBold()).toBe(true);
expect(object.getColorR()).toBe(1);
expect(object.getColorG()).toBe(2);
expect(object.getColorB()).toBe(3);
});
});
describe('TiledSpriteObject', function() {
it('should expose TiledSpriteObject specific methods', function() {
var object = new gd.TiledSpriteObject('MyTiledSpriteObject');
object.setTexture('MyImageName');
expect(object.getTexture()).toBe('MyImageName');
});
});
describe('ShapePainterObject', function() {
it('should expose ShapePainterObject specific methods', function() {
var object = new gd.ShapePainterObject('MyShapePainterObject');
object.setCoordinatesAbsolute();
expect(object.areCoordinatesAbsolute()).toBe(true);
object.setCoordinatesRelative();
expect(object.areCoordinatesAbsolute()).toBe(false);
});
});
describe('TextEntryObject', function() {
it('should expose TextEntryObject', function() {
var object = new gd.TextEntryObject('MyTextEntryObject');
});
});
describe('JsCodeEvent', function() {
it('can store its code', function() {
var event = new gd.JsCodeEvent();
event.setInlineCode('console.log("Hello world");');
expect(event.getInlineCode()).toBe('console.log("Hello world");');
});
it('can store the objects to pass as parameter', function() {
var event = new gd.JsCodeEvent();
event.setParameterObjects('MyObject');
expect(event.getParameterObjects()).toBe('MyObject');
});
it('can be cloned', function() {
var event = new gd.JsCodeEvent();
event.setInlineCode('console.log("Hello world 2");');
event.setParameterObjects('MyObject2');
var cloneEvent = event.clone();
expect(cloneEvent.getParameterObjects()).toBe('MyObject2');
expect(cloneEvent.getInlineCode()).toBe('console.log("Hello world 2");');
});
});
});
+97
View File
@@ -0,0 +1,97 @@
const initGDevelopJS = require('../../Binaries/Output/libGD.js/Release/libGD.js');
describe('libGD.js object serialization', function() {
let gd = null;
beforeAll(() => (gd = initGDevelopJS()));
describe('gd.SerializerElement', function() {
it('should support operations on its value', function() {
var element = new gd.SerializerElement();
element.setString('aaa');
expect(element.getValue().getString()).toBe('aaa');
element.setInt(123);
expect(element.getValue().getInt()).toBe(123);
element.setDouble(123.456);
expect(element.getValue().getDouble()).toBe(123.456);
});
it('should cast values from a type to another', function() {
var element = new gd.SerializerElement();
element.setString('123');
expect(element.getValue().getString()).toBe('123');
expect(element.getValue().getInt()).toBe(123);
expect(element.getValue().getDouble()).toBe(123.0);
element.setString('true');
expect(element.getValue().getBool()).toBe(true);
element.setBool(false);
expect(element.getValue().getBool()).toBe(false);
});
it('should support operations on its children', function() {
var element = new gd.SerializerElement();
expect(element.hasChild('Missing')).toBe(false);
var child1 = element.addChild('Child1');
expect(element.hasChild('Child1')).toBe(true);
expect(element.getChild('Child1').ptr).toBe(child1.ptr);
var child2 = new gd.SerializerElement();
child2.addChild('subChild').setString('Hello world!');
element.addChild('Child2');
element.setChild('Child2', child2);
expect(
element
.getChild('Child2')
.getChild('subChild')
.getValue()
.getString()
).toBe('Hello world!');
});
});
describe('gd.Serializer', function() {
it('should serialize a Text Object', function() {
var obj = new gd.TextObject('testObject');
obj.setType('TextObject::Text');
obj.setName('testObject');
obj.setString('Text of the object, with 官话 characters');
var serializedObject = new gd.SerializerElement();
obj.serializeTo(serializedObject);
var jsonObject = gd.Serializer.toJSON(serializedObject);
serializedObject.delete();
obj.delete();
expect(jsonObject).toBe(
'{"bold": false,"italic": false,"name": "testObject","smoothed": true,"type": "TextObject::Text","underlined": false,"variables": [],"behaviors": [],"string": "Text of the object, with 官话 characters","font": "","characterSize": 20,"color": {"b": 255,"g": 255,"r": 255}}'
);
});
});
describe('gd.Serializer.fromJSON', function() {
it('should unserialize and reserialize JSON', function() {
var json =
'{"a": {"a1": {"name": "","referenceTo": "/a/a1"}},"b": {"b1": "world"},"c": {"c1": 3},"things": {"0": {"name": "layout0","referenceTo": "/layouts/layout"},"1": {"name": "layout1","referenceTo": "/layouts/layout"},"2": {"name": "layout2","referenceTo": "/layouts/layout"},"3": {"name": "layout3","referenceTo": "/layouts/layout"},"4": {"name": "layout4","referenceTo": "/layouts/layout"}}}';
var element = gd.Serializer.fromJSON(json);
var outputJson = gd.Serializer.toJSON(element);
expect(outputJson).toBe(json);
});
});
describe('gd.Serializer.fromJSObject', function() {
it('should unserialize and reserialize JSON', function() {
var json =
'{"a": {"a1": {"name": "","referenceTo": "/a/a1"}},"b": {"b1": "world"},"c": {"c1": 3},"things": {"0": {"name": "layout0","referenceTo": "/layouts/layout"},"1": {"name": "layout1","referenceTo": "/layouts/layout"},"2": {"name": "layout2","referenceTo": "/layouts/layout"},"3": {"name": "layout3","referenceTo": "/layouts/layout"},"4": {"name": "layout4","referenceTo": "/layouts/layout"}}}';
var object = JSON.parse(json);
var element = gd.Serializer.fromJSObject(object);
var outputJson = gd.Serializer.toJSON(element);
expect(outputJson).toBe(json);
});
});
});
+23
View File
@@ -0,0 +1,23 @@
const initGDevelopJS = require('../../Binaries/Output/libGD.js/Release/libGD.js');
describe('libGD.js - stress tests', function() {
it('should support being required a lot', function() {
process.setMaxListeners(40); //Avoid Node.js warning.
for (var i = 0; i < 20; ++i) {
var gd = initGDevelopJS();
var layout = new gd.Layout();
layout.setName('Test #' + i);
expect(layout.getName()).toBe('Test #' + i);
layout.delete();
}
});
it('should support creates a lot of objects', function() {
var gd = initGDevelopJS();
for (var i = 0; i < 100; ++i) {
var layout = new gd.Layout();
layout.setName('Test #' + i);
expect(layout.getName()).toBe('Test #' + i);
layout.delete();
}
});
});
+65
View File
@@ -0,0 +1,65 @@
const initGDevelopJS = require('../../Binaries/Output/libGD.js/Release/libGD.js');
describe('libGD.js - gd.Vector* tests', function() {
let gd = null;
beforeAll(() => (gd = initGDevelopJS()));
describe('gd.VectorString', function() {
let vector = null;
beforeAll(() => vector = new gd.VectorString());
it('should allow to add strings', function() {
expect(vector.size()).toBe(0);
vector.push_back('Hello world');
expect(vector.size()).toBe(1);
expect(vector.get(0)).toBe('Hello world');
});
it('can be converted to a JS array', function() {
vector.push_back('Hello world 2');
vector.push_back('Hello world 3');
var array = vector.toJSArray();
expect(Array.isArray(array)).toBe(true);
expect(array.length).toBe(3);
expect(array[0]).toBe('Hello world');
expect(array[1]).toBe('Hello world 2');
});
it('can be cleared', function() {
vector.clear();
expect(vector.size()).toBe(0);
});
it('should allow to add lots of strings', function() {
expect(vector.size()).toBe(0);
for (var i = 0; i < 250; ++i) {
vector.push_back('Hello world #' + i);
}
expect(vector.size()).toBe(250);
expect(vector.get(34)).toBe('Hello world #34');
});
it('can change a string at a specified index', function() {
var vector2 = new gd.VectorString();
vector2.push_back('foo');
vector2.push_back('bar');
expect(vector2.size()).toBe(2);
expect(vector2.get(1)).toBe('bar');
expect(vector.set(56, 'Modified hello world'));
expect(vector.get(34)).toBe('Hello world #34');
expect(vector2.set(1, 'baz'));
expect(vector.get(56)).toBe('Modified hello world');
expect(vector2.get(1)).toBe('baz');
expect(vector2.get(0)).toBe('foo');
expect(vector.size()).toBe(250);
});
it('should allow to add UTF8 strings', function() {
vector.clear();
expect(vector.size()).toBe(0);
vector.push_back('Bonjour à tous');
vector.push_back('官话');
expect(vector.size()).toBe(2);
expect(vector.get(0)).toBe('Bonjour à tous');
expect(vector.get(1)).toBe('官话');
});
afterAll(() => vector.delete());
});
});
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015-present Florian Rival
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.
+7505
View File
File diff suppressed because it is too large Load Diff
+39
View File
@@ -0,0 +1,39 @@
{
"name": "GDevelop.js",
"version": "0.0.1",
"author": "Florian Rival <[email protected]> (http://florianrival.com)",
"description": "Core of GDevelop, the open-source game creator requiring no programming skills, ported to Javascript",
"keywords": [
"GDevelop",
"game",
"creator",
"events",
"programming",
"making",
"port"
],
"scripts": {
"build": "grunt build",
"test": "jest"
},
"license": "MIT",
"devDependencies": {
"extend": "^2.0.1",
"grunt": "^1.0.1",
"grunt-contrib-clean": "^1.0.0",
"grunt-contrib-compress": "^1.3.0",
"grunt-contrib-concat": "^1.0.1",
"grunt-contrib-copy": "^1.0.0",
"grunt-contrib-uglify": "^2.0.0",
"grunt-mkdir": "^1.0.0",
"grunt-newer": "^1.2.0",
"grunt-shell": "^2.1.0",
"grunt-string-replace": "^1.3.1",
"jest": "^23.5.0"
},
"jest": {
"transformIgnorePatterns": [
"../Binaries/Output/libGD.js/Release/libGD.js"
]
}
}
+193
View File
@@ -0,0 +1,193 @@
#!/usr/bin/env node
/**
* This script launch Emscripten's WebIDL binder to generate
* the glue.cpp and glue.js file inside Bindings directory
* using Bindings.idl
*/
var debug = false; //If true, add additional checks in bindings files.
var fs = require('fs');
var exec = require('child_process').exec;
if (!process.env.EMSCRIPTEN) {
console.error('EMSCRIPTEN env. variable is not set');
console.log(
'Please set Emscripten environment by launching `emsdk_env` script'
);
}
var emscriptenPath = process.env.EMSCRIPTEN;
var webIdlBinderPath = emscriptenPath + '/tools/webidl_binder.py';
generateGlueFromBinding(function(err) {
if (err) return fatalError(err);
patchGlueCppFile(function(err) {
if (err) return fatalError(err);
});
});
/**
* Run Embind webidl_binder.py to generate glue.cpp and glue.js
* from Bindings.idl
*/
function generateGlueFromBinding(cb) {
fs.exists(webIdlBinderPath, function(exists) {
if (!exists) {
cb({
message: 'Please check your Emscripten installation',
output: "Can't find " + webIdlBinderPath,
});
return;
}
exec(
'python "' + webIdlBinderPath + '" Bindings/Bindings.idl Bindings/glue',
function(err, stdout, stderr) {
if (err) {
cb({ message: 'Error while running WebIDL binder:', output: err });
}
cb(null);
}
);
});
}
/**
* A few modification needs to be made to glue.cpp because of limitations
* of the IDL language/binder.
*/
function patchGlueCppFile(cb) {
var file = 'Bindings/glue.cpp';
var classesToErase = [
'ArbitraryResourceWorkerJS',
'AbstractFileSystemJS',
'BehaviorJsImplementation',
'ObjectJsImplementation',
'BehaviorSharedDataJsImplementation',
];
var functionsToErase = [
'emscripten_bind_ArbitraryResourceWorkerJS_ExposeImage_1',
'emscripten_bind_ArbitraryResourceWorkerJS_ExposeShader_1',
'emscripten_bind_ArbitraryResourceWorkerJS_ExposeFile_1',
];
fs.readFile(file, function(err, data) {
if (err) cb(err);
var patchedFile = '';
var insideReturnStringFunction = false;
var erasingClass = false;
var erasingFunction = false;
data
.toString()
.split('\n')
.forEach(function(line) {
//When declaring a function returning "[Const, Ref] DOMString"
//or "[Const, Value] DOMString"
//in the IDL file, the return type is const char*. We are using
//std::string in GDevelop and need to call c_str.
if (insideReturnStringFunction) {
//[Const, Value] DOMString
if (line.indexOf('static char*') !== -1) {
line = line.replace('static char*', 'static gd::String');
} else if (line.indexOf(', &temp);') !== -1) {
line = line.replace(', &temp);', ', temp.c_str());');
//[Const, Ref] DOMString
} else {
if (debug) {
//For debugging, use a temporary useless reference
//to check the return type is a reference and not a value.
//Could generate false positive.
line = line
.replace(';', '); return ref.c_str();')
.replace(
'return &',
'gd::String & ref = const_cast<gd::String&>('
);
} else {
line = line.replace(';', '.c_str();').replace('&', '');
}
}
}
//Make sure free functions are called properly.
var freeCallPos = line.indexOf('self->FREE_');
if (freeCallPos !== -1) {
var nameEndPos = line.indexOf('(', freeCallPos);
var name = line.substring(freeCallPos + 11, nameEndPos);
var startOfLine = line.substring(0, freeCallPos);
var endOfLine = line.substring(nameEndPos + 1, line.length);
var hasOtherParamers = endOfLine[0] !== ')';
line =
startOfLine +
name +
'(*self' +
(hasOtherParamers ? ', ' : '') +
endOfLine;
}
//Fix calls to operator [] with pointers
line = line.replace('self->MAP_set', '(*self)MAP_set');
//Simulate copy operator with CLONE_type
var cloneCallPos = line.indexOf('self->CLONE_');
if (cloneCallPos !== -1) {
line = line.replace('self->CLONE_', 'new ');
line = line.replace('()', '(*self)');
}
//Custom function MAPS_keys to get the keys of a map
var mapKeyCallPos = line.indexOf('self->MAP_keys');
if (mapKeyCallPos !== -1) {
line =
'temp.clear(); for(auto it = self->begin(); it != self->end();' +
'++it) { temp.push_back(it->first); } return &temp;';
}
if (line.indexOf('class') === 0) {
for (var i = 0; i < classesToErase.length; ++i) {
if (line.indexOf('class ' + classesToErase[i]) === 0) {
erasingClass = true;
}
}
}
if (line.indexOf('EMSCRIPTEN_KEEPALIVE') !== -1) {
for (var i = 0; i < functionsToErase.length; ++i) {
if (
line.indexOf(
'EMSCRIPTEN_KEEPALIVE ' + functionsToErase[i] + '('
) !== -1
) {
erasingFunction = true;
}
}
}
if (!erasingClass && !erasingFunction) patchedFile += line + '\n';
if (line.indexOf('const char* EMSCRIPTEN_KEEPALIVE') == 0) {
insideReturnStringFunction = true;
} else if (line.indexOf('}') == 0) {
insideReturnStringFunction = false;
}
if (erasingClass && line.indexOf('};') === 0) {
erasingClass = false;
}
if (erasingFunction && line.indexOf('}') === 0) {
erasingFunction = false;
}
});
fs.writeFile(file, patchedFile, function(err) {
cb(err);
});
});
}
function fatalError(error) {
if (error.message) console.error(error.message);
if (error.output) console.log(error.output);
process.exit(1);
}