patternjavascriptMinor
Combat simulator - single page JavaScript with requirejs from a Java Applet
Viewed 0 times
combatjavascriptwithrequirejsappletjavasinglepagesimulatorfrom
Problem
Single-page web app from Applet
I'm seeking feedback on my first single-page web app using RequireJS and a web worker. I converted it from a Java applet, and the code is up on GitHub.
It's a combat simulator based on the old-school (1977) game of Melee. The RequireJS design is based on jrburke's single page app template.
Here's what the whole design looks like in UML:
Feedback I'm seeking
I'm an experienced Java programmer who, upon realizing Java applets don't run in browsers anymore (I know I'm late to that party!), decided to bite the bullet and move to JavaScript.
The result I have seems to work well and in some ways is probably better than my Java applet originally was. I'm curious for feedback on my approach to converting the applet and the use of RequireJS, web workers, etc.
I'm seeking feedback on my first single-page web app using RequireJS and a web worker. I converted it from a Java applet, and the code is up on GitHub.
It's a combat simulator based on the old-school (1977) game of Melee. The RequireJS design is based on jrburke's single page app template.
Here's what the whole design looks like in UML:
Feedback I'm seeking
I'm an experienced Java programmer who, upon realizing Java applets don't run in browsers anymore (I know I'm late to that party!), decided to bite the bullet and move to JavaScript.
The result I have seems to work well and in some ways is probably better than my Java applet originally was. I'm curious for feedback on my approach to converting the applet and the use of RequireJS, web workers, etc.
- I tried as best I could to reproduce the Java Swing GUI in HTML 5 and BootStrap. I'm not 100% happy with an HTML Select with multiple option to reproduce a Java
JList. It's not great in mobile versions of Chrome (Android), as it remains "closed" until you tap on it. There's no easy way to select all, for example (you have to tap once on each item!). But it works great in desktop browsers. You can try the "live" version of the simulator.
- Since HTML 5 web workers have almost 100% separate memory space from the main thread, I intentionally didn't use the same class with RequireJS in two separate threads (see the UML diagram). Probably this isn't a hard an fast rule. But in an early design, one of my classes (
HeroesSingleton) was being loaded twice (once in the main, once in the web worker) and so it wasn't actually a singleton. That seems to be a gotcha with RequireJS and threads.
- I followed a few templates for RequireJS class designs for the "objects" in the simulator, e.g.,
Hero,Weapon, etc. In the simulator, onlyHeroandGameobjects are objects that change state (not immutable). I'm pretty sure the methods (getters) I have there are overkill since there is no
Solution
I'm an experienced Java programmer who, upon realizing Java applets don't run in browsers anymore (I know I'm late to that party!), decided to bite the bullet and move to JavaScript.
Yay! Welcome to JavaScript, where everything looks broken! >:D
I'd love feedback on how to save time with intellisense developing a single-page requirejs or any other modular technique.
I'd recommend Intellij IDEA (the paid one) as it has some pretty good code analysis, and it really figures out what goes where. For free alternatives, Visual Studio or even Visual Studio Code are good too. If you just want plain syntax highlighting, linting, indentation etc. then Sublime Text 3 with a bunch of linter plugins (too many to mention) should also suffice.
Based on your diagram, the simulator only needs
Also, unlike Java which ignores unused imports, module management systems like RequireJS don't have a way to know what imports aren't used. They just load everything you tell them to. If that happens, and if you happen to execute DOM-related code or non-existent globals inside a worker (like say use
Since your code is more of a "data, render, clear" operation, which simply just rendering and re-rendering really, consider using a template library like Mustache. That way, you can simply just write plain HTML with some "mustaches" (points where data interpolates with the template). Then in one render call, you get a full HTML which you can pop in to the page using
Now with regards to "threading", I believe your solution is so... Java-ish or CPP-ish. The beauty of JS is that it runs on one thread (forget thread pools, synchronizations etc), but it's very fast. Instead of spawning "threads" to operate different multiple jobs, I believe JS is fast enough to execute all of them in the same thread... given some trickery.
You can simply opt to do async operations instead. That is, single thread, multiple tasks, specially scheduled so that each has its opportunity to operate. You can spawn instances of your simulator and listen to them finish using Promises (I believe in Java they call them Futures?). You can use
For instance, something like this:
Yay! Welcome to JavaScript, where everything looks broken! >:D
I'd love feedback on how to save time with intellisense developing a single-page requirejs or any other modular technique.
I'd recommend Intellij IDEA (the paid one) as it has some pretty good code analysis, and it really figures out what goes where. For free alternatives, Visual Studio or even Visual Studio Code are good too. If you just want plain syntax highlighting, linting, indentation etc. then Sublime Text 3 with a bunch of linter plugins (too many to mention) should also suffice.
importScripts('../lib/require.js');
require(["./HeroesSingleton", "./Hero", "./Game", "./controller", "./Logger"], function (HeroesSingleton, Hero, Game, controller, Logger) {Based on your diagram, the simulator only needs
Hero, Game, and Logger. Not sure why HeroesSingleton and even controller is present here. It's also worth noting that inside a worker, you don't have the usual globals you would find in the browser thread. You can't even access the DOM.Also, unlike Java which ignores unused imports, module management systems like RequireJS don't have a way to know what imports aren't used. They just load everything you tell them to. If that happens, and if you happen to execute DOM-related code or non-existent globals inside a worker (like say use
controller in the context of a worker), then your code might break.var p = document.createElement('p');
p.className = "bg-warning";
p.appendChild(document.createTextNode("No results becase the simulator was stopped before it finished."));
document.getElementById("matchupWins").appendChild(p);
p = document.createElement('p');
p.className = "bg-warning";
p.appendChild(document.createTextNode("No results becase the simulator was stopped before it finished.")); document.getElementById("heroWins").appendChild(p);
document.getElementById("startSimulation").disabled = false;Since your code is more of a "data, render, clear" operation, which simply just rendering and re-rendering really, consider using a template library like Mustache. That way, you can simply just write plain HTML with some "mustaches" (points where data interpolates with the template). Then in one render call, you get a full HTML which you can pop in to the page using
innerHTML.Now with regards to "threading", I believe your solution is so... Java-ish or CPP-ish. The beauty of JS is that it runs on one thread (forget thread pools, synchronizations etc), but it's very fast. Instead of spawning "threads" to operate different multiple jobs, I believe JS is fast enough to execute all of them in the same thread... given some trickery.
You can simply opt to do async operations instead. That is, single thread, multiple tasks, specially scheduled so that each has its opportunity to operate. You can spawn instances of your simulator and listen to them finish using Promises (I believe in Java they call them Futures?). You can use
setInterval timer instead of loop so that you have asynchronous iterations.For instance, something like this:
function simulate(config){
return new Promise(function(resolve, reject){
// Do async stuff here
if(successful) resolve(results);
else reject(error);
});
}
// Create 20 instances of the simulator by iterating through an array
// created by _.range() (there's no range function in JS, check lodash)
var simulatorPromises = _.range(20).map(function(index){
return simulate(config);
});
// Assign listeners for when all simulators finish
Promise.all(simulatorPromises).then(function(results){
// All simulators succeeded
// `results` is an array of resolved values from the instances in the
// order they were in the `simulatorPromises` array.
}, function(error){
// Not all simulators succeeded
});
// By now, simulators should be running asynchronously. Your listener
// will eventually fire when they're done.Code Snippets
importScripts('../lib/require.js');
require(["./HeroesSingleton", "./Hero", "./Game", "./controller", "./Logger"], function (HeroesSingleton, Hero, Game, controller, Logger) {var p = document.createElement('p');
p.className = "bg-warning";
p.appendChild(document.createTextNode("No results becase the simulator was stopped before it finished."));
document.getElementById("matchupWins").appendChild(p);
p = document.createElement('p');
p.className = "bg-warning";
p.appendChild(document.createTextNode("No results becase the simulator was stopped before it finished.")); document.getElementById("heroWins").appendChild(p);
document.getElementById("startSimulation").disabled = false;function simulate(config){
return new Promise(function(resolve, reject){
// Do async stuff here
if(successful) resolve(results);
else reject(error);
});
}
// Create 20 instances of the simulator by iterating through an array
// created by _.range() (there's no range function in JS, check lodash)
var simulatorPromises = _.range(20).map(function(index){
return simulate(config);
});
// Assign listeners for when all simulators finish
Promise.all(simulatorPromises).then(function(results){
// All simulators succeeded
// `results` is an array of resolved values from the instances in the
// order they were in the `simulatorPromises` array.
}, function(error){
// Not all simulators succeeded
});
// By now, simulators should be running asynchronously. Your listener
// will eventually fire when they're done.Context
StackExchange Code Review Q#108526, answer score: 4
Revisions (0)
No revisions yet.