Anut-py / h-raylib

Haskell bindings for raylib
https://hackage.haskell.org/package/h-raylib
Apache License 2.0
81 stars 14 forks source link

Web build support #4

Open Anut-py opened 1 year ago

Anut-py commented 1 year ago

Set up h-raylib to work with emscripten and support building to WebAssembly.

Anut-py commented 1 year ago

This will be possible when the ghc WebAssembly backend adds support for JS FFI. Then the C code can be compiled with emscripten and the Haskell can be compiled to wasm.

Ashe commented 1 year ago

Do we know if GHC has made any progress on this? I feel like h-raylib could handle this this could become a really simple and universal method of getting into haskell gamedev. I feel like it'd be a huge challenge to get this working and I wouldn't know where to begin, but if you think it's possible then there's hope maybe? :)

Anut-py commented 1 year ago

Web building with GHC is still a somewhat complex process (this has the instructions. It seems a lot simpler than it was when I created this issue though).

What I had in mind for this was to compile the Raylib C code using emscripten (as described here), which would output JS equivalents to the raylib functions. Then the Haskell code would be compiled using GHC's web build support. The main problem is finding a way to link the two together (i.e. call the JS functions from Haskell). I found this article about it (the article is about Asterius, which has since been merged into GHC), so it should be possible.

Anut-py commented 1 year ago

Update: I was able to make a simple example using haskell to call JS functions. Once it's cleaned up a bit I'll create a repository for it.

Anut-py commented 1 year ago

Here's the proof-of-concept repository: https://github.com/Anut-py/ghc-web-test

As I said, the Haskell-to-WASM compilation process is a bit of a hassle, but it's relatively simple with Nix. Now that this is done I have to figure out how to call the raylib functions through WASM.

Anut-py commented 1 year ago

I integrated raylib into the repository above. I'll add this to h-raylib as soon as possible.

Anut-py commented 9 months ago

I have (in the web branch) added experimental web support and converted the basic-window example into a web-compatible example. Unfortunately, ghc's wasm backend does not yet support Template Haskell, which is required for the changes I made*, so we'll have to wait a bit. TH support for the wasm backend is being worked on, and you can track the progress here.

* The changes to support the web actually don't require TH; I made a TH function to make writing h-raylib/web programs easier, which is why Template Haskell support is necessary. See Raylib.Util.raylibApplication and the basic-window example for more (in the web branch, of course).

realbogart commented 7 months ago

First of all, you are doing great work here! I'm very excited about the possibility of running a Haskell/raylib game in the browser.

The changes to support the web actually don't require TH; I made a TH function to make writing h-raylib/web programs easier, which is why Template Haskell support is necessary. See Raylib.Util.raylibApplication and the basic-window example for more (in the web branch, of course).

Does this mean that it's possible to skip raylibApplication to get web working without template haskell? I'm eager to get the web support :smile:

Thanks!

Anut-py commented 7 months ago

It’s been a while since I made that comment. At the moment, h-raylib uses a lot of template haskell internally, so I don’t think it is possible to get it to work. Afaik we’ll just have to wait for ghc wasm support for template haskell.

Anut-py commented 4 weeks ago

The ghc merge request looks like it will be merged soon

GunpowderGuy commented 2 weeks ago

The request has been merged. Do you think h raylib could get to work on the web shortly by any chance? Next raylib game jam will require running on that platform

Anut-py commented 2 weeks ago

Yeah, the merge request has been merged and I see a new commit in ghc-wasm-meta. I’ll test it out today and figure out how many bugs there are (hopefully few enough that I can fix them before the game jam)

Anut-py commented 2 weeks ago

I faced another issue when I tried it out (see https://gitlab.haskell.org/ghc/ghc-wasm-meta/-/issues/16). I don't think this will be fixed before the gamejam.

metiulekm commented 2 weeks ago

I got the h-raylib-web-template working. I used the 538905f4b6e91a80cff5359b4f04d034b7882b6d commit (the one with Raylib vendored into it). I needed to add allow-newer like you said in the GHC tracker, but also needed to set shared: True and use different (de)allocation functions:

diff --git a/build_haskell.sh b/build_haskell.sh
old mode 100644
new mode 100755
index 349c6ba..7ab35c3
--- a/build_haskell.sh
+++ b/build_haskell.sh
@@ -13,5 +13,5 @@ echo $'\nBuilt successfully!\n'
 echo "Moving project binary..."
 mkdir -p ../public
 rm -f ../public/haskell.wasm
-cp ./dist-newstyle/build/wasm32-wasi/ghc-*/haskell-0.1.0.0/x/haskell/build/haskell/haskell.wasm ../public/haskell.wasm
+cp ./dist-newstyle/build/wasm32-wasi/ghc-*/haskell-0.1.0.0/x/haskell/opt/build/haskell/haskell.wasm ../public/haskell.wasm
 echo "Binary moved successfully!"
diff --git a/build_raylib.sh b/build_raylib.sh
old mode 100644
new mode 100755
index a0e90d2..1c862f6
--- a/build_raylib.sh
+++ b/build_raylib.sh
@@ -28,7 +28,7 @@ fi

 echo $'Compiling bindings...'

-emcc cbits/rl_bindings.c raylib/src/libraylib.a \
+$EMCC cbits/rl_bindings.c raylib/src/libraylib.a \
   -Iraylib/src \
   -DPLATFORM_WEB \
   -DGRAPHICS_API_OPENGL_ES2 \
diff --git a/haskell/cabal.project b/haskell/cabal.project
index 4c9c76c..8dabf51 100644
--- a/haskell/cabal.project
+++ b/haskell/cabal.project
@@ -4,3 +4,6 @@ optimization: 2

 package h-raylib
   flags: -detect-platform +platform-web
+
+allow-newer: *:time
+shared: True
diff --git a/src/index.ts b/src/index.ts
index 7484869..7df3ff9 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -172,7 +172,7 @@ window.addEventListener("load", () => {
           const result = raylib[name](...params);
           if (returnSizeBytes === 0) return;

-          const ptr = raylib._malloc(returnSizeBytes);
+          const ptr = raylib._MemAlloc_(returnSizeBytes);
           const signed = returnType === ParamTypes.SIGNED_INT;
           if (returnSizeBytes === 1)
             (signed ? heaps.HEAP8 : heaps.HEAPU8)[ptr] = result;
@@ -188,11 +188,11 @@ window.addEventListener("load", () => {

           return ptr;
         },
-        // Frees a pointer; a pointer created with `raylib._malloc`cannot be
+        // Frees a pointer; a pointer created with `raylib._MemAlloc_`cannot be
         // directly freed in haskell (with `Foreign.Marshal.Alloc.free`), so
         // this function is called from haskell instead.
         free: (ptr: number) => {
-          raylib._free(ptr);
+          raylib._MemFree_(ptr);
         },
         memory: memory,
       },
Anut-py commented 1 week ago

Thank you @metiulekm, that worked.

@GunpowderGuy you can use h-raylib-web-template, but it's very buggy at the moment. So far I've found one major bug, updateCamera doesn't work. If you plan on making a 3d game you could try implementing it yourself in Haskell by following rcamera.h.