Add experimental export with Cocos2d

This commit is contained in:
Florian Rival
2016-03-19 23:05:45 +01:00
parent 5485b28b19
commit 21012c3445
22 changed files with 1789 additions and 943 deletions

View File

@@ -29,7 +29,8 @@ public:
GetObjectMetadata("PanelSpriteObject::PanelSprite")
.SetIncludeFile("PanelSpriteObject/panelspriteruntimeobject.js")
.AddIncludeFile("PanelSpriteObject/panelspriteruntimeobject-pixi-renderer.js");
.AddIncludeFile("PanelSpriteObject/panelspriteruntimeobject-pixi-renderer.js")
.AddIncludeFile("PanelSpriteObject/panelspriteruntimeobject-cocos-renderer.js");
GetAllActionsForObject("PanelSpriteObject::PanelSprite")["PanelSpriteObject::Width"].SetFunctionName("setWidth").SetGetter("getWidth");
GetAllConditionsForObject("PanelSpriteObject::PanelSprite")["PanelSpriteObject::Width"].SetFunctionName("getWidth");

View File

@@ -30,7 +30,8 @@ public:
GetObjectMetadata("TextObject::Text")
.SetIncludeFile("TextObject/textruntimeobject.js")
.AddIncludeFile("TextObject/textruntimeobject-pixi-renderer.js");
.AddIncludeFile("TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile("TextObject/textruntimeobject-cocos-renderer.js");
GetAllActionsForObject("TextObject::Text")["TextObject::String"].SetFunctionName("setString").SetGetter("getString").SetIncludeFile("TextObject/textruntimeobject.js");
GetAllConditionsForObject("TextObject::Text")["TextObject::String"].SetFunctionName("getString").SetIncludeFile("TextObject/textruntimeobject.js");

View File

@@ -31,7 +31,8 @@ public:
GetObjectMetadata("TiledSpriteObject::TiledSprite")
.SetIncludeFile("TiledSpriteObject/tiledspriteruntimeobject.js")
.AddIncludeFile("TiledSpriteObject/tiledspriteruntimeobject-pixi-renderer.js");
.AddIncludeFile("TiledSpriteObject/tiledspriteruntimeobject-pixi-renderer.js")
.AddIncludeFile("TiledSpriteObject/tiledspriteruntimeobject-cocos-renderer.js");
GetAllActionsForObject("TiledSpriteObject::TiledSprite")["TiledSpriteObject::Width"].SetFunctionName("setWidth").SetGetter("getWidth").SetIncludeFile("TiledSpriteObject/tiledspriteruntimeobject.js");
GetAllConditionsForObject("TiledSpriteObject::TiledSprite")["TiledSpriteObject::Width"].SetFunctionName("getWidth").SetIncludeFile("TiledSpriteObject/tiledspriteruntimeobject.js");

View File

@@ -71,8 +71,8 @@ public:
Exporter exporter(gd::NativeFileSystem::Get());
bool exportSuccessed = externalLayout ?
exporter.ExportExternalLayoutForPreview(project, layout, *externalLayout, exportDir) :
exporter.ExportLayoutForPreview(project, layout, exportDir);
exporter.ExportExternalLayoutForPixiPreview(project, layout, *externalLayout, exportDir) :
exporter.ExportLayoutForPixiPreview(project, layout, exportDir);
if (!exportSuccessed)
{

View File

@@ -180,48 +180,88 @@ BaseProjectExportDialog::BaseProjectExportDialog(wxWindow* parent, wxWindowID id
flexGridSizer1147->Add(m_staticText6768, 0, wxALL|wxEXPAND|wxALIGN_CENTER|wxALIGN_LEFT, 5);
m_panel71 = new wxPanel(exportChoice, wxID_ANY, wxDefaultPosition, wxSize(-1,-1), wxTAB_TRAVERSAL);
exportChoice->AddPage(m_panel71, _("Mobile and web stores using CocoonJS (Experimental)"), false);
m_panel611 = new wxPanel(exportChoice, wxID_ANY, wxDefaultPosition, wxSize(-1,-1), wxTAB_TRAVERSAL);
exportChoice->AddPage(m_panel611, _("Export to a website or native game with Cocos2d (experimental)"), false);
wxFlexGridSizer* flexGridSizer551 = new wxFlexGridSizer(0, 1, 0, 0);
flexGridSizer551->SetFlexibleDirection( wxBOTH );
flexGridSizer551->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
flexGridSizer551->AddGrowableCol(0);
flexGridSizer551->AddGrowableRow(1);
m_panel71->SetSizer(flexGridSizer551);
wxFlexGridSizer* flexGridSizer342 = new wxFlexGridSizer(0, 1, 0, 0);
flexGridSizer342->SetFlexibleDirection( wxBOTH );
flexGridSizer342->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
flexGridSizer342->AddGrowableCol(0);
m_panel611->SetSizer(flexGridSizer342);
cocoonjslogoPanel2 = new wxPanel(m_panel71, wxID_ANY, wxDefaultPosition, wxSize(-1,-1), wxTAB_TRAVERSAL|wxBORDER_SIMPLE);
cocoonjslogoPanel2->SetBackgroundColour(wxColour(wxT("rgb(51,16,69)")));
wxFlexGridSizer* flexGridSizer353 = new wxFlexGridSizer(0, 3, 0, 0);
flexGridSizer353->SetFlexibleDirection( wxBOTH );
flexGridSizer353->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
flexGridSizer353->AddGrowableCol(0);
flexGridSizer353->AddGrowableRow(0);
flexGridSizer551->Add(cocoonjslogoPanel2, 0, wxALL|wxALIGN_CENTER, 0);
flexGridSizer342->Add(flexGridSizer353, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 0);
wxFlexGridSizer* flexGridSizer633 = new wxFlexGridSizer(0, 2, 0, 0);
flexGridSizer633->SetFlexibleDirection( wxBOTH );
flexGridSizer633->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
cocoonjslogoPanel2->SetSizer(flexGridSizer633);
wxFlexGridSizer* flexGridSizer364 = new wxFlexGridSizer(0, 2, 0, 0);
flexGridSizer364->SetFlexibleDirection( wxBOTH );
flexGridSizer364->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
flexGridSizer364->AddGrowableCol(1);
m_staticBitmap574 = new wxStaticBitmap(cocoonjslogoPanel2, wxID_ANY, wxXmlResource::Get()->LoadBitmap(wxT("cocoonjslogo")), wxDefaultPosition, wxSize(-1,-1), 0 );
flexGridSizer353->Add(flexGridSizer364, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 0);
flexGridSizer633->Add(m_staticBitmap574, 0, wxALL, 0);
ID_STATICTEXT45 = new wxStaticText(m_panel611, wxID_ANY, _("Export folder:"), wxDefaultPosition, wxSize(-1,-1), 0);
m_staticText655 = new wxStaticText(m_panel71, wxID_ANY, _("CocoonJS is a technology that helps HTML5 developers\npublish their web-based games and apps in the most\nimportant mobile and web stores."), wxDefaultPosition, wxSize(-1,-1), wxALIGN_CENTRE);
flexGridSizer364->Add(ID_STATICTEXT45, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
flexGridSizer551->Add(m_staticText655, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_TOP, 10);
wxFlexGridSizer* flexGridSizer386 = new wxFlexGridSizer(0, 3, 0, 0);
flexGridSizer386->SetFlexibleDirection( wxBOTH );
flexGridSizer386->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
flexGridSizer386->AddGrowableCol(0);
flexGridSizer386->AddGrowableRow(0);
wxStaticBoxSizer* staticBoxSizer112 = new wxStaticBoxSizer( new wxStaticBox(m_panel71, wxID_ANY, _("Note")), wxVERTICAL);
flexGridSizer364->Add(flexGridSizer386, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 0);
flexGridSizer551->Add(staticBoxSizer112, 1, wxALL|wxEXPAND, 5);
cocosExportFolderEdit = new wxTextCtrl(m_panel611, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(-1,-1), 0);
#if wxVERSION_NUMBER >= 3000
cocosExportFolderEdit->SetHint(wxT(""));
#endif
wxFlexGridSizer* flexGridSizer114 = new wxFlexGridSizer(0, 2, 0, 0);
flexGridSizer114->SetFlexibleDirection( wxBOTH );
flexGridSizer114->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
flexGridSizer114->AddGrowableCol(0);
flexGridSizer386->Add(cocosExportFolderEdit, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
staticBoxSizer112->Add(flexGridSizer114, 1, wxALL|wxEXPAND, 0);
cocosExportBrowseBt = new wxButton(m_panel611, wxID_ANY, _("..."), wxDefaultPosition, wxSize(30,-1), 0);
m_staticText676 = new wxStaticText(m_panel71, wxID_ANY, _("Just click on the \"Export\" button and GDevelop will\npackage your game in a single zip file.\nYou'll then be able to upload this file on the cloud compiler\nat cloud.ludei.com, which will allows you to compile\nthe game into an iOS/Android app."), wxDefaultPosition, wxSize(-1,-1), wxALIGN_LEFT);
flexGridSizer386->Add(cocosExportBrowseBt, 1, wxRIGHT|wxTOP|wxBOTTOM|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
flexGridSizer114->Add(m_staticText676, 0, wxALL|wxEXPAND|wxALIGN_CENTER|wxALIGN_LEFT, 5);
ID_STATICTEXT213 = new wxStaticText(m_panel611, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(-1,-1), 0);
flexGridSizer342->Add(ID_STATICTEXT213, 1, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);
ID_STATICTEXT114 = new wxStaticText(m_panel611, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(-1,-1), 0);
flexGridSizer342->Add(ID_STATICTEXT114, 1, wxLEFT|wxRIGHT|wxBOTTOM|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);
wxStaticBoxSizer* staticBoxSizer4515 = new wxStaticBoxSizer( new wxStaticBox(m_panel611, wxID_ANY, _("Note")), wxVERTICAL);
flexGridSizer342->Add(staticBoxSizer4515, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
wxFlexGridSizer* flexGridSizer4616 = new wxFlexGridSizer(0, 1, 0, 0);
flexGridSizer4616->SetFlexibleDirection( wxBOTH );
flexGridSizer4616->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
flexGridSizer4616->AddGrowableCol(0);
staticBoxSizer4515->Add(flexGridSizer4616, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 0);
ID_STATICTEXT31719 = new wxStaticText(m_panel611, wxID_ANY, _("When the exportation is done, send the files to your website (you need a web hosting) and just go to the website to start the game."), wxDefaultPosition, wxSize(-1,-1), 0);
ID_STATICTEXT31719->Wrap(450);
flexGridSizer4616->Add(ID_STATICTEXT31719, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
ID_STATICTEXT317 = new wxStaticText(m_panel611, wxID_ANY, _("If you install Cocos2d-x, you can also use it to export the game to Android, iOS, Windows, Mac or Linux."), wxDefaultPosition, wxSize(-1,-1), 0);
ID_STATICTEXT317->Wrap(450);
flexGridSizer4616->Add(ID_STATICTEXT317, 1, wxALL|wxEXPAND|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);
ID_HYPERLINKCTRL218 = new wxHyperlinkCtrl(m_panel611, wxID_ANY, _("Click here to learn more about Cocos2d export"), wxT("http://wiki.compilgames.net/doku.php/en/game_develop/tutorials/howtoexportwithcocos2dx"), wxDefaultPosition, wxSize(-1,-1), wxHL_DEFAULT_STYLE);
ID_HYPERLINKCTRL218->SetNormalColour(wxColour(wxT("#0000FF")));
ID_HYPERLINKCTRL218->SetHoverColour(wxColour(wxT("#0000FF")));
ID_HYPERLINKCTRL218->SetVisitedColour(wxColour(wxT("#FF0000")));
flexGridSizer4616->Add(ID_HYPERLINKCTRL218, 1, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);
wxFlexGridSizer* flexGridSizer50 = new wxFlexGridSizer(0, 3, 0, 0);
flexGridSizer50->SetFlexibleDirection( wxBOTH );
@@ -281,6 +321,7 @@ BaseProjectExportDialog::BaseProjectExportDialog(wxWindow* parent, wxWindowID id
#endif
// Connect events
browseBt->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(BaseProjectExportDialog::OnBrowseBtClick), NULL, this);
cocosExportBrowseBt->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(BaseProjectExportDialog::OnCocosExportBrowseBtClick), NULL, this);
closeBt->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(BaseProjectExportDialog::OnCloseBtClicked), NULL, this);
exportBt->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(BaseProjectExportDialog::OnExportBtClicked), NULL, this);
@@ -289,6 +330,7 @@ BaseProjectExportDialog::BaseProjectExportDialog(wxWindow* parent, wxWindowID id
BaseProjectExportDialog::~BaseProjectExportDialog()
{
browseBt->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(BaseProjectExportDialog::OnBrowseBtClick), NULL, this);
cocosExportBrowseBt->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(BaseProjectExportDialog::OnCocosExportBrowseBtClick), NULL, this);
closeBt->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(BaseProjectExportDialog::OnCloseBtClicked), NULL, this);
exportBt->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(BaseProjectExportDialog::OnExportBtClicked), NULL, this);

View File

@@ -53,11 +53,15 @@ protected:
wxStaticBitmap* m_staticBitmap5744;
wxStaticText* m_staticText6555;
wxStaticText* m_staticText6768;
wxPanel* m_panel71;
wxPanel* cocoonjslogoPanel2;
wxStaticBitmap* m_staticBitmap574;
wxStaticText* m_staticText655;
wxStaticText* m_staticText676;
wxPanel* m_panel611;
wxStaticText* ID_STATICTEXT45;
wxTextCtrl* cocosExportFolderEdit;
wxButton* cocosExportBrowseBt;
wxStaticText* ID_STATICTEXT213;
wxStaticText* ID_STATICTEXT114;
wxStaticText* ID_STATICTEXT31719;
wxStaticText* ID_STATICTEXT317;
wxHyperlinkCtrl* ID_HYPERLINKCTRL218;
wxStaticBitmap* ID_STATICBITMAP2;
wxHyperlinkCtrl* ID_HYPERLINKCTRL1;
wxButton* closeBt;
@@ -65,6 +69,7 @@ protected:
protected:
virtual void OnBrowseBtClick(wxCommandEvent& event) { event.Skip(); }
virtual void OnCocosExportBrowseBtClick(wxCommandEvent& event) { event.Skip(); }
virtual void OnCloseBtClicked(wxCommandEvent& event) { event.Skip(); }
virtual void OnExportBtClicked(wxCommandEvent& event) { event.Skip(); }
@@ -85,11 +90,15 @@ public:
wxStaticText* GetStaticText6555() { return m_staticText6555; }
wxStaticText* GetStaticText6768() { return m_staticText6768; }
wxPanel* GetPanel118() { return m_panel118; }
wxStaticBitmap* GetStaticBitmap574() { return m_staticBitmap574; }
wxPanel* GetCocoonjslogoPanel2() { return cocoonjslogoPanel2; }
wxStaticText* GetStaticText655() { return m_staticText655; }
wxStaticText* GetStaticText676() { return m_staticText676; }
wxPanel* GetPanel71() { return m_panel71; }
wxStaticText* GetID_STATICTEXT45() { return ID_STATICTEXT45; }
wxTextCtrl* GetCocosExportFolderEdit() { return cocosExportFolderEdit; }
wxButton* GetCocosExportBrowseBt() { return cocosExportBrowseBt; }
wxStaticText* GetID_STATICTEXT213() { return ID_STATICTEXT213; }
wxStaticText* GetID_STATICTEXT114() { return ID_STATICTEXT114; }
wxStaticText* GetID_STATICTEXT31719() { return ID_STATICTEXT31719; }
wxStaticText* GetID_STATICTEXT317() { return ID_STATICTEXT317; }
wxHyperlinkCtrl* GetID_HYPERLINKCTRL218() { return ID_HYPERLINKCTRL218; }
wxPanel* GetPanel611() { return m_panel611; }
wxChoicebook* GetExportChoice() { return exportChoice; }
wxStaticBitmap* GetID_STATICBITMAP2() { return ID_STATICBITMAP2; }
wxHyperlinkCtrl* GetID_HYPERLINKCTRL1() { return ID_HYPERLINKCTRL1; }

View File

@@ -13,7 +13,7 @@
#include "GDCore/Tools/HelpFileAccess.h"
#include "GDCore/Project/Project.h"
#include "GDCore/CommonTools.h"
#include "GDJS/IDE/Exporter.h"
#include "GDJS/IDE/ExporterHelper.h"
#include "GDCore/IDE/wxTools/SkinHelper.h"
namespace gdjs
@@ -23,15 +23,17 @@ ProjectExportDialog::ProjectExportDialog(wxWindow* parent, gd::Project & project
BaseProjectExportDialog(parent),
project(project_)
{
//TODO: Remove when CocoonJS support is fully working.
exportChoice->RemovePage(2);
exportFolderEdit->AutoCompleteDirectories();
cocosExportFolderEdit->AutoCompleteDirectories();
if ( wxDirExists(project.GetLastCompilationDirectory()) )
{
exportFolderEdit->SetValue(project.GetLastCompilationDirectory());
cocosExportFolderEdit->SetValue(project.GetLastCompilationDirectory());
}
else
{
exportFolderEdit->SetValue(wxFileName::GetHomeDir()+wxFileName::GetPathSeparator()+DeleteInvalidCharacters(project.GetName()));
cocosExportFolderEdit->SetValue(wxFileName::GetHomeDir()+wxFileName::GetPathSeparator()+DeleteInvalidCharacters(project.GetName()));
}
//Open the latest used export type.
@@ -39,7 +41,7 @@ ProjectExportDialog::ProjectExportDialog(wxWindow* parent, gd::Project & project
wxConfigBase::Get()->Read("Export/JS platform/LatestExportType", &latestPage, 0);
exportChoice->SetSelection(latestPage);
hasNode = !Exporter::GetNodeExecutablePath().empty();
hasNode = !ExporterHelper::GetNodeExecutablePath().empty();
nodejsLink->Show(!hasNode);
if ( !hasNode )
{
@@ -57,11 +59,11 @@ ProjectExportDialog::ExportType ProjectExportDialog::GetExportType()
switch(exportChoice->GetSelection())
{
case 1:
return Cordova;
return PixiCordova;
case 2:
return CocoonJS;
return Cocos2d;
default:
return Normal;
return Pixi;
}
}
@@ -107,18 +109,27 @@ void ProjectExportDialog::OnBrowseBtClick(wxCommandEvent& event)
exportFolderEdit->SetValue(dialog.GetPath());
}
void ProjectExportDialog::OnCocosExportBrowseBtClick(wxCommandEvent& event)
{
wxDirDialog dialog(this, _("Choose a folder, empty if possible, where the game should be exported."));
if ( dialog.ShowModal() == wxID_OK )
cocosExportFolderEdit->SetValue(dialog.GetPath());
}
gd::String ProjectExportDialog::GetExportDir()
{
if ( GetExportType() == Normal )
return gd::String(exportFolderEdit->GetValue());
else
if ( GetExportType() == PixiCordova )
return gd::String(wxFileName::GetHomeDir())+wxString(wxFileName::GetPathSeparator())+gd::String(DeleteInvalidCharacters(project.GetName()));
if ( GetExportType() == Cocos2d )
return gd::String(cocosExportFolderEdit->GetValue());
else
return gd::String(exportFolderEdit->GetValue());
}
bool ProjectExportDialog::RequestMinify()
{
if (!hasNode) return false;
return GetExportType() != Normal || minifyCheck->GetValue();
return GetExportType() == PixiCordova || minifyCheck->GetValue();
}
}

View File

@@ -28,9 +28,9 @@ public:
enum ExportType
{
Normal,
Cordova,
CocoonJS
Pixi,
PixiCordova,
Cocos2d
};
/**
@@ -49,6 +49,7 @@ public:
bool RequestMinify();
protected:
virtual void OnCocosExportBrowseBtClick(wxCommandEvent& event);
virtual void OnBrowseBtClick(wxCommandEvent& event);
virtual void OnCloseBtClicked(wxCommandEvent& event);
virtual void OnExportBtClicked(wxCommandEvent& event);

View File

@@ -32,6 +32,7 @@
#include "GDCore/IDE/ProjectStripper.h"
#include "GDCore/CommonTools.h"
#include "GDJS/IDE/Exporter.h"
#include "GDJS/IDE/ExporterHelper.h"
#include "GDJS/Events/CodeGeneration/EventsCodeGenerator.h"
#include "GDJS/IDE/Dialogs/ProjectExportDialog.h"
#include "GDJS/IDE/Dialogs/CocoonJSUploadDialog.h"
@@ -41,418 +42,73 @@
namespace gdjs
{
//Nice tools functions
static void InsertUnique(std::vector<gd::String> & container, gd::String str)
{
if ( std::find(container.begin(), container.end(), str) == container.end() )
container.push_back(str);
}
static void GenerateFontsDeclaration(gd::AbstractFileSystem & fs, const gd::String & outputDir, gd::String & css, gd::String & html)
{
std::vector<gd::String> ttfFiles = fs.ReadDir(outputDir, ".TTF");
for(std::size_t i = 0; i<ttfFiles.size();++i) {
gd::String relativeFile = ttfFiles[i];
fs.MakeRelative(relativeFile, outputDir);
css += "@font-face{ font-family : \"gdjs_font_";
css += relativeFile;
css += "\"; src : url('";
css += relativeFile;
css +="') format('truetype'); }";
html += "<div style=\"font-family: 'gdjs_font_";
html += relativeFile;
html += "';\">.</div>";
}
}
Exporter::~Exporter()
{
}
bool Exporter::ExportLayoutForPreview(gd::Project & project, gd::Layout & layout, gd::String exportDir)
bool Exporter::ExportLayoutForPixiPreview(gd::Project & project, gd::Layout & layout, gd::String exportDir)
{
return ExportLayoutForPreview(project, layout, exportDir, "");
ExporterHelper helper(fs);
return helper.ExportLayoutForPixiPreview(project, layout, exportDir, "");
}
bool Exporter::ExportExternalLayoutForPreview(gd::Project & project, gd::Layout & layout,
bool Exporter::ExportExternalLayoutForPixiPreview(gd::Project & project, gd::Layout & layout,
gd::ExternalLayout & externalLayout, gd::String exportDir)
{
gd::SerializerElement options;
options.AddChild("injectExternalLayout").SetValue(externalLayout.GetName());
return ExportLayoutForPreview(project, layout, exportDir,
ExporterHelper helper(fs);
return helper.ExportLayoutForPixiPreview(project, layout, exportDir,
gd::Serializer::ToJSON(options)
);
}
bool Exporter::ExportLayoutForPreview(gd::Project & project, gd::Layout & layout, gd::String exportDir, gd::String additionalSpec)
{
fs.MkDir(exportDir);
fs.ClearDir(exportDir);
fs.MkDir(exportDir+"/libs");
fs.MkDir(exportDir+"/Extensions");
std::vector<gd::String> includesFiles;
gd::Project exportedProject = project;
//Export resources (*before* generating events as some resources filenames may be updated)
ExportResources(fs, exportedProject, exportDir);
//Generate events code
if ( !ExportEventsCode(exportedProject, fs.GetTempDir()+"/GDTemporaries/JSCodeTemp/", includesFiles) )
return false;
//Export source files
if ( !ExportExternalSourceFiles(exportedProject, fs.GetTempDir()+"/GDTemporaries/JSCodeTemp/", includesFiles) )
{
gd::LogError(_("Error during exporting! Unable to export source files:\n")+lastError);
return false;
}
//Strip the project (*after* generating events as the events may use stripped things (objects groups...))
gd::ProjectStripper::StripProject(exportedProject);
exportedProject.SetFirstLayout(layout.GetName());
//Export the project
ExportToJSON(fs, exportedProject, fs.GetTempDir() + "/GDTemporaries/JSCodeTemp/data.js",
"gdjs.projectData");
includesFiles.push_back(fs.GetTempDir()+"/GDTemporaries/JSCodeTemp/data.js");
//Copy all the dependencies
ExportIncludesAndLibs(includesFiles, exportDir, false);
//Create the index file
if (!ExportIndexFile("./JsPlatform/Runtime/index.html", exportDir, includesFiles, additionalSpec))
return false;
return true;
}
gd::String Exporter::ExportToJSON(gd::AbstractFileSystem &fs, const gd::Project &project, gd::String filename,
gd::String wrapIntoVariable)
{
fs.MkDir(fs.DirNameFrom(filename));
//Save the project to JSON
gd::SerializerElement rootElement;
project.SerializeTo(rootElement);
gd::String output = gd::Serializer::ToJSON(rootElement);
if (!wrapIntoVariable.empty()) output = wrapIntoVariable + " = " + output + ";";
if (!fs.WriteToFile(filename, output))
return "Unable to write "+filename;
return "";
}
bool Exporter::ExportIndexFile(gd::String source, gd::String exportDir, const std::vector<gd::String> & includesFiles, gd::String additionalSpec)
{
gd::String str = fs.ReadFile(source);
//Generate custom declarations for font resources
gd::String customCss;
gd::String customHtml;
GenerateFontsDeclaration(fs, exportDir, customCss, customHtml);
//Generate the file
if (!CompleteIndexFile(str, customCss, customHtml, exportDir, includesFiles, additionalSpec))
return false;
//Write the index.html file
if (!fs.WriteToFile(exportDir + "/index.html", str))
{
lastError = "Unable to write index file.";
return false;
}
return true;
}
bool Exporter::ExportCordovaConfigFile(const gd::Project & project, gd::String exportDir)
{
gd::String str = fs.ReadFile("./JsPlatform/Runtime/Cordova/config.xml")
.FindAndReplace("GDJS_PROJECTNAME", project.GetName())
.FindAndReplace("GDJS_PACKAGENAME", project.GetPackageName())
.FindAndReplace("GDJS_ORIENTATION", "default");
if (!fs.WriteToFile(exportDir + "/config.xml", str))
{
lastError = "Unable to write configuration file.";
return false;
}
return true;
}
bool Exporter::CompleteIndexFile(gd::String & str, gd::String customCss, gd::String customHtml, gd::String exportDir, const std::vector<gd::String> & includesFiles, gd::String additionalSpec)
{
if (additionalSpec.empty()) additionalSpec = "{}";
gd::String codeFilesIncludes;
for (std::vector<gd::String>::const_iterator it = includesFiles.begin(); it != includesFiles.end(); ++it)
{
if ( !fs.FileExists(exportDir + "/" + *it) )
{
std::cout << "Warning: Unable to found " << exportDir+"/"+*it << "." << std::endl;
continue;
}
gd::String relativeFile = exportDir+"/"+*it;
fs.MakeRelative(relativeFile, exportDir);
codeFilesIncludes += "\t<script src=\""+relativeFile+"\"></script>\n";
}
str = str.FindAndReplace("/* GDJS_CUSTOM_STYLE */", customCss)
.FindAndReplace("<!-- GDJS_CUSTOM_HTML -->", customHtml)
.FindAndReplace("<!-- GDJS_CODE_FILES -->", codeFilesIncludes)
.FindAndReplace("{}/*GDJS_ADDITIONAL_SPEC*/", additionalSpec);
return true;
}
bool Exporter::ExportEventsCode(gd::Project & project, gd::String outputDir, std::vector<gd::String> & includesFiles)
{
fs.MkDir(outputDir);
//First, do not forget common includes (they must be included before events generated code files).
InsertUnique(includesFiles, "libs/jshashtable.js");
InsertUnique(includesFiles, "gd.js");
InsertUnique(includesFiles, "libs/hshg.js");
InsertUnique(includesFiles, "pixi-renderers/pixi.js");
InsertUnique(includesFiles, "pixi-renderers/runtimegame-pixi-renderer.js");
InsertUnique(includesFiles, "pixi-renderers/runtimescene-pixi-renderer.js");
InsertUnique(includesFiles, "pixi-renderers/layer-pixi-renderer.js");
InsertUnique(includesFiles, "pixi-renderers/pixi-image-manager.js");
InsertUnique(includesFiles, "pixi-renderers/spriteruntimeobject-pixi-renderer.js");
InsertUnique(includesFiles, "pixi-renderers/loadingscreen-pixi-renderer.js");
InsertUnique(includesFiles, "howler-sound-manager/howler.min.js");
InsertUnique(includesFiles, "howler-sound-manager/howler-sound-manager.js");
InsertUnique(includesFiles, "inputmanager.js");
InsertUnique(includesFiles, "timemanager.js");
InsertUnique(includesFiles, "runtimeobject.js");
InsertUnique(includesFiles, "profiler.js");
InsertUnique(includesFiles, "runtimescene.js");
InsertUnique(includesFiles, "scenestack.js");
InsertUnique(includesFiles, "polygon.js");
InsertUnique(includesFiles, "force.js");
InsertUnique(includesFiles, "layer.js");
InsertUnique(includesFiles, "timer.js");
InsertUnique(includesFiles, "runtimegame.js");
InsertUnique(includesFiles, "variable.js");
InsertUnique(includesFiles, "variablescontainer.js");
InsertUnique(includesFiles, "eventscontext.js");
InsertUnique(includesFiles, "runtimebehavior.js");
InsertUnique(includesFiles, "spriteruntimeobject.js");
//Common includes for events only.
InsertUnique(includesFiles, "events-tools/commontools.js");
InsertUnique(includesFiles, "events-tools/runtimescenetools.js");
InsertUnique(includesFiles, "events-tools/inputtools.js");
InsertUnique(includesFiles, "events-tools/objecttools.js");
InsertUnique(includesFiles, "events-tools/cameratools.js");
InsertUnique(includesFiles, "events-tools/soundtools.js");
InsertUnique(includesFiles, "events-tools/storagetools.js");
InsertUnique(includesFiles, "events-tools/stringtools.js");
InsertUnique(includesFiles, "events-tools/windowtools.js");
InsertUnique(includesFiles, "events-tools/networktools.js");
for (std::size_t i = 0;i<project.GetLayoutsCount();++i)
{
std::set<gd::String> eventsIncludes;
gd::Layout & exportedLayout = project.GetLayout(i);
gd::String eventsOutput = EventsCodeGenerator::GenerateSceneEventsCompleteCode(project, exportedLayout,
exportedLayout.GetEvents(), eventsIncludes, false /*Export for edittime*/);
gd::String filename = outputDir+"code"+gd::String::From(i)+".js";
//Export the code
if (fs.WriteToFile(filename, eventsOutput))
{
for ( std::set<gd::String>::iterator include = eventsIncludes.begin() ; include != eventsIncludes.end(); ++include )
InsertUnique(includesFiles, *include);
InsertUnique(includesFiles, filename);
}
else {
lastError = _("Unable to write ") + filename;
return false;
}
}
return true;
}
bool Exporter::ExportExternalSourceFiles(gd::Project & project, gd::String outputDir, std::vector<gd::String> & includesFiles)
{
const std::vector < std::shared_ptr<gd::SourceFile> > & allFiles = project.GetAllSourceFiles();
for (std::size_t i = 0;i<allFiles.size();++i)
{
if (allFiles[i] == std::shared_ptr<gd::SourceFile>() ) continue;
if (allFiles[i]->GetLanguage() != "Javascript" ) continue;
gd::SourceFile & file = *allFiles[i];
gd::String filename = file.GetFileName();
fs.MakeAbsolute(filename, fs.DirNameFrom(project.GetProjectFile()));
gd::String outFilename = "ext-code"+gd::String::From(i)+".js";
if (!fs.CopyFile(filename, outputDir+outFilename))
gd::LogWarning(_("Could not copy external file") + filename);
InsertUnique(includesFiles, outputDir+outFilename);
}
return true;
}
bool Exporter::ExportIncludesAndLibs(std::vector<gd::String> & includesFiles, gd::String exportDir, bool minify)
{
#if !defined(GD_NO_WX_GUI)
//Includes files :
if ( minify )
{
gd::String nodeExec = GetNodeExecutablePath();
if ( nodeExec.empty() || !fs.FileExists(nodeExec) )
{
std::cout << "Node.js executable not found." << std::endl;
gd::LogWarning(_("The exported script could not be minified: Please check that you installed Node.js on your system."));
minify = false;
}
else
{
gd::String jsPlatformDir = wxGetCwd()+"/JsPlatform/";
gd::String cmd = nodeExec+" \""+jsPlatformDir+"Tools/uglify-js/bin/uglifyjs\" ";
gd::String allJsFiles;
for ( std::vector<gd::String>::iterator include = includesFiles.begin() ; include != includesFiles.end(); ++include )
{
if ( fs.FileExists(jsPlatformDir+"Runtime/"+*include) )
allJsFiles += "\""+jsPlatformDir+"Runtime/"+*include+"\" ";
else if ( fs.FileExists(jsPlatformDir+"Runtime/Extensions/"+*include) )
allJsFiles += "\""+jsPlatformDir+"Runtime/Extensions/"+*include+"\" ";
else if ( fs.FileExists(*include) )
allJsFiles += "\""+*include+"\" ";
}
cmd += allJsFiles;
cmd += "-o \""+exportDir+"/code.js\"";
wxArrayString output;
wxArrayString errors;
long res = wxExecute(cmd, output, errors);
if ( res != 0 )
{
std::cout << "Execution of \"UglifyJS\" failed (Command line : " << cmd << ")." << std::endl;
std::cout << "Output: ";
for (size_t i = 0;i<output.size();++i)
std::cout << output[i] << std::endl;
for (size_t i = 0;i<errors.size();++i)
std::cout << errors[i] << std::endl;
gd::LogWarning(_("The exported script could not be minified.\n\nMay be an extension is triggering this error: Try to contact the developer if you think it is the case."));
minify = false;
}
else
{
includesFiles.clear();
InsertUnique(includesFiles, "code.js");
return true;
}
}
}
#else
minify = false;
#endif
//If the closure compiler failed or was not request, simply copy all the include files.
if ( !minify )
{
for ( std::vector<gd::String>::iterator include = includesFiles.begin() ; include != includesFiles.end(); ++include )
{
if ( fs.FileExists("./JsPlatform/Runtime/"+*include) )
{
gd::String path = fs.DirNameFrom(exportDir+"/"+*include);
if ( !fs.DirExists(path) ) fs.MkDir(path);
fs.CopyFile("./JsPlatform/Runtime/"+*include, exportDir+"/"+*include);
//Ok, the filename is relative to the export dir.
}
else if ( fs.FileExists("./JsPlatform/Runtime/Extensions/"+*include) )
{
gd::String path = fs.DirNameFrom(exportDir+"/Extensions/"+*include);
if ( !fs.DirExists(path) ) fs.MkDir(path);
fs.CopyFile("./JsPlatform/Runtime/Extensions/"+*include, exportDir+"/Extensions/"+*include);
*include = "Extensions/"+*include; //Ensure filename is relative to the export dir.
}
else if ( fs.FileExists(*include) )
{
fs.CopyFile(*include, exportDir+"/"+fs.FileNameFrom(*include));
*include = fs.FileNameFrom(*include); //Ensure filename is relative to the export dir.
}
else
{
std::cout << "Could not copy include file " << *include << " (File not found)." << std::endl;
}
}
}
return true;
}
void Exporter::ExportResources(gd::AbstractFileSystem & fs, gd::Project & project, gd::String exportDir, wxProgressDialog * progressDialog)
{
gd::ProjectResourcesCopier::CopyAllResourcesTo(project, fs, exportDir, true, progressDialog, false, false);
}
void Exporter::ShowProjectExportDialog(gd::Project & project)
{
#if !defined(GD_NO_WX_GUI)
ProjectExportDialog dialog(NULL, project);
if ( dialog.ShowModal() != 1 ) return;
bool exportForCocoonJS = dialog.GetExportType() == ProjectExportDialog::CocoonJS;
bool exportForCordova = dialog.GetExportType() == ProjectExportDialog::Cordova;
if (dialog.GetExportType() == ProjectExportDialog::Cocos2d)
{
ExportWholeCocos2dProject(project, dialog.GetExportDir());
}
else
{
bool exportForCordova = dialog.GetExportType() == ProjectExportDialog::PixiCordova;
ExportWholePixiProject(project, dialog.GetExportDir(), dialog.RequestMinify(),
exportForCordova);
}
ExportWholeProject(project, dialog.GetExportDir(), dialog.RequestMinify(),
exportForCocoonJS, exportForCordova);
#else
gd::LogError("BAD USE: Exporter::ShowProjectExportDialog is not available.");
#endif
}
bool Exporter::ExportWholeProject(gd::Project & project, gd::String exportDir,
bool minify, bool exportForCocoonJS, bool exportForCordova)
bool Exporter::ExportWholePixiProject(gd::Project & project, gd::String exportDir,
bool minify, bool exportForCordova)
{
ExporterHelper helper(fs);
auto exportProject = [this, &project, &minify,
&exportForCocoonJS, &exportForCordova](gd::String exportDir)
&exportForCordova, &helper](gd::String exportDir)
{
wxProgressDialog * progressDialogPtr = NULL;
#if !defined(GD_NO_WX_GUI)
wxProgressDialog progressDialog(_("Export in progress ( 1/2 )"), _("Exporting the project..."));
progressDialogPtr = &progressDialog;
#endif
//Prepare the export directory
fs.MkDir(exportDir);
fs.ClearDir(exportDir);
fs.MkDir(exportDir+"/libs");
fs.MkDir(exportDir+"/pixi-renderers");
fs.MkDir(exportDir+"/Extensions");
std::vector<gd::String> includesFiles;
if (exportForCocoonJS)
{
fs.MkDir(exportDir+"/libs/CocoonJS");
includesFiles.push_back("libs/CocoonJS/cocoon.min.js");
}
gd::Project exportedProject = project;
//Export the resources (before generating events as some resources filenames may be updated)
#if !defined(GD_NO_WX_GUI)
ExportResources(fs, exportedProject, exportDir, &progressDialog);
#else
ExportResources(fs, exportedProject, exportDir, NULL);
#endif
helper.ExportResources(fs, exportedProject, exportDir, progressDialogPtr);
#if !defined(GD_NO_WX_GUI)
progressDialog.SetTitle(_("Export in progress ( 2/2 )"));
@@ -460,32 +116,25 @@ bool Exporter::ExportWholeProject(gd::Project & project, gd::String exportDir,
#endif
//Export events
if ( !ExportEventsCode(exportedProject, fs.GetTempDir()+"/GDTemporaries/JSCodeTemp/", includesFiles) )
if ( !helper.ExportEventsCode(exportedProject, fs.GetTempDir()+"/GDTemporaries/JSCodeTemp/", includesFiles) )
{
gd::LogError(_("Error during exporting! Unable to export events:\n")+lastError);
return false;
}
helper.AddLibsInclude(true, false, includesFiles);
//Export source files
if ( !ExportExternalSourceFiles(exportedProject, fs.GetTempDir()+"/GDTemporaries/JSCodeTemp/", includesFiles) )
if ( !helper.ExportExternalSourceFiles(exportedProject, fs.GetTempDir()+"/GDTemporaries/JSCodeTemp/", includesFiles) )
{
gd::LogError(_("Error during exporting! Unable to export source files:\n")+lastError);
return false;
}
#if !defined(GD_NO_WX_GUI)
progressDialog.Update(60, _("Preparing the project..."));
#endif
//Strip the project (*after* generating events as the events may use stripped things like objects groups...)...
gd::ProjectStripper::StripProject(exportedProject);
#if !defined(GD_NO_WX_GUI)
progressDialog.Update(70, _("Exporting files..."));
#endif
//...and export it
ExportToJSON(fs, exportedProject, fs.GetTempDir() + "/GDTemporaries/JSCodeTemp/data.js",
helper.ExportToJSON(fs, exportedProject, fs.GetTempDir() + "/GDTemporaries/JSCodeTemp/data.js",
"gdjs.projectData");
includesFiles.push_back(fs.GetTempDir()+"/GDTemporaries/JSCodeTemp/data.js");
@@ -494,59 +143,19 @@ bool Exporter::ExportWholeProject(gd::Project & project, gd::String exportDir,
#endif
//Copy all dependencies and the index (or metadata) file.
gd::String additionalSpec = exportForCocoonJS ? "{forceFullscreen:true}" : "";
ExportIncludesAndLibs(includesFiles, exportDir, minify);
helper.RemoveIncludes(false, true, includesFiles);
helper.ExportIncludesAndLibs(includesFiles, exportDir, minify);
gd::String source = exportForCordova ?
"./JsPlatform/Runtime/Cordova/www/index.html" :
"./JsPlatform/Runtime/index.html";
if (!ExportIndexFile(source, exportDir, includesFiles, additionalSpec))
if (!helper.ExportPixiIndexFile(source, exportDir, includesFiles, ""))
{
gd::LogError(_("Error during export:\n") + lastError);
return false;
}
//Exporting for online upload requires to zip the whole game.
if (exportForCocoonJS)
{
#if !defined(GD_NO_WX_GUI)
progressDialog.Update(90, _("Creating the zip file..."));
//Getting all the files to includes in the directory
wxArrayString files;
wxDir::GetAllFiles(exportDir, &files);
wxString zipTempName = fs.GetTempDir()+"/GDTemporaries/zipped_"+gd::String::From(&project)+".zip";
wxFFileOutputStream out(zipTempName);
wxZipOutputStream zip(out);
for(std::size_t i = 0; i < files.size(); ++i)
{
wxFileName filename(files[i]);
filename.MakeRelativeTo(exportDir);
wxFileInputStream file(files[i]);
if ( file.IsOk() )
{
zip.PutNextEntry(filename.GetFullPath());
zip.Write(file);
}
}
if ( !zip.Close() || !out.Close() )
gd::LogWarning(_("Unable to finalize the creation of the zip file!\n\nThe exported project won't be put in a zip file."));
else
{
progressDialog.Update(95, _("Cleaning files..."));
fs.ClearDir(exportDir);
fs.CopyFile(zipTempName, exportDir+"/packaged_game.zip");
wxRemoveFile(zipTempName);
}
#else
gd::LogError("BAD USE: Trying to export to a zip file, but this feature is not available when wxWidgets support is disabled.");
#endif
}
return true;
};
@@ -554,7 +163,7 @@ bool Exporter::ExportWholeProject(gd::Project & project, gd::String exportDir,
{
//Prepare the export directory
fs.MkDir(exportDir);
if (!ExportCordovaConfigFile(project, exportDir))
if (!helper.ExportCordovaConfigFile(project, exportDir))
return false;
if (!exportProject(exportDir + "/www"))
@@ -566,12 +175,7 @@ bool Exporter::ExportWholeProject(gd::Project & project, gd::String exportDir,
//Finished!
#if !defined(GD_NO_WX_GUI)
if ( exportForCocoonJS )
{
CocoonJSUploadDialog uploadDialog(NULL, exportDir+wxString(wxFileName::GetPathSeparator())+"packaged_game.zip");
uploadDialog.ShowModal();
}
else if ( exportForCordova )
if ( exportForCordova )
{
CordovaPackageDialog packageDialog(NULL, exportDir);
packageDialog.ShowModal();
@@ -589,44 +193,86 @@ bool Exporter::ExportWholeProject(gd::Project & project, gd::String exportDir,
return true;
}
bool Exporter::ExportWholeCocos2dProject(gd::Project & project, gd::String exportDir)
{
ExporterHelper helper(fs);
wxProgressDialog * progressDialogPtr = NULL;
#if !defined(GD_NO_WX_GUI)
wxProgressDialog progressDialog(_("Export in progress ( 1/2 )"), _("Exporting the project..."));
progressDialogPtr = &progressDialog;
#endif
//Prepare the export directory
fs.MkDir(exportDir);
fs.ClearDir(exportDir);
std::vector<gd::String> includesFiles;
gd::Project exportedProject = project;
//Export the resources (before generating events as some resources filenames may be updated)
helper.ExportResources(fs, exportedProject, exportDir + "/res", progressDialogPtr);
#if !defined(GD_NO_WX_GUI)
progressDialog.SetTitle(_("Export in progress ( 2/2 )"));
progressDialog.Update(50, _("Exporting events..."));
#endif
//Export events
if ( !helper.ExportEventsCode(exportedProject, fs.GetTempDir()+"/GDTemporaries/JSCodeTemp/", includesFiles) )
{
gd::LogError(_("Error during exporting! Unable to export events:\n")+lastError);
return false;
}
helper.AddLibsInclude(false, true, includesFiles);
//Export source files
if ( !helper.ExportExternalSourceFiles(exportedProject, fs.GetTempDir()+"/GDTemporaries/JSCodeTemp/", includesFiles) )
{
gd::LogError(_("Error during exporting! Unable to export source files:\n")+lastError);
return false;
}
//Strip the project (*after* generating events as the events may use stripped things like objects groups...)...
gd::ProjectStripper::StripProject(exportedProject);
//...and export it
helper.ExportToJSON(fs, exportedProject, fs.GetTempDir() + "/GDTemporaries/JSCodeTemp/data.js",
"gdjs.projectData");
includesFiles.push_back(fs.GetTempDir()+"/GDTemporaries/JSCodeTemp/data.js");
#if !defined(GD_NO_WX_GUI)
progressDialog.Update(80, _("Exporting files..."));
#endif
//Copy all dependencies and the index (or metadata) file.
helper.RemoveIncludes(true, false, includesFiles);
helper.ExportIncludesAndLibs(includesFiles, exportDir + "/src", false);
gd::String source = "./JsPlatform/Runtime/index.html";
if (!helper.ExportCocos2dFiles(project, exportDir, includesFiles))
{
gd::LogError(_("Error during export:\n") + lastError);
return false;
}
//Finished!
#if !defined(GD_NO_WX_GUI)
//TODO: Factor/update message?
if ( wxMessageBox(_("Compilation achieved. Do you want to open the folder where the project has been compiled\?"),
_("Compilation finished"), wxYES_NO) == wxYES )
{
gd::ShowFolder(exportDir);
}
#endif
return true;
}
gd::String Exporter::GetProjectExportButtonLabel()
{
return _("Export to the web");
}
#if !defined(GD_NO_WX_GUI)
gd::String Exporter::GetNodeExecutablePath()
{
std::vector<gd::String> guessPaths;
wxString userPath;
if ( wxConfigBase::Get()->Read("Paths/Node" , &userPath) && !userPath.empty() )
guessPaths.push_back(userPath);
else
{
//Try some common paths.
#if defined(WINDOWS)
guessPaths.push_back("C:/Program Files/nodejs/node.exe");
guessPaths.push_back("C:/Program Files (x86)/nodejs/node.exe");
#elif defined(LINUX) || defined(MACOS)
guessPaths.push_back("/usr/bin/env/nodejs");
guessPaths.push_back("/usr/bin/nodejs");
guessPaths.push_back("/usr/local/bin/nodejs");
guessPaths.push_back("/usr/bin/env/node");
guessPaths.push_back("/usr/bin/node");
guessPaths.push_back("/usr/local/bin/node");
#else
#warning Please complete this so as to return a path to the Node executable.
#endif
}
for (size_t i = 0;i<guessPaths.size();++i)
{
if ( wxFileExists(guessPaths[i]) )
return guessPaths[i];
}
return "";
}
#endif
}

View File

@@ -13,7 +13,6 @@ namespace gd { class Project; }
namespace gd { class Layout; }
namespace gd { class ExternalLayout; }
namespace gd { class AbstractFileSystem; }
class wxProgressDialog;
namespace gdjs
{
@@ -46,7 +45,7 @@ public:
* \param exportDir The directory where the preview must be created.
* \return true if export was successful.
*/
bool ExportLayoutForPreview(gd::Project & project, gd::Layout & layout, gd::String exportDir);
bool ExportLayoutForPixiPreview(gd::Project & project, gd::Layout & layout, gd::String exportDir);
/**
* \brief Create a preview for the specified external layout and layout.
@@ -57,135 +56,30 @@ public:
* \param exportDir The directory where the preview must be created.
* \return true if export was successful.
*/
bool ExportExternalLayoutForPreview(gd::Project & project, gd::Layout & layout,
bool ExportExternalLayoutForPixiPreview(gd::Project & project, gd::Layout & layout,
gd::ExternalLayout & externalLayout, gd::String exportDir);
/**
* \brief Export the specified project.
* \brief Export the specified project, using Pixi.js.
*
* Called by ShowProjectExportDialog if the user clicked on Ok.
*/
bool ExportWholeProject(gd::Project & project, gd::String exportDir,
bool minify, bool exportForCocoonJS, bool exportForCordova);
bool ExportWholePixiProject(gd::Project & project, gd::String exportDir,
bool minify, bool exportForCordova);
/**
* \brief Export the specified project, using Cocos2d.
*
* Called by ShowProjectExportDialog if the user clicked on Ok.
*/
bool ExportWholeCocos2dProject(gd::Project & project, gd::String exportDir);
/**
* \brief Return the error that occurred during the last export.
*/
const gd::String & GetLastError() const { return lastError; };
#if !defined(GD_NO_WX_GUI)
/**
* \brief Try to locate the Node.js executable. (Node must be installed in a standard folder).
* \return An empty string if not found, a full path to the node executable otherwise.
*/
static gd::String GetNodeExecutablePath();
#endif
private:
/**
* \brief Export a project to JSON
*
* \param fs The abstract file system to use to write the file
* \param project The project to be exported.
* \param filename The filename where export the project
* \param wrapIntoVariable If not empty, the resulting json will be wrapped in this javascript
* variable allowing to use it as a classical javascript object.
* \return Empty string if everthing is ok, description of the error otherwise.
*/
static gd::String ExportToJSON(gd::AbstractFileSystem &fs, const gd::Project &project, gd::String filename,
gd::String wrapIntoVariable);
/**
* \brief Copy all the resources of the project to to the export directory, updating the resources filenames.
*
* \param fs The abstract file system to use
* \param project The project with resources to be exported.
* \param exportDir The directory where the preview must be created.
* \param progressDlg Optional wxProgressDialog which will be updated with the progress.
*/
static void ExportResources(gd::AbstractFileSystem & fs, gd::Project & project, gd::String exportDir,
wxProgressDialog * progressDlg = NULL);
/**
* \brief Copy all the includes files and the standard libraries files to the export directory.
*
* The includes files are also modified so as to be relative to the export directory
* ( Files with absolute filenames are copied into the export directory and their path are stripped ).
*
* \param includesFiles A vector with filenames to be copied.
* \param exportDir The directory where the preview must be created.
* \param minify If true, the includes files must be merged into one file using Google Closure Compiler.
* ( includesFiles parameter will be updated with the new filename )
*/
bool ExportIncludesAndLibs(std::vector<gd::String> & includesFiles, gd::String exportDir, bool minify);
/**
* \brief Generate the events JS code, and save them to the export directory.
*
* Files are named "codeX.js", X being the number of the layout in the project.
* \param project The project with resources to be exported.
* \param outputDir The directory where the events code must be generated.
* \param includesFiles A reference to a vector that will be filled with JS files to be exported along with the project.
* ( including "codeX.js" files ).
*/
bool ExportEventsCode(gd::Project & project, gd::String outputDir, std::vector<gd::String> & includesFiles);
/**
* \brief Copy the external source files used by the game into the export directory, and add them into files
* to be included.
*
* Files are named "ext-codeX.js", X being the index of the external source file in the project.
* \param project The project with resources to be exported.
* \param outputDir The directory where the events code must be generated.
* \param includesFiles A reference to a vector that will be filled with JS files to be exported along with the project.
* (including "ext-codeX.js" files).
*/
bool ExportExternalSourceFiles(gd::Project & project, gd::String outputDir, std::vector<gd::String> & includesFiles);
/**
* \brief Generate the standard index file and save it to the export directory.
*
* The includes files must be relative to the export directory.
*
* \param project The project with layouts to be exported.
* \param source The file to be used as a template for the final file.
* \param exportDir The directory where the preview must be created.
* \param includesFiles The JS files to be included in the HTML file. Order is important.
* \param additionalSpec JSON string that will be passed to the gdjs.RuntimeGame object.
*/
bool ExportIndexFile(gd::String source, gd::String exportDir, const std::vector<gd::String> & includesFiles, gd::String additionalSpec = "");
/**
* \brief Replace the annotations in a index.html file by the specified content.
*
* \param indexFileContent The source of the index.html file.
* \param customCss "<!-- GDJS_CUSTOM_STYLE -->" will be replaced by the content of the string.
* \param customHtml "<!-- GDJS_CUSTOM_HTML -->" will be replaced by the content of the string.
* \param exportDir The directory where the project must be generated.
* \param codeFilesIncludes "<!-- GDJS_CODE_FILES -->" will be replaced by HTML tags to include the filenames contained inside the vector.
* \param additionalSpec The string "GDJS_ADDITIONAL_SPEC" surrounded by comments marks will be replaced by the content of the string.
*/
bool CompleteIndexFile(gd::String & indexFileContent, gd::String customCss, gd::String customHtml, gd::String exportDir, const std::vector<gd::String> & includesFiles, gd::String additionalSpec);
/**
* \brief Generate the Cordova configuration file and save it to the export directory.
*
* \param project The project to be used to generate the configuration file.
* \param exportDir The directory where the config.xml must be created.
*/
bool ExportCordovaConfigFile(const gd::Project & project, gd::String exportDir);
/**
* \brief Launch all export methods to generate a complete, stand-alone game for previewing.
*
* \param layout The layout to be previewed.
* \param exportDir The directory where the preview must be created.
* \param additionalSpec Any additional parameters to be passed to the gdjs.RuntimeGame.
* \return true if export was successful.
*/
bool ExportLayoutForPreview(gd::Project & project, gd::Layout & layout, gd::String exportDir, gd::String additionalSpec);
gd::AbstractFileSystem & fs; ///< The abstract file system to be used for exportation.
gd::String lastError; ///< The last error that occurred.
};

View File

@@ -0,0 +1,512 @@
/*
* GDevelop JS Platform
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
* This project is released under the MIT License.
*/
#include <algorithm>
#include <sstream>
#include <fstream>
#include <streambuf>
#include <string>
#if !defined(GD_NO_WX_GUI)
#include <wx/filename.h>
#include <wx/dir.h>
#include <wx/msgdlg.h>
#include <wx/config.h>
#include <wx/progdlg.h>
#include <wx/zipstrm.h>
#include <wx/wfstream.h>
#endif
#include "GDCore/Tools/Localization.h"
#include "GDCore/Tools/Log.h"
#include "GDCore/TinyXml/tinyxml.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/ExternalLayout.h"
#include "GDCore/IDE/AbstractFileSystem.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Project/ExternalEvents.h"
#include "GDCore/Project/SourceFile.h"
#include "GDCore/IDE/wxTools/ShowFolder.h"
#include "GDCore/IDE/Project/ProjectResourcesCopier.h"
#include "GDCore/IDE/ProjectStripper.h"
#include "GDCore/CommonTools.h"
#include "GDJS/Events/CodeGeneration/EventsCodeGenerator.h"
#include "GDJS/IDE/ExporterHelper.h"
#undef CopyFile //Disable an annoying macro
namespace gdjs
{
//Nice tools functions
static void InsertUnique(std::vector<gd::String> & container, gd::String str)
{
if ( std::find(container.begin(), container.end(), str) == container.end() )
container.push_back(str);
}
static void GenerateFontsDeclaration(gd::AbstractFileSystem & fs, const gd::String & outputDir, gd::String & css, gd::String & html)
{
std::vector<gd::String> ttfFiles = fs.ReadDir(outputDir, ".TTF");
for(std::size_t i = 0; i<ttfFiles.size();++i) {
gd::String relativeFile = ttfFiles[i];
fs.MakeRelative(relativeFile, outputDir);
css += "@font-face{ font-family : \"gdjs_font_";
css += relativeFile;
css += "\"; src : url('";
css += relativeFile;
css +="') format('truetype'); }";
html += "<div style=\"font-family: 'gdjs_font_";
html += relativeFile;
html += "';\">.</div>";
}
}
bool ExporterHelper::ExportLayoutForPixiPreview(gd::Project & project, gd::Layout & layout, gd::String exportDir, gd::String additionalSpec)
{
fs.MkDir(exportDir);
fs.ClearDir(exportDir);
std::vector<gd::String> includesFiles;
gd::Project exportedProject = project;
//Export resources (*before* generating events as some resources filenames may be updated)
ExportResources(fs, exportedProject, exportDir);
//Generate events code
if ( !ExportEventsCode(exportedProject, fs.GetTempDir()+"/GDTemporaries/JSCodeTemp/", includesFiles) )
return false;
AddLibsInclude(true, false, includesFiles);
//Export source files
if ( !ExportExternalSourceFiles(exportedProject, fs.GetTempDir()+"/GDTemporaries/JSCodeTemp/", includesFiles) )
{
gd::LogError(_("Error during exporting! Unable to export source files:\n")+lastError);
return false;
}
//Strip the project (*after* generating events as the events may use stripped things (objects groups...))
gd::ProjectStripper::StripProject(exportedProject);
exportedProject.SetFirstLayout(layout.GetName());
//Export the project
ExportToJSON(fs, exportedProject, fs.GetTempDir() + "/GDTemporaries/JSCodeTemp/data.js",
"gdjs.projectData");
includesFiles.push_back(fs.GetTempDir()+"/GDTemporaries/JSCodeTemp/data.js");
//Copy all the dependencies
RemoveIncludes(false, true, includesFiles);
ExportIncludesAndLibs(includesFiles, exportDir, false);
//Create the index file
if (!ExportPixiIndexFile("./JsPlatform/Runtime/index.html", exportDir, includesFiles, additionalSpec))
return false;
return true;
}
gd::String ExporterHelper::ExportToJSON(gd::AbstractFileSystem &fs, const gd::Project &project, gd::String filename,
gd::String wrapIntoVariable)
{
fs.MkDir(fs.DirNameFrom(filename));
//Save the project to JSON
gd::SerializerElement rootElement;
project.SerializeTo(rootElement);
gd::String output = gd::Serializer::ToJSON(rootElement);
if (!wrapIntoVariable.empty()) output = wrapIntoVariable + " = " + output + ";";
if (!fs.WriteToFile(filename, output))
return "Unable to write "+filename;
return "";
}
bool ExporterHelper::ExportPixiIndexFile(gd::String source, gd::String exportDir, const std::vector<gd::String> & includesFiles, gd::String additionalSpec)
{
gd::String str = fs.ReadFile(source);
//Generate custom declarations for font resources
gd::String customCss;
gd::String customHtml;
GenerateFontsDeclaration(fs, exportDir, customCss, customHtml);
//Generate the file
if (!CompleteIndexFile(str, customCss, customHtml, exportDir, includesFiles, additionalSpec))
return false;
//Write the index.html file
if (!fs.WriteToFile(exportDir + "/index.html", str))
{
lastError = "Unable to write index file.";
return false;
}
return true;
}
bool ExporterHelper::ExportCordovaConfigFile(const gd::Project & project, gd::String exportDir)
{
gd::String str = fs.ReadFile("./JsPlatform/Runtime/Cordova/config.xml")
.FindAndReplace("GDJS_PROJECTNAME", project.GetName())
.FindAndReplace("GDJS_PACKAGENAME", project.GetPackageName())
.FindAndReplace("GDJS_ORIENTATION", "default");
if (!fs.WriteToFile(exportDir + "/config.xml", str))
{
lastError = "Unable to write configuration file.";
return false;
}
return true;
}
bool ExporterHelper::ExportCocos2dFiles(const gd::Project & project, gd::String exportDir, const std::vector<gd::String> & includesFiles)
{
if (!fs.CopyFile("./JsPlatform/Runtime/Cocos2d/main.js", exportDir + "/main.js"))
{
lastError = "Unable to write Cocos2d main.js file.";
return false;
}
if (!fs.CopyFile("./JsPlatform/Runtime/Cocos2d/index.html", exportDir + "/index.html"))
{
lastError = "Unable to write Cocos2d index.html file.";
return false;
}
if (!fs.CopyFile("./JsPlatform/Runtime/Cocos2d/cocos2d-js-v3.10.js", exportDir + "/cocos2d-js-v3.10.js"))
{
lastError = "Unable to write Cocos2d cocos2d-js-v3.10.js file.";
return false;
}
gd::String includeFilesStr = "";
bool first = true;
for(auto & file : includesFiles)
{
if ( !fs.FileExists(exportDir + "/src/" + file) )
{
std::cout << "Warning: Unable to find " << exportDir + "/" + file << "." << std::endl;
continue;
}
includeFilesStr += gd::String(first ? "" : ", ") + "\"src/" + file + "\"\n";
first = false;
}
gd::String str = fs.ReadFile("./JsPlatform/Runtime/Cocos2d/project.json")
.FindAndReplace("// GDJS_INCLUDE_FILES", includeFilesStr);
if (!fs.WriteToFile(exportDir + "/project.json", str))
{
lastError = "Unable to write Cocos2d project.json file.";
return false;
}
return true;
}
bool ExporterHelper::CompleteIndexFile(gd::String & str, gd::String customCss, gd::String customHtml, gd::String exportDir, const std::vector<gd::String> & includesFiles, gd::String additionalSpec)
{
if (additionalSpec.empty()) additionalSpec = "{}";
gd::String codeFilesIncludes;
for (std::vector<gd::String>::const_iterator it = includesFiles.begin(); it != includesFiles.end(); ++it)
{
if ( !fs.FileExists(exportDir + "/" + *it) )
{
std::cout << "Warning: Unable to find " << exportDir+"/"+*it << "." << std::endl;
continue;
}
gd::String relativeFile = exportDir+"/"+*it;
fs.MakeRelative(relativeFile, exportDir);
codeFilesIncludes += "\t<script src=\""+relativeFile+"\"></script>\n";
}
str = str.FindAndReplace("/* GDJS_CUSTOM_STYLE */", customCss)
.FindAndReplace("<!-- GDJS_CUSTOM_HTML -->", customHtml)
.FindAndReplace("<!-- GDJS_CODE_FILES -->", codeFilesIncludes)
.FindAndReplace("{}/*GDJS_ADDITIONAL_SPEC*/", additionalSpec);
return true;
}
void ExporterHelper::AddLibsInclude(bool pixiRenderers, bool cocosRenderers, std::vector<gd::String> & includesFiles)
{
if (pixiRenderers)
{
InsertUnique(includesFiles, "pixi-renderers/pixi.js");
InsertUnique(includesFiles, "pixi-renderers/runtimegame-pixi-renderer.js");
InsertUnique(includesFiles, "pixi-renderers/runtimescene-pixi-renderer.js");
InsertUnique(includesFiles, "pixi-renderers/layer-pixi-renderer.js");
InsertUnique(includesFiles, "pixi-renderers/pixi-image-manager.js");
InsertUnique(includesFiles, "pixi-renderers/spriteruntimeobject-pixi-renderer.js");
InsertUnique(includesFiles, "pixi-renderers/loadingscreen-pixi-renderer.js");
InsertUnique(includesFiles, "howler-sound-manager/howler.min.js");
InsertUnique(includesFiles, "howler-sound-manager/howler-sound-manager.js");
}
if (cocosRenderers)
{
InsertUnique(includesFiles, "cocos-renderers/cocos-director-manager.js");
InsertUnique(includesFiles, "cocos-renderers/cocos-image-manager.js");
InsertUnique(includesFiles, "cocos-renderers/cocos-tools.js");
InsertUnique(includesFiles, "cocos-renderers/layer-cocos-renderer.js");
InsertUnique(includesFiles, "cocos-renderers/loadingscreen-cocos-renderer.js");
InsertUnique(includesFiles, "cocos-renderers/runtimegame-cocos-renderer.js");
InsertUnique(includesFiles, "cocos-renderers/runtimescene-cocos-renderer.js");
InsertUnique(includesFiles, "cocos-renderers/spriteruntimeobject-cocos-renderer.js");
InsertUnique(includesFiles, "cocos-sound-manager/cocos-sound-manager.js");
}
}
void ExporterHelper::RemoveIncludes(bool pixiRenderers, bool cocosRenderers, std::vector<gd::String> & includesFiles)
{
if (pixiRenderers)
{
for (auto it = includesFiles.begin(); it != includesFiles.end(); )
{
if (it->find("pixi-renderer") != gd::String::npos)
includesFiles.erase(it++);
else
++it;
}
}
if (cocosRenderers)
{
for (auto it = includesFiles.begin(); it != includesFiles.end(); )
{
if (it->find("cocos-renderer") != gd::String::npos)
includesFiles.erase(it++);
else
++it;
}
}
}
bool ExporterHelper::ExportEventsCode(gd::Project & project, gd::String outputDir, std::vector<gd::String> & includesFiles)
{
fs.MkDir(outputDir);
//First, do not forget common includes (they must be included before events generated code files).
InsertUnique(includesFiles, "libs/jshashtable.js");
InsertUnique(includesFiles, "gd.js");
InsertUnique(includesFiles, "libs/hshg.js");
InsertUnique(includesFiles, "inputmanager.js");
InsertUnique(includesFiles, "timemanager.js");
InsertUnique(includesFiles, "runtimeobject.js");
InsertUnique(includesFiles, "profiler.js");
InsertUnique(includesFiles, "runtimescene.js");
InsertUnique(includesFiles, "scenestack.js");
InsertUnique(includesFiles, "polygon.js");
InsertUnique(includesFiles, "force.js");
InsertUnique(includesFiles, "layer.js");
InsertUnique(includesFiles, "timer.js");
InsertUnique(includesFiles, "runtimegame.js");
InsertUnique(includesFiles, "variable.js");
InsertUnique(includesFiles, "variablescontainer.js");
InsertUnique(includesFiles, "eventscontext.js");
InsertUnique(includesFiles, "runtimebehavior.js");
InsertUnique(includesFiles, "spriteruntimeobject.js");
//Common includes for events only.
InsertUnique(includesFiles, "events-tools/commontools.js");
InsertUnique(includesFiles, "events-tools/runtimescenetools.js");
InsertUnique(includesFiles, "events-tools/inputtools.js");
InsertUnique(includesFiles, "events-tools/objecttools.js");
InsertUnique(includesFiles, "events-tools/cameratools.js");
InsertUnique(includesFiles, "events-tools/soundtools.js");
InsertUnique(includesFiles, "events-tools/storagetools.js");
InsertUnique(includesFiles, "events-tools/stringtools.js");
InsertUnique(includesFiles, "events-tools/windowtools.js");
InsertUnique(includesFiles, "events-tools/networktools.js");
for (std::size_t i = 0;i<project.GetLayoutsCount();++i)
{
std::set<gd::String> eventsIncludes;
gd::Layout & exportedLayout = project.GetLayout(i);
gd::String eventsOutput = EventsCodeGenerator::GenerateSceneEventsCompleteCode(project, exportedLayout,
exportedLayout.GetEvents(), eventsIncludes, false /*Export for edittime*/);
gd::String filename = outputDir+"code"+gd::String::From(i)+".js";
//Export the code
if (fs.WriteToFile(filename, eventsOutput))
{
for ( std::set<gd::String>::iterator include = eventsIncludes.begin() ; include != eventsIncludes.end(); ++include )
InsertUnique(includesFiles, *include);
InsertUnique(includesFiles, filename);
}
else {
lastError = _("Unable to write ") + filename;
return false;
}
}
return true;
}
bool ExporterHelper::ExportExternalSourceFiles(gd::Project & project, gd::String outputDir, std::vector<gd::String> & includesFiles)
{
const std::vector < std::shared_ptr<gd::SourceFile> > & allFiles = project.GetAllSourceFiles();
for (std::size_t i = 0;i<allFiles.size();++i)
{
if (allFiles[i] == std::shared_ptr<gd::SourceFile>() ) continue;
if (allFiles[i]->GetLanguage() != "Javascript" ) continue;
gd::SourceFile & file = *allFiles[i];
gd::String filename = file.GetFileName();
fs.MakeAbsolute(filename, fs.DirNameFrom(project.GetProjectFile()));
gd::String outFilename = "ext-code"+gd::String::From(i)+".js";
if (!fs.CopyFile(filename, outputDir+outFilename))
gd::LogWarning(_("Could not copy external file") + filename);
InsertUnique(includesFiles, outputDir+outFilename);
}
return true;
}
bool ExporterHelper::ExportIncludesAndLibs(std::vector<gd::String> & includesFiles, gd::String exportDir, bool minify)
{
#if !defined(GD_NO_WX_GUI)
//Includes files :
if ( minify )
{
gd::String nodeExec = GetNodeExecutablePath();
if ( nodeExec.empty() || !fs.FileExists(nodeExec) )
{
std::cout << "Node.js executable not found." << std::endl;
gd::LogWarning(_("The exported script could not be minified: Please check that you installed Node.js on your system."));
minify = false;
}
else
{
gd::String jsPlatformDir = wxGetCwd()+"/JsPlatform/";
gd::String cmd = nodeExec+" \""+jsPlatformDir+"Tools/uglify-js/bin/uglifyjs\" ";
gd::String allJsFiles;
for ( std::vector<gd::String>::iterator include = includesFiles.begin() ; include != includesFiles.end(); ++include )
{
if ( fs.FileExists(jsPlatformDir+"Runtime/"+*include) )
allJsFiles += "\""+jsPlatformDir+"Runtime/"+*include+"\" ";
else if ( fs.FileExists(jsPlatformDir+"Runtime/Extensions/"+*include) )
allJsFiles += "\""+jsPlatformDir+"Runtime/Extensions/"+*include+"\" ";
else if ( fs.FileExists(*include) )
allJsFiles += "\""+*include+"\" ";
}
cmd += allJsFiles;
cmd += "-o \""+exportDir+"/code.js\"";
wxArrayString output;
wxArrayString errors;
long res = wxExecute(cmd, output, errors);
if ( res != 0 )
{
std::cout << "Execution of \"UglifyJS\" failed (Command line : " << cmd << ")." << std::endl;
std::cout << "Output: ";
for (size_t i = 0;i<output.size();++i)
std::cout << output[i] << std::endl;
for (size_t i = 0;i<errors.size();++i)
std::cout << errors[i] << std::endl;
gd::LogWarning(_("The exported script could not be minified.\n\nMay be an extension is triggering this error: Try to contact the developer if you think it is the case."));
minify = false;
}
else
{
includesFiles.clear();
InsertUnique(includesFiles, "code.js");
return true;
}
}
}
#else
minify = false;
#endif
//If the closure compiler failed or was not request, simply copy all the include files.
if ( !minify )
{
for ( std::vector<gd::String>::iterator include = includesFiles.begin() ; include != includesFiles.end(); ++include )
{
if ( fs.FileExists("./JsPlatform/Runtime/"+*include) )
{
gd::String path = fs.DirNameFrom(exportDir+"/"+*include);
if ( !fs.DirExists(path) ) fs.MkDir(path);
fs.CopyFile("./JsPlatform/Runtime/"+*include, exportDir+"/"+*include);
//Ok, the filename is relative to the export dir.
}
else if ( fs.FileExists("./JsPlatform/Runtime/Extensions/"+*include) )
{
gd::String path = fs.DirNameFrom(exportDir+"/Extensions/"+*include);
if ( !fs.DirExists(path) ) fs.MkDir(path);
fs.CopyFile("./JsPlatform/Runtime/Extensions/"+*include, exportDir+"/Extensions/"+*include);
*include = "Extensions/"+*include; //Ensure filename is relative to the export dir.
}
else if ( fs.FileExists(*include) )
{
fs.CopyFile(*include, exportDir+"/"+fs.FileNameFrom(*include));
*include = fs.FileNameFrom(*include); //Ensure filename is relative to the export dir.
}
else
{
std::cout << "Could not copy include file " << *include << " (File not found)." << std::endl;
}
}
}
return true;
}
void ExporterHelper::ExportResources(gd::AbstractFileSystem & fs, gd::Project & project, gd::String exportDir, wxProgressDialog * progressDialog)
{
gd::ProjectResourcesCopier::CopyAllResourcesTo(project, fs, exportDir, true, progressDialog, false, false);
}
#if !defined(GD_NO_WX_GUI)
gd::String ExporterHelper::GetNodeExecutablePath()
{
std::vector<gd::String> guessPaths;
wxString userPath;
if ( wxConfigBase::Get()->Read("Paths/Node" , &userPath) && !userPath.empty() )
guessPaths.push_back(userPath);
else
{
//Try some common paths.
#if defined(WINDOWS)
guessPaths.push_back("C:/Program Files/nodejs/node.exe");
guessPaths.push_back("C:/Program Files (x86)/nodejs/node.exe");
#elif defined(LINUX) || defined(MACOS)
guessPaths.push_back("/usr/bin/env/nodejs");
guessPaths.push_back("/usr/bin/nodejs");
guessPaths.push_back("/usr/local/bin/nodejs");
guessPaths.push_back("/usr/bin/env/node");
guessPaths.push_back("/usr/bin/node");
guessPaths.push_back("/usr/local/bin/node");
#else
#warning Please complete this so as to return a path to the Node executable.
#endif
}
for (size_t i = 0;i<guessPaths.size();++i)
{
if ( wxFileExists(guessPaths[i]) )
return guessPaths[i];
}
return "";
}
#endif
}

View File

@@ -0,0 +1,171 @@
/*
* GDevelop JS Platform
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
* This project is released under the MIT License.
*/
#ifndef EXPORTER_HELPER_H
#define EXPORTER_HELPER_H
#include <vector>
#include <string>
#include <set>
#include "GDCore/IDE/ProjectExporter.h"
namespace gd { class Project; }
namespace gd { class Layout; }
namespace gd { class ExternalLayout; }
namespace gd { class AbstractFileSystem; }
class wxProgressDialog;
namespace gdjs
{
/**
* \brief Export a project or a layout to a playable HTML5/Javascript based game.
*/
class ExporterHelper
{
public:
ExporterHelper(gd::AbstractFileSystem & fileSystem) : fs(fileSystem) {};
virtual ~ExporterHelper() {};
/**
* \brief Return the error that occurred during the last export.
*/
const gd::String & GetLastError() const { return lastError; };
#if !defined(GD_NO_WX_GUI)
/**
* \brief Try to locate the Node.js executable. (Node must be installed in a standard folder).
* \return An empty string if not found, a full path to the node executable otherwise.
*/
static gd::String GetNodeExecutablePath();
#endif
/**
* \brief Export a project to JSON
*
* \param fs The abstract file system to use to write the file
* \param project The project to be exported.
* \param filename The filename where export the project
* \param wrapIntoVariable If not empty, the resulting json will be wrapped in this javascript
* variable allowing to use it as a classical javascript object.
* \return Empty string if everthing is ok, description of the error otherwise.
*/
static gd::String ExportToJSON(gd::AbstractFileSystem &fs, const gd::Project &project, gd::String filename,
gd::String wrapIntoVariable);
/**
* \brief Copy all the resources of the project to to the export directory, updating the resources filenames.
*
* \param fs The abstract file system to use
* \param project The project with resources to be exported.
* \param exportDir The directory where the preview must be created.
* \param progressDlg Optional wxProgressDialog which will be updated with the progress.
*/
static void ExportResources(gd::AbstractFileSystem & fs, gd::Project & project, gd::String exportDir,
wxProgressDialog * progressDlg = NULL);
/**
* \brief Add libraries files from Pixi.js or Cocos2d to the list of includes.
*/
void AddLibsInclude(bool pixiRenderers, bool cocosRenderers, std::vector<gd::String> & includesFiles);
/**
* \brief Remove include files that are Pixi or Cocos2d renderers.
*/
void RemoveIncludes(bool pixiRenderers, bool cocosRenderers, std::vector<gd::String> & includesFiles);
/**
* \brief Copy all the includes files and the standard libraries files to the export directory.
*
* The includes files are also modified so as to be relative to the export directory
* ( Files with absolute filenames are copied into the export directory and their path are stripped ).
*
* \param includesFiles A vector with filenames to be copied.
* \param exportDir The directory where the preview must be created.
* \param minify If true, the includes files must be merged into one file using Google Closure Compiler.
* ( includesFiles parameter will be updated with the new filename )
*/
bool ExportIncludesAndLibs(std::vector<gd::String> & includesFiles, gd::String exportDir, bool minify);
/**
* \brief Generate the events JS code, and save them to the export directory.
*
* Files are named "codeX.js", X being the number of the layout in the project.
* \param project The project with resources to be exported.
* \param outputDir The directory where the events code must be generated.
* \param includesFiles A reference to a vector that will be filled with JS files to be exported along with the project.
* ( including "codeX.js" files ).
*/
bool ExportEventsCode(gd::Project & project, gd::String outputDir, std::vector<gd::String> & includesFiles);
/**
* \brief Copy the external source files used by the game into the export directory, and add them into files
* to be included.
*
* Files are named "ext-codeX.js", X being the index of the external source file in the project.
* \param project The project with resources to be exported.
* \param outputDir The directory where the events code must be generated.
* \param includesFiles A reference to a vector that will be filled with JS files to be exported along with the project.
* (including "ext-codeX.js" files).
*/
bool ExportExternalSourceFiles(gd::Project & project, gd::String outputDir, std::vector<gd::String> & includesFiles);
/**
* \brief Generate the standard index file and save it to the export directory.
*
* The includes files must be relative to the export directory.
*
* \param project The project with layouts to be exported.
* \param source The file to be used as a template for the final file.
* \param exportDir The directory where the preview must be created.
* \param includesFiles The JS files to be included in the HTML file. Order is important.
* \param additionalSpec JSON string that will be passed to the gdjs.RuntimeGame object.
*/
bool ExportPixiIndexFile(gd::String source, gd::String exportDir, const std::vector<gd::String> & includesFiles, gd::String additionalSpec = "");
/**
* \brief Replace the annotations in a index.html file by the specified content.
*
* \param indexFileContent The source of the index.html file.
* \param customCss "<!-- GDJS_CUSTOM_STYLE -->" will be replaced by the content of the string.
* \param customHtml "<!-- GDJS_CUSTOM_HTML -->" will be replaced by the content of the string.
* \param exportDir The directory where the project must be generated.
* \param codeFilesIncludes "<!-- GDJS_CODE_FILES -->" will be replaced by HTML tags to include the filenames contained inside the vector.
* \param additionalSpec The string "GDJS_ADDITIONAL_SPEC" surrounded by comments marks will be replaced by the content of the string.
*/
bool CompleteIndexFile(gd::String & indexFileContent, gd::String customCss, gd::String customHtml, gd::String exportDir, const std::vector<gd::String> & includesFiles, gd::String additionalSpec);
/**
* \brief Generate the Cordova configuration file and save it to the export directory.
*
* \param project The project to be used to generate the configuration file.
* \param exportDir The directory where the config.xml must be created.
*/
bool ExportCordovaConfigFile(const gd::Project & project, gd::String exportDir);
/**
* \brief Generate the base Cocos2d files.
*
* \param project The project to be used to generate the configuration file.
* \param exportDir The directory where the config.xml must be created.
*/
bool ExportCocos2dFiles(const gd::Project & project, gd::String exportDir, const std::vector<gd::String> & includesFiles);
/**
* \brief Launch all export methods to generate a complete, stand-alone game for previewing.
*
* \param layout The layout to be previewed.
* \param exportDir The directory where the preview must be created.
* \param additionalSpec Any additional parameters to be passed to the gdjs.RuntimeGame.
* \return true if export was successful.
*/
bool ExportLayoutForPixiPreview(gd::Project & project, gd::Layout & layout, gd::String exportDir, gd::String additionalSpec);
gd::AbstractFileSystem & fs; ///< The abstract file system to be used for exportation.
gd::String lastError; ///< The last error that occurred.
};
}
#endif // EXPORTER_HELPER_H

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<link rel="icon" type="image/GIF" href="res/favicon.ico"/>
<meta name="viewport" content="width=480, initial-scale=1">
<meta name="apple-mobile-web-app-capable" content="yes"/>
<meta name="full-screen" content="yes"/>
<meta name="screen-orientation" content="portrait"/>
<meta name="x5-fullscreen" content="true"/>
<meta name="360-fullscreen" content="true"/>
<style>
body, canvas, div {
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
-khtml-user-select: none;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
</style>
</head>
<body style="padding:0; margin: 0; background: #000;">
<script src="res/loading.js"></script>
<canvas id="gameCanvas" width="480" height="720"></canvas>
<script src="cocos2d-js-v3.10.js"></script>
<script cocos src="main.js"></script>
</body>
</html>

View File

@@ -0,0 +1,26 @@
cc.game.onStart = function(){
if(!cc.sys.isNative && document.getElementById("cocosLoading")) //If referenced loading.js, please remove it
document.body.removeChild(document.getElementById("cocosLoading"));
gdjs.registerObjects();
gdjs.registerBehaviors();
gdjs.registerGlobalCallbacks();
var game = new gdjs.RuntimeGame(gdjs.projectData, {});
// Pass true to enable retina display, disabled by default to improve performance
cc.view.enableRetina(false);
// Adjust viewport meta
cc.view.adjustViewPort(true);
// Setup the resolution policy and design resolution size
cc.view.setDesignResolutionSize(game.getDefaultWidth(), game.getDefaultHeight(), cc.ResolutionPolicy.SHOW_ALL);
// The game will be resized when browser size change
cc.view.resizeWithBrowserSize(true);
//Load all assets and start the game
game.loadAllAssets(function() {
game.startGameLoop();
});
};
cc.game.run();

View File

@@ -0,0 +1,16 @@
{
"project_type": "javascript",
"debugMode" : 1,
"showFPS" : true,
"frameRate" : 60,
"id" : "gameCanvas",
"renderMode" : 0,
"engineDir":"frameworks/cocos2d-html5",
"modules" : ["cocos2d"],
"jsList" : [
// GDJS_INCLUDE_FILES
]
}

View File

@@ -25,8 +25,7 @@ gdjs.CocosImageManager.prototype.getTexture = function(imageName) {
file = this.getInvalidTexture();
}
//TODO: path
var texture = cc.textureCache.addImage('src/JSPreview/' + file);
var texture = cc.textureCache.addImage('res/' + file);
return texture;
};
@@ -44,7 +43,7 @@ gdjs.CocosImageManager.prototype.isPowerOf2 = function(texture) {
gdjs.CocosImageManager.prototype.loadTextures = function(onProgress, onComplete) {
var that = this;
var files = Object.keys(this._resources).map(function(name) {
return 'src/JSPreview/' + that._resources[name].file;
return 'res/' + that._resources[name].file;
});
cc.LoaderScene.preload(files, function () {

View File

@@ -60,7 +60,7 @@ gdjs.RuntimeGameCocosRenderer.prototype.getWindowTitle = function() {
gdjs.RuntimeGameCocosRenderer.prototype.startGameLoop = function(fn) {
this._gameLoopFn = fn;
this._gameLoopFn(); //TODO
this._gameLoopFn();
}
gdjs.RuntimeGameCocosRenderer.prototype.getDirectorManager = function() {

View File

@@ -51,7 +51,7 @@ gdjs.RuntimeSceneCocosRenderer.prototype.render = function() {
var b = intColor & 255;
this._cocosBgLayer.setColor(cc.color(r, g, b));
this._renderProfileText();
//this._renderProfileText(); //Uncomment to display profiling times
};
gdjs.RuntimeSceneCocosRenderer.prototype._renderProfileText = function() {

View File

@@ -77,10 +77,10 @@ gdjs.SoundManager = gdjs.CocosSoundManager; //Register the class to let the engi
gdjs.CocosSoundManager.prototype._getFileFromSoundName = function(soundName) {
if (this._availableResources.hasOwnProperty(soundName) &&
this._availableResources[soundName].file) {
return 'src/JSPreview/' + this._availableResources[soundName].file;
return 'res/' + this._availableResources[soundName].file;
}
return 'src/JSPreview/' + soundName;
return 'res/' + soundName;
}
gdjs.CocosSoundManager.prototype.playSound = function(soundName, loop, volume, pitch) {

View File

@@ -20,7 +20,7 @@ gdjs.RuntimeScenePixiRenderer.prototype.onCanvasResized = function() {
gdjs.RuntimeScenePixiRenderer.prototype.render = function() {
if (!this._pixiRenderer) return;
this._renderProfileText();
//this._renderProfileText(); //Uncomment to display profiling times
// render the PIXI container of the scene
this._pixiRenderer.backgroundColor = this._runtimeScene.getBackgroundColor();

File diff suppressed because it is too large Load Diff