Dune 2: The Building of a Dynasty

Developer: Westwood Studios
Publisher: Virgin Games
Genre: Strategy (Real-time) / Top-down
System requirements:
moder browsers


introduction


Dune 2 is a great strategy, one of the first in the genre. It makes no sense to grovel and say how it's a great game for a whole generation of children of the 90s. And since I unexpectedly find a pleasure to Tinker with the code and port it to JavaScript, then of course my goal after TTD (article) inevitably became Dune 2. Luckily, I did not think to start with it, because I'm afraid I can't handle. As it turned out, even though Dune 2 and is simpler in functionality than TTD, but moving it was more difficult, but more on that later.

codebase


Choosing the right code base is a major factor in the success of porting the project with the use of emscripten. For example, the use of SDL, the lack of multithreading is a good marker of the fact that porting is a success. I went through like all projects one way or another connected with Dune 2, and stayed OpenDune. The trick that I was hooked — a full backup of all behaviors of the original game including all its bugs. It looks like the code for this project was originally obtained through semi-automatic from the original. In the code there are variables with the name local_03FF, a lot of global variables, the code very hard to read. The most serious shortcoming of the original code base in multithreading, it has caused a lot of problems when porting. But the result is really nice in the browser similar to the original very much, except the new pack of bugs.

so, the bare facts:

Language: C
The number of source files: 143
The number of lines of code: 59151
Size of binary: 423.2 KB
Equivalent JavaScript: ~1000 KB
Time spent on porting: ~ 2 months

Later in this article will describe the difficulties I encountered when porting. It's probably not interesting for anybody, if so, omit this sub-section to the "known issues".

Multithreading VS async


OpenDune has quite an interesting model of multithreading-based interrupts. To enable multithreading game code in idle spinning in endless cycles, it looks like this:

the
 while (true) {
uint16 key;
key = GUI_Widget_HandleEvents(w);

if (key = 13) {
break;
}

sleepIdle();
}


When you start the application initializes the interval timer function setitimer. This timer causes an interrupt at regular intervals of time. It suspends the main thread of execution and allows to execute arbitrary code. A JavaScript implementation of the same timer is trivial, however, had another way of porting in order to not artificially divide a project in JavaScript and C implementations. It was decided to completely abandon the use of the function setitimer instead, call sleepIdle() was replaced by the event-handling function for timer, ie, is idle this function defines which scheduled event came up and launches them for execution.

A more serious problem — internal cycles while, any appearance of such a loop in JavaScript will cause imminent hanging open browser tabs (or browsers in General). This is due to the fact that the majority of cycles waiting for user input (mouse click, keyboard), but the browser cannot process events from input devices, they are placed in the execution chain after the current executable block of JavaScript. Possible solution to this problem — manual editing of the code and translation of the problem code in asynchronous mode.

Small primerchik. Here is a draft code which causes the issue:
the
void someProblemFunction() {
{
//open 1
}

while (true) {
// open 2

while (true) {
// code 2
}

//  close  2
}

{
//close 2
}
}


After a painful mental manipulation, asynchronous code:

the
void asyncSomeProblemFunction() {
Async_InvokeInLoop(
asyncSomeProblemFunctionOpen1,
asyncSomeProblemFunctionCondition1,
asyncSomeProblemFunctionLoop1,
asyncSomeProblemFunctionClose1);
}

void asyncSomeProblemFunctionOpen1() {
// code from open 1
}

void asyncSomeProblemFunctionCondition1() {
// code from loop 1 condition
}

void asyncSomeProblemFunctionLoop1() {
Async_InvokeInLoop(
asyncSomeProblemFunctionOpen2,
asyncSomeProblemFunctionCondition2,
asyncSomeProblemFunctionLoop2,
asyncSomeProblemFunctionClose2);
}

void asyncSomeProblemFunctionClose1() {
// code from close 1
}


Hell of a job. The core of the entire system is a function of Async_InvokeInLoop.

the
void Async_InvokeInLoop(
void (*open)(), 
void (*condition)(bool* ref), 
void (*loop)(), 
void (*close)());


Async_InvokeInLoop — allows you to replace any cycle of while (true) asynchronous equivalent. Ensures that a call to open to the beginning of the cycle and close after the completion of the cycle. References to functions condition and loop are equal participants in the asynchronous iteration that they make clear from the title. Iteration is implemented using the function Async_Loop:

the
void Async_Loop() {
ScheduledAsync *top = STACK_TOP;

switch (top->state) {
case ScheduledAsync_OPEN: {
top->open();
top->state = ScheduledAsync_CONDITION;

return;
}

case ScheduledAsync_CONDITION: {
top->condition(&top->conditionValue);
top->state = ScheduledAsync_LOOP;

return;
}

case ScheduledAsync_LOOP: {
if (top- > conditionValue) {
top->loop();
top->state = ScheduledAsync_CONDITION;
} else {
top->state = ScheduledAsync_CLOSE;
}

return;
}

case ScheduledAsync_CLOSE: {
popStack();
top->close();

free(top);
return;
}

default:
abort();
}
}


The game loop (or a timer in JavaScript) periodically pulls this feature making everything in the game revolves. If the original function needs to return a result, the problems are doubled — you have to save the result in memory globally, and then extract it in other functions. Everything works by Convention. In the result, I got a hell of a framework for asynchronization project, here is its interface:

the
/*
* async.h
*
* Created on: 19.10.2012
* Author: caiiiycuk
*/

#ifndef ASYNC_H_
#define ASYNC_H_

#include "types.h"

extern void async_noop();
extern void async_false(bool *condition);
extern void async_true(bool *condition);

extern void Async_InvokeInLoop(void (*open)(), void (*condition)(bool* ref), void (*loop)(), void (*close)());
extern bool Async_IsPending();
extern void Async_Loop();

extern void Async_InvokeAfterAsync(void (*callback)());
extern void Async_InvokeAfterAsyncOrNow(void (*callback)());

extern void Async_Storage_uint16(uint16* storage);
extern void Async_StorageSet_uint16(uint16 value);


#endif /* ASYNC_H_ */


The synchronous nature of the game has mutated into asynchronous, which will delight some funny bugs:
the
    the
  • If you call the construction menu before the computer opponent will determine the following structure for the construction, it is possible to access to the facilities (fixed)
  • the
  • When the script is loaded, the possibility existed that the structure of the enemy will get 20 000 — 30 000 one unit of life instead of 150 — 200 (fixed)
  • the
  • because of the sync errors — the game map can perepisovatsya directly on top of a dialogue with the mentat, the truth is this rarely (not fixed)


Known issues


Due to the fact that the state testers consists of me and my fictional friends, it is only known that:
the
    the
  • the Game works in Firefox, Chrome, Opera, Chrome (Android ~4)
  • the Game is fully passed the house Harkonnen and serious problems are found the

  • a Small number of missions completed for two other home problems as there was
  • the
  • Game cursor never changes (regardless of actions) done intentionally (retarding it)
  • the
  • To scroll the map, use the minimap or arrows of the keyboard (use the keyboard anymore in the game)
  • the
  • Have music, but no effects
  • the
  • On the game map can cause artifacts (very rarely, you will understand), in this case it helps the open/close game menu
  • the
  • menu items are: save, load, restart mission
  • the
  • In the game only one slot to save (on all house)

Everything else works, or should work.
Tracker: https://github.com/caiiiycuk/play-dune

Play?


http://play-dune.com/

71

UPD1. hotkeys: habrahabr.ru/post/159501/#comment_5516325
UPD2. Server prasadam, give direct links to be shown:
Game house Atreides
Game house Ordos
Game house Harkonnen

UPD3. the Server ran into a limitation tcpsndbuf — the total size of the buffers, which can be used to send data over a TCP connection. The restriction which puts me provider, a more powerful server can not afford, sorry if you were unable to play. Waiting for when the load returns to normal.

UPD4. the problem was in my curve hands, now the server needs to handle the load.

UPD5. spaceport is not working the button "Invoice", please be careful, if you have not ordered either one unit to the spaceport and click on "Send Order", it automatically pressed the button "Invoice" and everything hangs.

UPD6. project website.
Article based on information from habrahabr.ru

Комментарии

Популярные сообщения из этого блога

Fresh hay from the cow, or 3000 icons submitted!

Knowledge base. Part 2. Freebase: make requests to the Google Knowledge Graph

Group edit the resources (documents) using MIGXDB