Compare commits

..

15 Commits

15 changed files with 160 additions and 80 deletions

View File

@@ -1,8 +1,9 @@
[target.wasm32-unknown-emscripten]
# TODO: Document what the fuck this is.
rustflags = [
"-O", "-C", "link-args=-O2 --profiling",
#"-C", "link-args=-O3 --closure 1",
"-C", "link-args=-sASYNCIFY -sALLOW_MEMORY_GROWTH=1",
"-C", "link-args=-sUSE_SDL=2 -sUSE_SDL_IMAGE=2 -sSDL2_IMAGE_FORMATS=['png']",
"-C", "link-args=--preload-file assets/ -lidbfs.js",
"-C", "link-args=--preload-file assets/",
]

View File

@@ -1,13 +1,13 @@
name: Github Pages
on: [push]
permissions:
contents: write
jobs:
build-github-pages:
deploy:
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
steps:
- uses: actions/checkout@v2 # repo checkout

7
BUILD.md Normal file
View File

@@ -0,0 +1,7 @@
# Building Pac-Man
## GitHub Actions Workflow
1. Build workflow produces executables & WASM files for all platforms
2. Uploaded as artifacts
3. Deployment workflow downloads artifacts and uploads to GitHub Pages

View File

@@ -23,6 +23,9 @@ at.
- More than 4 ghosts
- Custom Level Generation
- Multi-map tunnelling
- Online Scoreboard
- WebAssembly build contains a special API key for communicating with server.
- To prevent abuse, the server will only accept scores from the WebAssembly build.
## Installation

54
STORY.md Normal file
View File

@@ -0,0 +1,54 @@
# Story
This is living document that describes the story of the project, from inspiration to solution.
When a website is available, this document will help curate it's content.
## Inspiration
I initially got the idea for this project after finding a video about another Pac-Man clone on YouTube.
[![Code Review Thumbnail][code-review-thumbnail]][code-review-video]
This implementation was written in C++, used SDL2 for graphics, and was kinda weird - but it worked.
- I think it was weird because the way it linked files together is extremely non-standard.
Essentially, it was a single file that included all the other files. This is not how C++ projects are typically structured.
- This implementation was also extremely dependent on OOP; Rust has no real counterpart for OOP code, so writing my own implementation would be a challenge.
## Lifetimes
Rust's SDL2 implementation is a wrapper around the C library, so it's not as nice as the C++ implementation.
Additionally, lifetimes in this library are a bit weird, making them quite difficult to deal with.
I found a whole blog post complaining about this ([1][fighting-lifetimes-1], [2][fighting-lifetimes-2], [3][fighting-lifetimes-3]), so I'm not alone in this.
## Emscripten & RuggRogue
One of the targets for this project is to build a web-accessible version of the game. If you were watching at all during
the Rust hype, one of it's primary selling points was a growing community of Rust-based web applications, thanks to
WebAssembly.
The problem is that much of this work was done for pure-Rust applications - and SDL is C++.
This requires a C++ WebAssembly compiler such as Emscripten; and it's a pain to get working.
Luckily though, someone else has done this before, and they fully documented it - [RuggRouge][ruggrouge].
- Built with Rust
- Uses SDL2
- Compiling for WebAssembly with Emscripten
- Also compiles for Windows & Linux
This repository has been massively helpful in getting my WebAssembly builds working.
## Key Capturing Extensions in WASM Build
Some extensions I had installed were capturing keys.
The issue presented with some keys never being sent to the application.
To confirm, enter safe mode or switch to a different browser without said extensions.
If the issue disappears, it's because of an extension in your browser stealing keys in a way that is incompatible with the batshit insanity of Emscripten.
[code-review-video]: https://www.youtube.com/watch?v=OKs_JewEeOo
[code-review-thumbnail]: https://img.youtube.com/vi/OKs_JewEeOo/hqdefault.jpg
[fighting-lifetimes-1]: https://devcry.heiho.net/html/2022/20220709-rust-and-sdl2-fighting-with-lifetimes.html
[fighting-lifetimes-2]: https://devcry.heiho.net/html/2022/20220716-rust-and-sdl2-fighting-with-lifetimes-2.html
[fighting-lifetimes-3]: https://devcry.heiho.net/html/2022/20220724-rust-and-sdl2-fighting-with-lifetimes-3.html
[ruggrogue]: https://tung.github.io/ruggrogue/

View File

@@ -3,16 +3,22 @@
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>
<style>
body {
margin: 0;
padding: 0;
background: #000;
}
canvas {
display: block;
margin: 0 auto;
background: #000;
}
</style>
<body>
<canvas id="canvas"></canvas>
<script>
var Module = {
'locateFile': function (path, prefix) {
if (path.endsWith(".data")) {
return prefix + "deps/" + path;
}
return prefix + path;
},
'canvas': document.getElementById('canvas'),
};
</script>

View File

@@ -1,7 +0,0 @@
& cargo build --target=wasm32-unknown-emscripten --release
mkdir -p dist -Force
cp ./target/wasm32-unknown-emscripten/release/Pac_Man.wasm ./dist
cp ./target/wasm32-unknown-emscripten/release/Pac-Man.js ./dist
cp index.html dist

View File

@@ -1,11 +1,13 @@
#!/bin/sh
set -eux
# set -eu
echo "Building WASM with Emscripten"
cargo build --target=wasm32-unknown-emscripten --release
echo "Copying release files to dist/"
mkdir -p dist
cp target/wasm32-unknown-emscripten/release/pacman.wasm dist
cp target/wasm32-unknown-emscripten/release/pacman.js dist
cp target/wasm32-unknown-emscripten/release/deps/pacman.data dist/deps
output_folder="target/wasm32-unknown-emscripten/release"
cp $output_folder/pacman.wasm dist
cp $output_folder/pacman.js dist
cp $output_folder/deps/pacman.data dist
cp assets/index.html dist

58
scripts/build-windows.sh Executable file
View File

@@ -0,0 +1,58 @@
#!/bin/bash
set -eu
SDL_VERSION="2.28.3"
SDL_IMAGE_VERSION="2.6.3"
SDL_MIXER_VERSION="2.6.3"
SDL_TTF_VERSION="2.20.2"
SDL="https://github.com/libsdl-org/SDL/releases/download/release-${SDL_VERSION}/SDL2-devel-${SDL_VERSION}-mingw.tar.gz"
SLD_IMAGE="https://github.com/libsdl-org/SDL_image/releases/download/release-${SDL_IMAGE_VERSION}/SDL2_image-devel-${SDL_IMAGE_VERSION}-mingw.tar.gz"
SDL_MIXER="https://github.com/libsdl-org/SDL_mixer/releases/download/release-${SDL_MIXER_VERSION}/SDL2_mixer-devel-${SDL_MIXER_VERSION}-mingw.tar.gz"
SDL_TTF="https://github.com/libsdl-org/SDL_ttf/releases/download/release-${SDL_TTF_VERSION}/SDL2_ttf-devel-${SDL_TTF_VERSION}-mingw.tar.gz"
EXTRACT_DIR="./target/x86_64-pc-windows-gnu/release/deps"
if [ ! -f $EXTRACT_DIR/libSDL2.a ]; then
if [ ! -f ./sdl2.tar.gz ]; then
echo "Downloading SDL2@$SDL_VERSION..."
curl -L -o ./sdl2.tar.gz $SDL
fi
echo "Extracting SDL2..."
tar -xzf ./sdl2.tar.gz -C $EXTRACT_DIR --strip-components=3 "SDL2-$SDL_VERSION/x86_64-w64-mingw32/lib/libSDL2.a"
rm -f ./sdl2.tar.gz
fi
if [ ! -f $EXTRACT_DIR/libSDL2_image.a ]; then
if [ ! -f ./sdl2_image.tar.gz ]; then
echo "Downloading SDL2_image@$SDL_IMAGE_VERSION..."
curl -L -o ./sdl2_image.tar.gz $SLD_IMAGE
fi
echo "Extracting SDL2_image..."
tar -xzf ./sdl2_image.tar.gz -C $EXTRACT_DIR --strip-components=3 "SDL2_image-$SDL_IMAGE_VERSION/x86_64-w64-mingw32/lib/libSDL2_image.a"
fi
rm -f ./sdl2_image.tar.gz
if [ ! -f $EXTRACT_DIR/libSDL2_mixer.a ]; then
if [ ! -f ./sdl2_mixer.tar.gz ]; then
echo "Downloading SDL2_mixer@$SDL_MIXER_VERSION..."
curl -L -o ./sdl2_mixer.tar.gz $SDL_MIXER
fi
echo "Extracting SDL2_mixer..."
tar -xzf ./sdl2_mixer.tar.gz -C $EXTRACT_DIR --strip-components=3 "SDL2_mixer-$SDL_MIXER_VERSION/x86_64-w64-mingw32/lib/libSDL2_mixer.a"
rm -f ./sdl2_mixer.tar.gz
fi
if [ ! -f $EXTRACT_DIR/libSDL2_ttf.a ]; then
if [ ! -f ./sdl2_ttf.tar.gz ]; then
echo "Downloading SDL2_ttf@$SDL_TTF_VERSION..."
curl -L -o ./sdl2_ttf.tar.gz $SDL_TTF
fi
echo "Extracting SDL2_ttf..."
tar -xzf ./sdl2_ttf.tar.gz -C $EXTRACT_DIR --strip-components=3 "SDL2_ttf-$SDL_TTF_VERSION/x86_64-w64-mingw32/lib/libSDL2_ttf.a"
rm -f ./sdl2_ttf.tar.gz
fi
echo "Building..."
cargo zigbuild --release --target x86_64-pc-windows-gnu

9
scripts/clean-windows.sh Executable file
View File

@@ -0,0 +1,9 @@
#!/bin/bash
set -eux
echo "Cleaning library files from ./target/x86_64-pc-windows-gnu/release/deps"
rm -f ./target/x86_64-pc-windows-gnu/release/deps/libSDL2.a
rm -f ./target/x86_64-pc-windows-gnu/release/deps/libSDL2_image.a
rm -f ./target/x86_64-pc-windows-gnu/release/deps/libSDL2_mixer.a
rm -f ./target/x86_64-pc-windows-gnu/release/deps/libSDL2_ttf.a
echo "Done."

View File

@@ -1,3 +0,0 @@
& ./build.ps1
cd ./dist
python -m http.server

View File

@@ -92,6 +92,7 @@ impl<'a> AnimatedTexture<'a> {
direction: Direction,
frame: u32,
) {
// TODO: If the frame we're targeting is in the opposite direction (due to self.reverse), we should pre-emptively reverse.
let current = self.current_frame();
self.render_static(canvas, position, direction, Some(current));

View File

@@ -1,49 +0,0 @@
// taken from https://github.com/Gigoteur/PX8/blob/master/src/px8/emscripten.rs
#[cfg(target_os = "emscripten")]
pub mod emscripten {
use std::cell::RefCell;
use std::ffi::{CStr, CString};
use std::os::raw::{c_char, c_float, c_int, c_void};
use std::ptr::null_mut;
#[allow(non_camel_case_types)]
type em_callback_func = unsafe extern "C" fn();
extern "C" {
// void emscripten_set_main_loop(em_callback_func func, int fps, int simulate_infinite_loop)
pub fn emscripten_set_main_loop(
func: em_callback_func,
fps: c_int,
simulate_infinite_loop: c_int,
);
pub fn emscripten_cancel_main_loop();
pub fn emscripten_pause_main_loop();
pub fn emscripten_get_now() -> c_float;
}
thread_local!(static MAIN_LOOP_CALLBACK: RefCell<*mut c_void> = RefCell::new(null_mut()));
pub fn set_main_loop_callback<F>(callback: F)
where
F: FnMut(),
{
MAIN_LOOP_CALLBACK.with(|log| {
*log.borrow_mut() = &callback as *const _ as *mut c_void;
});
unsafe {
emscripten_set_main_loop(wrapper::<F>, -1, 1);
}
unsafe extern "C" fn wrapper<F>()
where
F: FnMut(),
{
MAIN_LOOP_CALLBACK.with(|z| {
let closure = *z.borrow_mut() as *mut F;
(*closure)();
});
}
}
}

View File

@@ -5,7 +5,6 @@ use sdl2::keyboard::Keycode;
use sdl2::render::{Texture, TextureCreator};
use sdl2::video::WindowContext;
use sdl2::{pixels::Color, render::Canvas, video::Window};
use tracing::event;
use crate::constants::{MapTile, BOARD_HEIGHT, BOARD_WIDTH, RAW_BOARD};
use crate::direction::Direction;

View File

@@ -17,15 +17,13 @@ mod map;
mod modulation;
mod pacman;
#[cfg(target_os = "emscripten")]
mod emscripten;
pub fn main() {
let sdl_context = sdl2::init().unwrap();
let video_subsystem = sdl_context.video().unwrap();
// Setup tracing
let subscriber = tracing_subscriber::fmt()
.with_ansi(cfg!(not(target_os = "emscripten")))
.with_max_level(tracing::Level::DEBUG)
.finish()
.with(ErrorLayer::default());
@@ -77,6 +75,7 @@ pub fn main() {
// TODO: Fix key repeat delay issues by using VecDeque for instant key repeat
for event in event_pump.poll_iter() {
match event {
// Handle quitting keys or window close
Event::Quit { .. }
@@ -146,7 +145,7 @@ pub fn main() {
true
};
loop {
if !main_loop() {
break;