Comments (3)
Related to this, I wrote a minimal implementation of the API to support Weltmeister with NodeJS for my Impact2 branch a few years ago. In my typical style it doesn't need any external packages (no npm
). Some paths may need to be adjusted for Impact1. It's supposed to sit in the main directory as server.js
and you'd start the server for Weltmeister with node server.js
.
https://gist.github.com/phoboslab/0bf09acb2ea4912d621ae80c439a4bba
So, just an idea for an (imho) nice and clean solution: how about we rename this to impact.js
, put it in the main directory and add the baking functionality. You could then call it with:
node impact.js editor
to start the server for Weltmeisternode impact.js bake
(orbuild
?) to produce the baked output
Edit: looking at this again, it would need some more adjustments. The current Weltmeister API distinguished between a browse
and glob
command, which could both be served through the new glob
command of the server.js
alone. This requires some changes in the weltmeister.js
and modal-dialogs.js
, though. Not sure if it's worth the effort.
from impact.
I kind of like the simplicity and separation of concerns of having:
- /tools/bake.js (for baking)
- /server.js (for the Weltmeister/development server)
Edit: I like your server.js. It looks like a simpler version of impact-dev-server, which I've been using.
from impact.
Edit: looking at this again, it would need some more adjustments. The current Weltmeister API distinguished between a
browse
andglob
command, which could both be served through the newglob
command of theserver.js
alone. This requires some changes in theweltmeister.js
andmodal-dialogs.js
, though. Not sure if it's worth the effort.
I got it working.
Here's a patch to get Weltmeister running off node server.js
. It looks bigger than it really is, due to indentation.
diff --git a/lib/weltmeister/config.js b/lib/weltmeister/config.js
index 8d99632..cf29d1b 100644
--- a/lib/weltmeister/config.js
+++ b/lib/weltmeister/config.js
@@ -119,7 +119,6 @@ wm.config = {
// to change these.
'api': {
'save': 'lib/weltmeister/api/save.php',
- 'browse': 'lib/weltmeister/api/browse.php',
'glob': 'lib/weltmeister/api/glob.php'
}
};
diff --git a/lib/weltmeister/entities.js b/lib/weltmeister/entities.js
index 68fa638..5f7fd8e 100644
--- a/lib/weltmeister/entities.js
+++ b/lib/weltmeister/entities.js
@@ -7,52 +7,49 @@ ig.module(
.defines(function(){ "use strict";
// Load the list of entity files via AJAX PHP glob
-var path = wm.config.api.glob + '?',
+var moduleNames = [],
+ modules = {},
globs = typeof wm.config.project.entityFiles == 'string' ?
[wm.config.project.entityFiles] :
wm.config.project.entityFiles;
-
-for (var i = 0; i < globs.length; i++) {
- path += 'glob[]=' + encodeURIComponent(globs[i]) + '&';
-}
-
-path += 'nocache=' + Math.random();
-
-var req = $.ajax({
- url: path,
- method: 'get',
- dataType: 'json',
- // MUST load synchronous, as the engine would otherwise determine that it
- // can't resolve dependencies to weltmeister.entities when there are
- // no more files to load and weltmeister.entities is still not defined
- // because the ajax request hasn't finished yet.
- // FIXME FFS!
- async: false,
- success: function(files) {
+for (let i = 0; i < globs.length; i++) {
+ var dir = globs[i].substr(0, globs[i].indexOf('*'));
+ var req = $.ajax({
+ url: wm.config.api.glob + '?dir=' + encodeURIComponent(dir) + '&type=js',
+ method: 'get',
+ dataType: 'json',
- // File names to Module names
- var moduleNames = [];
- var modules = {};
- for( var i = 0; i < files.length; i++ ) {
- var name = files[i]
- .replace(new RegExp("^"+ig.lib+"|\\.js$", "g"), '')
- .replace(/\//g, '.');
- moduleNames.push( name );
- modules[name] = files[i];
+ // MUST load synchronous, as the engine would otherwise determine that it
+ // can't resolve dependencies to weltmeister.entities when there are
+ // no more files to load and weltmeister.entities is still not defined
+ // because the ajax request hasn't finished yet.
+ // FIXME FFS!
+ async: false,
+ success: function(data) {
+
+ // File names to Module names
+ var files = data.files;
+ for( var i = 0; i < files.length; i++ ) {
+ var name = files[i]
+ .replace(new RegExp("^"+ig.lib+"|\\.js$", "g"), '')
+ .replace(/\//g, '.');
+ moduleNames.push( name );
+ modules[name] = files[i];
+ }
+ },
+ error: function( xhr, status, error ){
+ throw(
+ "Failed to load entity list via glob.php: " + error + "\n" +
+ xhr.responseText
+ );
}
-
- // Define a Module that requires all entity Modules
- ig.module('weltmeister.entities')
- .requires.apply(ig, moduleNames)
- .defines(function(){ wm.entityModules = modules; });
- },
- error: function( xhr, status, error ){
- throw(
- "Failed to load entity list via glob.php: " + error + "\n" +
- xhr.responseText
- );
- }
-});
+ });
+}
+
+// Define a Module that requires all entity Modules
+ig.module('weltmeister.entities')
+ .requires.apply(ig, moduleNames)
+ .defines(function(){ wm.entityModules = modules; });
});
\ No newline at end of file
diff --git a/lib/weltmeister/modal-dialogs.js b/lib/weltmeister/modal-dialogs.js
index 53ff3cd..cc920bd 100644
--- a/lib/weltmeister/modal-dialogs.js
+++ b/lib/weltmeister/modal-dialogs.js
@@ -93,7 +93,7 @@ wm.ModalDialogPathSelect = wm.ModalDialog.extend({
this.parent();
this.pathInput = $('<input/>', {'type': 'text', 'class': 'modalDialogPath'} );
this.buttonDiv.before( this.pathInput );
- this.pathDropdown = new wm.SelectFileDropdown( this.pathInput, wm.config.api.browse, this.fileType );
+ this.pathDropdown = new wm.SelectFileDropdown( this.pathInput, wm.config.api.glob, this.fileType );
},
diff --git a/lib/weltmeister/select-file-dropdown.js b/lib/weltmeister/select-file-dropdown.js
index 6975a1d..4dfbb83 100644
--- a/lib/weltmeister/select-file-dropdown.js
+++ b/lib/weltmeister/select-file-dropdown.js
@@ -59,14 +59,12 @@ wm.SelectFileDropdown = ig.Class.extend({
this.div.append( parentDir );
}
for( var i = 0; i < data.dirs.length; i++ ) {
- var name = data.dirs[i].match(/[^\/]*$/)[0] + '/';
- var dir = $('<a/>', {'class':'dir', href:data.dirs[i], html: name, title: name});
+ var dir = $('<a/>', {'class':'dir', href: data.dirs[i].path, html: data.dirs[i].name, title: data.dirs[i].name});
dir.bind( 'click', this.selectDir.bind(this) );
this.div.append( dir );
}
for( var i = 0; i < data.files.length; i++ ) {
- var name = data.files[i].match(/[^\/]*$/)[0];
- var file = $('<a/>', {'class':'file', href:data.files[i], html: name, title: name});
+ var file = $('<a/>', {'class':'file', href: data.files[i].path, html: data.files[i].name, title: data.files[i].name});
file.bind( 'click', this.selectFile.bind(this) );
this.div.append( file );
}
diff --git a/lib/weltmeister/weltmeister.js b/lib/weltmeister/weltmeister.js
index 772e531..ceb70a3 100644
--- a/lib/weltmeister/weltmeister.js
+++ b/lib/weltmeister/weltmeister.js
@@ -64,13 +64,13 @@ wm.Weltmeister = ig.Class.extend({
// Dialogs
- this.loadDialog = new wm.ModalDialogPathSelect( 'Load Level', 'Load', 'scripts' );
+ this.loadDialog = new wm.ModalDialogPathSelect( 'Load Level', 'Load', wm.Weltmeister.levelType );
this.loadDialog.onOk = this.load.bind(this);
this.loadDialog.setPath( wm.config.project.levelPath );
$('#levelLoad').bind( 'click', this.showLoadDialog.bind(this) );
$('#levelNew').bind( 'click', this.showNewDialog.bind(this) );
- this.saveDialog = new wm.ModalDialogPathSelect( 'Save Level', 'Save', 'scripts' );
+ this.saveDialog = new wm.ModalDialogPathSelect( 'Save Level', 'Save', wm.Weltmeister.levelType );
this.saveDialog.onOk = this.save.bind(this);
this.saveDialog.setPath( wm.config.project.levelPath );
$('#levelSaveAs').bind( 'click', this.saveDialog.open.bind(this.saveDialog) );
@@ -84,7 +84,7 @@ wm.Weltmeister = ig.Class.extend({
this.mode = this.MODE.DEFAULT;
- this.tilesetSelectDialog = new wm.SelectFileDropdown( '#layerTileset', wm.config.api.browse, 'images' );
+ this.tilesetSelectDialog = new wm.SelectFileDropdown( '#layerTileset', wm.config.api.glob, wm.Weltmeister.imageType );
this.entities = new wm.EditEntities( $('#layerEntities') );
$('#layers').sortable({
@@ -455,16 +455,12 @@ wm.Weltmeister = ig.Class.extend({
"});";
}
- var postString =
- 'path=' + encodeURIComponent( path ) +
- '&data=' + encodeURIComponent(dataString);
-
var req = $.ajax({
- url: wm.config.api.save,
+ url: wm.config.api.save + '?path=' + encodeURIComponent( path ),
type: 'POST',
dataType: 'json',
async: false,
- data: postString,
+ data: dataString,
success:this.saveResponse.bind(this)
});
},
@@ -937,6 +933,9 @@ wm.Weltmeister.getMaxHeight = function() {
return $(window).height() - $('#headerMenu').height();
};
+wm.Weltmeister.levelType = 'js,json';
+wm.Weltmeister.imageType = 'png,jpg,jpeg,gif';
+
// Custom ig.Image class for use in Weltmeister. To make the zoom function
// work, we need some additional scaling behavior:
diff --git a/server.js b/server.js
new file mode 100644
index 0000000..cfdc077
--- /dev/null
+++ b/server.js
@@ -0,0 +1,155 @@
+'use strict';
+
+let http = require('http'),
+ url = require('url'),
+ querystring = require('querystring'),
+ fs = require('fs'),
+ port = parseInt(process.argv[2] || 8080, 10);
+
+
+let API = {
+ glob: function(params, body, callback) {
+ let dir = params.dir ? params.dir.replace(/\/$/, '') : '';
+ fs.readdir('./' + dir, function(err, files){
+ if (err) {
+ return callback('dir listing failed for ./' + dir);
+ }
+
+ let parent = dir.replace(/\/?[^\/]*$/,'');
+ let ret = {current: dir, parent: parent, dirs: [], files: []},
+ typeMatch = (params.type ? params.type.split(',') : [])
+ .map(type => new RegExp('\\.' + type + '$'));
+
+ for (let i = 0; i < files.length; i++) {
+ let fileName = files[i],
+ filePath = (dir && dir+'/') + fileName;
+
+ if (fileName.match(/^\./)) {
+ continue;
+ }
+
+ if (fs.statSync('./' + filePath).isDirectory()) {
+ ret.dirs.push({name: fileName, path: filePath});
+ }
+ else if (
+ typeMatch.length === 0 ||
+ typeMatch.find(pattern => fileName.match(pattern))
+ ) {
+ ret.files.push({name: fileName, path: filePath});
+ }
+ }
+ callback(null, ret);
+ });
+ },
+
+ save: function(params, body, callback) {
+ if (!params.path) {
+ return callback('no path specified');
+ }
+
+ fs.writeFile(params.path, body, {flag: 'w'}, function(err) {
+ if (err) {
+ return callback('error writing file ' + params.path);
+ }
+ callback(null, {saved: true});
+ });
+ }
+};
+
+
+
+let utf8 = '; charset=utf-8';
+let mime = {
+ html: 'text/html' + utf8,
+ htm: 'text/html' + utf8,
+ json: 'application/json' + utf8,
+ js: 'application/javascript' + utf8,
+ jpeg: 'image/jpeg',
+ jpg: 'image/jpeg',
+ gif: 'image/gif',
+ png: 'image/png',
+ ogg: 'audio/ogg',
+ mp3: 'audio/mp3',
+ aac: 'audio/aac',
+ css: 'text/css' + utf8,
+ bin: 'application/octet-stream'
+};
+
+let serveAPI = function(request, response, name) {
+ let uri = url.parse(request.url, true),
+ apiFunc = API[name];
+
+ if (!apiFunc) {
+ return writeError(request, response, 404, 'no such API function: ' + name);
+ }
+
+ console.log('API:', name, uri.query);
+
+ let bodyChunks = [];
+ request.on('data', function(chunk){
+ bodyChunks.push(chunk);
+ });
+
+ request.on('end', function(){
+ let body = Buffer.concat(bodyChunks);
+ apiFunc(uri.query, body, function(err, data){
+ if (err) {
+ writeError(request, response, 500, err);
+ }
+ else {
+ response.writeHead(200, {'Content-Type': mime.json});
+ response.write(JSON.stringify(data));
+ response.end();
+ }
+ });
+ });
+};
+
+let serveStaticFile = function(request, response) {
+ let uri = querystring.unescape(url.parse(request.url).pathname),
+ fileName = './' + uri;
+
+ fs.stat(fileName, function(err, stat) {
+ if (err) {
+ return writeError(request, response, 404, 'not found');
+ }
+
+ if (stat.isDirectory()) {
+ fileName += '/index.html';
+ }
+
+ fs.readFile(fileName, 'binary', function(err, file) {
+ if (err) {
+ return writeError(request, response, 500, 'internal error');
+ }
+
+ let ext = fileName.match(/\.(\w+)$/);
+ console.log('200: ' + uri);
+ response.writeHead(200, {'content-type': (ext && mime[ext[1]]) || mime.bin});
+ response.write(file, 'binary');
+ response.end();
+ });
+ });
+};
+
+let writeError = function(request, response, code, message) {
+ console.log(code+': ' + request.url);
+ response.writeHead(code, {'Content-Type': mime.json});
+ response.write(JSON.stringify({error: {code: code, message: message}}));
+ response.end();
+};
+
+http.createServer(function(request, response) {
+ let apiMatch = null;
+ if (apiMatch = request.url.match(/\/api\/(\w+)/)) {
+ serveAPI(request, response, apiMatch[1]);
+ }
+ else {
+ serveStaticFile(request, response);
+ }
+}).listen(port);
+
+console.log(
+ 'Listening on http://localhost:' + port + '/\n' +
+ 'CTRL + C to shutdown'
+);
The browse
API call doesn't exist anymore (it uses glob
).
Since your version of glob
is slightly different, and also because the level filename in your version is set via the URL (instead of as part of the POST data), this is no longer backwards compatible with the old PHP back-end. Think it is important to keep the PHP back-end working? Would it possibly make sense to remove it?
Also tweaked your version to filter multiple file types at once, because the original Weltmeister shows only "js/json" files in the level drop-down, and only "png/jpg/jpeg/gif" files in tile-sheet drop-down.
Any thoughts to make this cleaner?
from impact.
Related Issues (20)
- Unused property: Weltmeister.MODE.DRAW
- Unused property in wm.EditEntities.scaleSelectedEntity
- tutorials in text/html needed
- Just create one `Undo` object per tile edit
- EditMap.linkWithCollision currently assumes same tilesize
- Weltmeister Start up HOT 5
- "touch-scroll" is broken in Weltmeister in Firefox
- Spelling for method ig.Entity.seperateOnXAxis
- secure or delete forum account HOT 2
- X-Ray cannot find ores from mods HOT 1
- Issue with Pong Example HOT 3
- Move JSONFormat into separate file json-format.js
- Weltmeister produces invalid level JSON
- cannot open HOT 4
- Weltmeiser not working HOT 10
- Math.atan2( y, x), y is the first
- Should we call `Math.sqrt` for dot ?
- Weltmeister: New entities being spawned way off-screen
- Import debugout.js with an impact.js project
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from impact.