mirror of
https://github.com/Heretek-AI/GDevelop-Heretek.git
synced 2026-10-12 02:02:42 +00:00
Integrate GDevelop.js (emscripten bindings of Core/Extensions to JS) inside the repository
This commit is contained in:
committed by
Florian Rival
parent
ccc29ef416
commit
1693d863d3
+5
-9
@@ -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
|
||||
|
||||
@@ -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
@@ -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 ../..
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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 "";
|
||||
}
|
||||
};
|
||||
@@ -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"
|
||||
@@ -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;
|
||||
@@ -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*/
|
||||
@@ -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)
|
||||
@@ -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',
|
||||
]);
|
||||
};
|
||||
@@ -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
@@ -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");');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -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());
|
||||
});
|
||||
});
|
||||
@@ -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.
|
||||
Generated
+7505
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user