summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorStar Rauchenberger <fefferburbia@gmail.com>2023-11-02 20:11:53 -0400
committerStar Rauchenberger <fefferburbia@gmail.com>2023-11-02 20:11:53 -0400
commit84c305819930713603124d984acb51df87761246 (patch)
tree435bbb75392311eea42d5d46d6b00caa36b35a8a
parenta59fcafb2e81f3cb40ff320b106030e8fed4bd66 (diff)
downloadmazeoflife-84c305819930713603124d984acb51df87761246.tar.gz
mazeoflife-84c305819930713603124d984acb51df87761246.tar.bz2
mazeoflife-84c305819930713603124d984acb51df87761246.zip
emscripten port emscripten
-rw-r--r--CMakeLists.txt18
-rw-r--r--gamestate.cpp53
-rw-r--r--hslist.cpp20
-rw-r--r--index.html143
-rw-r--r--mazeoflife.cpp5
-rw-r--r--mazeoflife.h1
-rw-r--r--sdl.h20
-rw-r--r--titlestate.cpp15
-rw-r--r--util.cpp2
9 files changed, 219 insertions, 58 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index e44357f..8db4315 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -1,21 +1,11 @@
1cmake_minimum_required (VERSION 3.1) 1cmake_minimum_required (VERSION 3.1)
2project (mazeoflife) 2project (mazeoflife)
3 3
4#set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -sUSE_SDL=2 -sUSE_SDL_NET=2 -sUSE_SDL_TTF=2") 4set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -sUSE_SDL=2 -sUSE_SDL_TTF=2 -pthread")
5 5set(CMAKE_EXECUTABLE_SUFFIX ".html")
6set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
7
8find_package(SDL2 REQUIRED)
9find_package(SDL2_ttf REQUIRED)
10find_package(SDL2_net REQUIRED)
11
12include_directories(
13 ${SDL2_INCLUDE_DIR}
14 ${SDL2_TTF_INCLUDE_DIR}
15 ${SDL2_NET_INCLUDE_DIRS}
16)
17 6
18add_executable(mazeoflife hslist.cpp hs_state.cpp mazeoflife.cpp util.cpp titlestate.cpp gamestate.cpp) 7add_executable(mazeoflife hslist.cpp hs_state.cpp mazeoflife.cpp util.cpp titlestate.cpp gamestate.cpp)
19set_property(TARGET mazeoflife PROPERTY CXX_STANDARD 17) 8set_property(TARGET mazeoflife PROPERTY CXX_STANDARD 17)
20set_property(TARGET mazeoflife PROPERTY CXX_STANDARD_REQUIRED ON) 9set_property(TARGET mazeoflife PROPERTY CXX_STANDARD_REQUIRED ON)
21target_link_libraries(mazeoflife ${SDL2_LIBRARY} ${SDL2_TTF_LIBRARY} ${SDL2_NET_LIBRARIES}) 10set_property(TARGET mazeoflife PROPERTY LINK_FLAGS "--preload-file resources -lidbfs.js -sALLOW_MEMORY_GROWTH")
11target_link_libraries(mazeoflife ${SDL2_LIBRARY} ${SDL2_TTF_LIBRARY})
diff --git a/gamestate.cpp b/gamestate.cpp index 953d35b..4a0f701 100644 --- a/gamestate.cpp +++ b/gamestate.cpp
@@ -9,9 +9,11 @@
9#include <iostream> 9#include <iostream>
10#include <list> 10#include <list>
11#include <memory> 11#include <memory>
12#include <mutex>
12#include <random> 13#include <random>
13#include <set> 14#include <set>
14#include <sstream> 15#include <sstream>
16#include <thread>
15#include <tuple> 17#include <tuple>
16#include <unordered_map> 18#include <unordered_map>
17#include <unordered_set> 19#include <unordered_set>
@@ -450,34 +452,31 @@ std::unique_ptr<State> startNewLevel(int level,
450 452
451class LoadGameState : public State { 453class LoadGameState : public State {
452 public: 454 public:
453 LoadGameState(int level) : level_(level) {} 455 LoadGameState(Game& game, int level) : level_(level) {
454
455 std::unique_ptr<State> operator()(Game& game) {
456 std::ostringstream wintitle; 456 std::ostringstream wintitle;
457 wintitle << "Maze Of Life - Level " << level_; 457 wintitle << "Maze Of Life - Level " << level_;
458 SDL_SetWindowTitle(game.window.get(), wintitle.str().c_str()); 458 SDL_SetWindowTitle(game.window.get(), wintitle.str().c_str());
459 459
460 // Randomly place the player in a corner 460 // Randomly place the player in a corner
461 int playerx, playery;
462 switch (std::uniform_int_distribution(0, 3)(game.rng)) { 461 switch (std::uniform_int_distribution(0, 3)(game.rng)) {
463 case 0: { 462 case 0: {
464 playerx = 1; 463 playerx_ = 1;
465 playery = 1; 464 playery_ = 1;
466 break; 465 break;
467 } 466 }
468 case 1: { 467 case 1: {
469 playerx = 1; 468 playerx_ = 1;
470 playery = HEIGHT - 2; 469 playery_ = HEIGHT - 2;
471 break; 470 break;
472 } 471 }
473 case 2: { 472 case 2: {
474 playerx = WIDTH - 2; 473 playerx_ = WIDTH - 2;
475 playery = HEIGHT - 2; 474 playery_ = HEIGHT - 2;
476 break; 475 break;
477 } 476 }
478 case 3: { 477 case 3: {
479 playerx = WIDTH - 2; 478 playerx_ = WIDTH - 2;
480 playery = 1; 479 playery_ = 1;
481 break; 480 break;
482 } 481 }
483 } 482 }
@@ -503,18 +502,34 @@ class LoadGameState : public State {
503 SDL_RenderPresent(game.renderer.get()); 502 SDL_RenderPresent(game.renderer.get());
504 503
505 // Do 50 gens of Conway 504 // Do 50 gens of Conway
506 std::unique_ptr<GameBoard> board = 505 std::thread([&] {
507 std::make_unique<GameBoard>(game, level_, playerx, playery); 506 std::unique_ptr<GameBoard> board =
507 std::make_unique<GameBoard>(game, level_, playerx_, playery_);
508
509 std::lock_guard boardGuard(boardMutex_);
510 board_.swap(board);
511 }).detach();
512 }
513
514 std::unique_ptr<State> operator()(Game& game) {
515 std::lock_guard boardGuard(boardMutex_);
516 if (board_) {
517 return startNewLevel(level_, std::move(board_), playerx_, playery_);
518 }
508 519
509 // Wait a bit 520 // Wait a bit
510 SDL_Delay(500); 521 // SDL_Delay(500);
511 522
512 // Start the level 523 // Start the level
513 return startNewLevel(level_, std::move(board), playerx, playery); 524 return nullptr;
514 } 525 }
515 526
516 private: 527 private:
517 int level_; 528 int level_;
529 int playerx_;
530 int playery_;
531 std::unique_ptr<GameBoard> board_;
532 std::mutex boardMutex_;
518}; 533};
519 534
520class PlayGameState : public State { 535class PlayGameState : public State {
@@ -600,7 +615,7 @@ class PlayGameState : public State {
600 } 615 }
601 616
602 if (trymove && move(to_x, to_y)) { 617 if (trymove && move(to_x, to_y)) {
603 return std::make_unique<LoadGameState>(level_ + 1); 618 return std::make_unique<LoadGameState>(game, level_ + 1);
604 } 619 }
605 } 620 }
606 } 621 }
@@ -641,6 +656,6 @@ std::unique_ptr<State> startNewLevel(int level,
641 playery); 656 playery);
642} 657}
643 658
644std::unique_ptr<State> GameState::operator()(Game&) { 659std::unique_ptr<State> GameState::operator()(Game& game) {
645 return std::make_unique<LoadGameState>(0); 660 return std::make_unique<LoadGameState>(game, 0);
646} 661}
diff --git a/hslist.cpp b/hslist.cpp index 9ae2aa4..9547acd 100644 --- a/hslist.cpp +++ b/hslist.cpp
@@ -12,6 +12,10 @@
12#include "titlestate.h" 12#include "titlestate.h"
13#include "util.h" 13#include "util.h"
14 14
15#ifdef __EMSCRIPTEN__
16#include <emscripten.h>
17#endif
18
15HighscoreList::HighscoreList(std::vector<Highscore> hslist) : hslist_(hslist) { 19HighscoreList::HighscoreList(std::vector<Highscore> hslist) : hslist_(hslist) {
16 resetRanks(); 20 resetRanks();
17} 21}
@@ -118,10 +122,18 @@ int HighscoreList::addHighscore(Highscore h) {
118} 122}
119 123
120void HighscoreList::writeToFile() { 124void HighscoreList::writeToFile() {
121 std::ofstream hsfile(getDataFile()); 125 {
122 hsfile << hslist_.size() << std::endl; 126 std::ofstream hsfile(getDataFile());
127 hsfile << hslist_.size() << std::endl;
123 128
124 for (const Highscore& h : hslist_) { 129 for (const Highscore& h : hslist_) {
125 hsfile << h.getName() << std::endl << h.getLevel() << std::endl; 130 hsfile << h.getName() << std::endl << h.getLevel() << std::endl;
131 }
126 } 132 }
133
134#ifdef __EMSCRIPTEN__
135 EM_ASM(FS.syncfs(function(err){
136 // Error
137 }););
138#endif
127} 139}
diff --git a/index.html b/index.html new file mode 100644 index 0000000..a63d720 --- /dev/null +++ b/index.html
@@ -0,0 +1,143 @@
1<!doctype html>
2<html lang="en-us">
3 <head>
4 <meta charset="utf-8">
5 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
6 <title>Emscripten-Generated Code</title>
7 <style>
8 .emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
9 textarea.emscripten { font-family: monospace; width: 80%; }
10 div.emscripten { text-align: center; }
11 div.emscripten_border { border: 1px solid black; }
12 /* the canvas *must not* have any border or padding, or mouse coords will be wrong */
13 canvas.emscripten { border: 1px solid black; background-color: transparent; position: absolute; top: 0; left: 0; }
14
15 .spinner {
16 height: 50px;
17 width: 50px;
18 margin: 0px auto;
19 -webkit-animation: rotation .8s linear infinite;
20 -moz-animation: rotation .8s linear infinite;
21 -o-animation: rotation .8s linear infinite;
22 animation: rotation 0.8s linear infinite;
23 border-left: 10px solid rgb(0,150,240);
24 border-right: 10px solid rgb(0,150,240);
25 border-bottom: 10px solid rgb(0,150,240);
26 border-top: 10px solid rgb(100,0,200);
27 border-radius: 100%;
28 background-color: rgb(200,100,250);
29 }
30 @-webkit-keyframes rotation {
31 from {-webkit-transform: rotate(0deg);}
32 to {-webkit-transform: rotate(360deg);}
33 }
34 @-moz-keyframes rotation {
35 from {-moz-transform: rotate(0deg);}
36 to {-moz-transform: rotate(360deg);}
37 }
38 @-o-keyframes rotation {
39 from {-o-transform: rotate(0deg);}
40 to {-o-transform: rotate(360deg);}
41 }
42 @keyframes rotation {
43 from {transform: rotate(0deg);}
44 to {transform: rotate(360deg);}
45 }
46
47 </style>
48 </head>
49 <body>
50 <hr/>
51 <figure style="overflow:visible;" id="spinner"><div class="spinner"></div><center style="margin-top:0.5em"><strong>emscripten</strong></center></figure>
52 <div class="emscripten" id="status">Downloading...</div>
53 <div class="emscripten">
54 <progress value="0" max="100" id="progress" hidden=1></progress>
55 </div>
56 <div class="emscripten_border">
57 <canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" tabindex=-1></canvas>
58 </div>
59 <hr/>
60 <div class="emscripten">
61 <input type="checkbox" id="resize">Resize canvas
62 <input type="checkbox" id="pointerLock" checked>Lock/hide mouse pointer
63 &nbsp;&nbsp;&nbsp;
64 <input type="button" value="Fullscreen" onclick="Module.requestFullscreen(document.getElementById('pointerLock').checked,
65 document.getElementById('resize').checked)">
66 </div>
67
68 <hr/>
69 <textarea class="emscripten" id="output" rows="8"></textarea>
70 <hr>
71 <script type='text/javascript'>
72 var statusElement = document.getElementById('status');
73 var progressElement = document.getElementById('progress');
74 var spinnerElement = document.getElementById('spinner');
75
76 var Module = {
77 print: (function() {
78 var element = document.getElementById('output');
79 if (element) element.value = ''; // clear browser cache
80 return function(text) {
81 if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
82 // These replacements are necessary if you render to raw HTML
83 //text = text.replace(/&/g, "&amp;");
84 //text = text.replace(/</g, "&lt;");
85 //text = text.replace(/>/g, "&gt;");
86 //text = text.replace('\n', '<br>', 'g');
87 console.log(text);
88 if (element) {
89 element.value += text + "\n";
90 element.scrollTop = element.scrollHeight; // focus on bottom
91 }
92 };
93 })(),
94 canvas: (() => {
95 var canvas = document.getElementById('canvas');
96
97 // As a default initial behavior, pop up an alert when webgl context is lost. To make your
98 // application robust, you may want to override this behavior before shipping!
99 // See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
100 canvas.addEventListener("webglcontextlost", (e) => { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
101
102 return canvas;
103 })(),
104 setStatus: (text) => {
105 if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' };
106 if (text === Module.setStatus.last.text) return;
107 var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
108 var now = Date.now();
109 if (m && now - Module.setStatus.last.time < 30) return; // if this is a progress update, skip it if too soon
110 Module.setStatus.last.time = now;
111 Module.setStatus.last.text = text;
112 if (m) {
113 text = m[1];
114 progressElement.value = parseInt(m[2])*100;
115 progressElement.max = parseInt(m[4])*100;
116 progressElement.hidden = false;
117 spinnerElement.hidden = false;
118 } else {
119 progressElement.value = null;
120 progressElement.max = null;
121 progressElement.hidden = true;
122 if (!text) spinnerElement.hidden = true;
123 }
124 statusElement.innerHTML = text;
125 },
126 totalDependencies: 0,
127 monitorRunDependencies: (left) => {
128 this.totalDependencies = Math.max(this.totalDependencies, left);
129 Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
130 }
131 };
132 Module.setStatus('Downloading...');
133 window.onerror = () => {
134 Module.setStatus('Exception thrown, see JavaScript console');
135 spinnerElement.style.display = 'none';
136 Module.setStatus = (text) => {
137 if (text) console.error('[post-exception status] ' + text);
138 };
139 };
140 </script>
141 <script async type="text/javascript" src="mazeoflife.js"></script>
142 </body>
143</html>
diff --git a/mazeoflife.cpp b/mazeoflife.cpp index 3afff78..873e08c 100644 --- a/mazeoflife.cpp +++ b/mazeoflife.cpp
@@ -59,6 +59,11 @@ int main(int, char**) {
59 game.state = std::make_unique<TitleState>(game); 59 game.state = std::make_unique<TitleState>(game);
60 60
61#ifdef __EMSCRIPTEN__ 61#ifdef __EMSCRIPTEN__
62 EM_ASM(FS.mkdir('/offline'); FS.mount(IDBFS, {}, '/offline');
63 FS.syncfs(true, function(err){
64 // Error
65 }););
66
62 emscripten_set_main_loop_arg(main_loop, &game, 0, 1); 67 emscripten_set_main_loop_arg(main_loop, &game, 0, 1);
63#else 68#else
64 for (;;) { 69 for (;;) {
diff --git a/mazeoflife.h b/mazeoflife.h index 8a31d07..d44f021 100644 --- a/mazeoflife.h +++ b/mazeoflife.h
@@ -15,7 +15,6 @@ struct Game {
15 15
16 sdl_wrapper sdl; 16 sdl_wrapper sdl;
17 ttf_wrapper ttf; 17 ttf_wrapper ttf;
18 net_wrapper net;
19 18
20 window_ptr window; 19 window_ptr window;
21 renderer_ptr renderer; 20 renderer_ptr renderer;
diff --git a/sdl.h b/sdl.h index 46d1fa4..4335ed2 100644 --- a/sdl.h +++ b/sdl.h
@@ -2,7 +2,6 @@
2#define SDL_H_A2226476 2#define SDL_H_A2226476
3 3
4#include <SDL.h> 4#include <SDL.h>
5#include <SDL_net.h>
6#include <SDL_ttf.h> 5#include <SDL_ttf.h>
7 6
8#include <memory> 7#include <memory>
@@ -17,11 +16,6 @@ class ttf_error : public std::logic_error {
17 ttf_error() : std::logic_error(TTF_GetError()) {} 16 ttf_error() : std::logic_error(TTF_GetError()) {}
18}; 17};
19 18
20class net_error : public std::logic_error {
21 public:
22 net_error() : std::logic_error(SDLNet_GetError()) {}
23};
24
25class sdl_wrapper { 19class sdl_wrapper {
26 public: 20 public:
27 sdl_wrapper() { 21 sdl_wrapper() {
@@ -50,20 +44,6 @@ class ttf_wrapper {
50 ~ttf_wrapper() { TTF_Quit(); } 44 ~ttf_wrapper() { TTF_Quit(); }
51}; 45};
52 46
53class net_wrapper {
54 public:
55 net_wrapper() {
56 if (SDLNet_Init() == -1) {
57 net_error ex;
58 SDLNet_Quit();
59
60 throw ex;
61 }
62 }
63
64 ~net_wrapper() { SDLNet_Quit(); }
65};
66
67class window_deleter { 47class window_deleter {
68 public: 48 public:
69 void operator()(SDL_Window* ptr) { SDL_DestroyWindow(ptr); } 49 void operator()(SDL_Window* ptr) { SDL_DestroyWindow(ptr); }
diff --git a/titlestate.cpp b/titlestate.cpp index b68e8fa..95bba6b 100644 --- a/titlestate.cpp +++ b/titlestate.cpp
@@ -14,10 +14,21 @@ std::unique_ptr<State> TitleState::operator()(Game& game) {
14 14
15 SDL_RenderClear(game.renderer.get()); 15 SDL_RenderClear(game.renderer.get());
16 SDL_RenderCopy(game.renderer.get(), background_.get(), NULL, NULL); 16 SDL_RenderCopy(game.renderer.get(), background_.get(), NULL, NULL);
17
18#ifdef __EMSCRIPTEN__
19 applyTexture(game.renderer.get(), pointer_.get(), 136,
20 selection_ == 0 ? 316 : (selection_ == 1 ? 350 : 381));
21
22 SDL_Rect eSpace = {164, 412, 79, 33};
23 SDL_SetRenderDrawColor(game.renderer.get(), 255, 255, 255, 255);
24 SDL_RenderFillRect(game.renderer.get(), &eSpace);
25#else
17 applyTexture(game.renderer.get(), pointer_.get(), 136, 26 applyTexture(game.renderer.get(), pointer_.get(), 136,
18 selection_ == 0 27 selection_ == 0
19 ? 316 28 ? 316
20 : (selection_ == 1 ? 350 : (selection_ == 2 ? 381 : 417))); 29 : (selection_ == 1 ? 350 : (selection_ == 2 ? 381 : 417)));
30#endif
31
21 SDL_RenderPresent(game.renderer.get()); 32 SDL_RenderPresent(game.renderer.get());
22 33
23 while (SDL_PollEvent(&e)) { 34 while (SDL_PollEvent(&e)) {
@@ -27,7 +38,11 @@ std::unique_ptr<State> TitleState::operator()(Game& game) {
27 } else if (e.type == SDL_KEYDOWN) { 38 } else if (e.type == SDL_KEYDOWN) {
28 if ((e.key.keysym.sym == SDLK_UP) && (selection_ != 0)) { 39 if ((e.key.keysym.sym == SDLK_UP) && (selection_ != 0)) {
29 selection_--; 40 selection_--;
41#ifdef __EMSCRIPTEN__
42 } else if ((e.key.keysym.sym == SDLK_DOWN) && (selection_ != 2)) {
43#else
30 } else if ((e.key.keysym.sym == SDLK_DOWN) && (selection_ != 3)) { 44 } else if ((e.key.keysym.sym == SDLK_DOWN) && (selection_ != 3)) {
45#endif
31 selection_++; 46 selection_++;
32 } else if (e.key.keysym.sym == SDLK_RETURN) { 47 } else if (e.key.keysym.sym == SDLK_RETURN) {
33 switch (selection_) { 48 switch (selection_) {
diff --git a/util.cpp b/util.cpp index 5947620..1bfa900 100644 --- a/util.cpp +++ b/util.cpp
@@ -32,6 +32,8 @@ font_ptr loadFont(int size) {
32std::string getDataFile() { 32std::string getDataFile() {
33#ifdef WINDOWS 33#ifdef WINDOWS
34 char* dir = getenv("USERPROFILE"); 34 char* dir = getenv("USERPROFILE");
35#elif __EMSCRIPTEN__
36 const char* dir = "/offline";
35#else 37#else
36 char* dir = getenv("HOME"); 38 char* dir = getenv("HOME");
37#endif 39#endif