Skip to content

Commit

Permalink
Sound, emscripten and breaking
Browse files Browse the repository at this point in the history
  • Loading branch information
cheyao committed Jan 3, 2025
1 parent da1620c commit da2f145
Show file tree
Hide file tree
Showing 18 changed files with 342 additions and 80 deletions.
9 changes: 6 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,10 @@ external/imgui/backends/imgui_impl_opengl3.cpp
)

set(ASSETS_EMBED_WEB
--preload-file=assets@assets
--preload-file=assets/fonts
--preload-file=assets/shaders
--preload-file=assets/strings
--preload-file=assets/textures
)

if(ANDROID STREQUAL ON)
Expand Down Expand Up @@ -250,9 +253,9 @@ elseif(WINDOWS)
elseif(WEB)
add_executable(${BUILD_NAME} ${SRC})

target_compile_options(${BUILD_NAME} PRIVATE -sUSE_SDL=3 -sUSE_FREETYPE=1 -DGLES -sNO_DISABLE_EXCEPTION_CATCHING -fno-rtti)
target_compile_options(${BUILD_NAME} PRIVATE -sUSE_SDL=3 -sUSE_FREETYPE=1 -DGLES -fno-rtti)
target_compile_options(${BUILD_NAME} PRIVATE ${ASSETS_EMBED_WEB})
target_link_options(${BUILD_NAME} PRIVATE -sUSE_SDL=3 -sUSE_FREETYPE=1 -sMIN_WEBGL_VERSION=2 -sMAX_WEBGL_VERSION=2 -sFULL_ES2 -sFULL_ES3 -sALLOW_MEMORY_GROWTH -sNO_DISABLE_EXCEPTION_CATCHING -fno-rtti)
target_link_options(${BUILD_NAME} PRIVATE -sUSE_SDL=3 -sUSE_FREETYPE=1 -sMIN_WEBGL_VERSION=2 -sMAX_WEBGL_VERSION=2 -sFULL_ES2 -sFULL_ES3 -sALLOW_MEMORY_GROWTH -fno-rtti -sFETCH)
target_link_options(${BUILD_NAME} PRIVATE ${ASSETS_EMBED_WEB})

if(DEBUG)
Expand Down
3 changes: 0 additions & 3 deletions assets/fonts/NotoSansCJK.ttc

This file was deleted.

Binary file added assets/sounds/subwoofer_lullaby.wav
Binary file not shown.
Binary file added assets/sounds/sweden.wav
Binary file not shown.
2 changes: 1 addition & 1 deletion assets/strings/strings.csv
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
id,comment,en,fr,zh-CN
AD0,,Did you know? This game was developed during 7+ months!,Tu savais ? Cette jeux été déveloper dans plus de 7 mois!,你知道吗?这个游戏的开发周期长于7个月!
AD1,,Did you know? This game uses with it's cusiom engine!,Tu savais ? Cette jeux utilise sa propre moteur de jeux!,你知道吗?这个游戏用的是自己的引擎!
AD1,,Did you know? This game uses it's cusiom engine!,Tu savais ? Cette jeux utilise sa propre moteur de jeux!,你知道吗?这个游戏用的是自己的引擎!
AD2,,Did you know? This game was coded entirely in C++!,Tu savais ? Cette jeux été crée entièrement avec C++!,你知道吗?这个游戏是完全用C++写的!
AD3,,Did you know? This game handles (almost) everything by itself!,Tu savais ? Cette jeux fait tout par lui même!,你知道吗?这个游戏自己掌控所有东西!
AD4,,Did you know? This game gets 1000+ FPS on natively!,Tu savais ? Cette jeux fait plus de 1000+ FPS!,你知道吗?这个游戏有1000+ FPS!
2 changes: 1 addition & 1 deletion cmake/index.html.in
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
<script type="text/javascript">
// Check for mobile user agent and scream at the user
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|BB|PlayBook|IEMobile|Windows Phone|Kindle|Silk|Opera Mini/i.test(navigator.userAgent)) {
window.alert("Mobile/Tablet support is not fully complete; this is intended for desktop usage.");
window.alert("Mobile/Tablet support is not fully complete; this is intended for desktop usage. Please watch video demo.");
}

const statusElement = document.getElementById('status');
Expand Down
3 changes: 2 additions & 1 deletion include/components/playerInventory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ class PlayerInventory : public CraftingInventory {
SDL_assert(cell < 9);
mSelect = cell;
}
std::size_t getSelection() const { return mSelect; }
[[nodiscard]] std::size_t getSelection() const { return mSelect; }
[[nodiscard]] Components::Item getItem() const { return mItems[mSelect]; }
void tryPlace(class Scene* scene, const Eigen::Vector2i& pos);

private:
Expand Down
2 changes: 2 additions & 0 deletions include/game.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,6 @@ class Game {

std::uint64_t mTicks;
EntityID mPlayer;

SDL_AudioStream* mStream;
};
22 changes: 21 additions & 1 deletion include/items.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,25 @@
#include <cstdint>

namespace Components {
enum class Item : std::uint64_t { AIR = 0, GRASS_BLOCK, STONE, OAK_LOG, OAK_LEAVES, OAK_PLANKS, STICK, DIRT, CRAFTING_TABLE };
enum class Item : std::uint64_t {
AIR = 0,
GRASS_BLOCK,
STONE,
OAK_LOG,
OAK_LEAVES,
OAK_PLANKS,
STICK,
DIRT,
CRAFTING_TABLE,
WOODEN_PICKAXE,
WOODEN_AXE,
WOODEN_SHOVEL,
WOODEN_HOE,
STONE_PICKAXE,
STONE_AXE,
STONE_SHOVEL,
STONE_HOE,
COBBLESTONE,
FURNACE,
};
} // namespace Components
8 changes: 7 additions & 1 deletion include/registers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,19 @@ consteval inline static auto AIR() { return static_cast<Item>(0); }
} // namespace Components

namespace registers {
enum class MiningSystem;

extern const std::unordered_map<Components::Item, std::string> TEXTURES;
extern const std::unordered_map<Components::Item, std::uint64_t> BREAK_TIMES;
extern const std::unordered_map<Components::Item, std::pair<int, std::uint64_t>> BREAK_TIMES;
extern const std::unordered_map<Components::Item, int> MINING_LEVEL;
extern const std::unordered_map<Components::Item, registers::MiningSystem> MINING_SYSTEM;

extern const std::vector<std::pair<float, std::vector<std::pair<Components::Item, Eigen::Vector2i>>>>
SURFACE_STRUCTURES;
extern const std::unordered_map<Components::Item, std::vector<std::pair<float, Components::Item>>> LOOT_TABLES;
extern const std::vector<std::tuple<std::pair<std::uint64_t, std::uint64_t>, std::vector<Components::Item>,
std::pair<std::uint64_t, Components::Item>>>
CRAFTING_RECIPIES;
extern const std::unordered_map<Components::Item, class Screen*(*)(void)> CLICKABLES;
extern const std::vector<std::string> BACKGROUND_SOUNDS;
} // namespace registers
21 changes: 13 additions & 8 deletions src/components/crafting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@
// TODO: Save crafting table
CraftingInventory::CraftingInventory(class Game* game, std::uint64_t size, std::uint64_t row, std::uint64_t col)
: Inventory(game, size), mRows(row), mCols(col), mCraftingItems(row * col, Components::AIR()),
mCraftingCount(row * col, 0), mLastCraft(0), mGridX(98), mGridY(114), mOutX(56), mOutY(9) {
mCraftingCount(row * col, 0), mLastCraft(0), mGridX(97), mGridY(114), mOutX(57), mOutY(9) {
mCountRegister[getID<CraftingInventory>()] = &mCraftingCount;
mItemRegister[getID<CraftingInventory>()] = &mCraftingItems;
}

CraftingInventory::CraftingInventory(class Game* game, const rapidjson::Value& contents, std::uint64_t row,
std::uint64_t col)
: Inventory(game, contents), mRows(row), mCols(col), mCraftingItems(), mCraftingCount(), mLastCraft(0),
mGridX(98), mGridY(114), mOutX(56), mOutY(9) {
mGridX(97), mGridY(114), mOutX(57), mOutY(9) {
mCountRegister[getID<CraftingInventory>()] = &mCraftingCount;
mItemRegister[getID<CraftingInventory>()] = &mCraftingItems;

Expand Down Expand Up @@ -291,8 +291,6 @@ bool CraftingInventory::checkRecipie(const std::uint64_t r) {
for (const auto& [item, count] : std::views::zip(mCraftingItems, mCraftingCount)) {
// The current cell is empty
if (item == Components::AIR()) {
SDL_assert(count == 0);

continue;
}

Expand Down Expand Up @@ -329,17 +327,24 @@ bool CraftingInventory::checkRecipie(const std::uint64_t r) {
for (std::size_t yoff = 0; yoff <= (mRows - shape.second); ++yoff) {
for (std::size_t x = 0; x < shape.first; ++x) {
for (std::size_t y = 0; y < shape.second; ++y) {
if (items[x + y * shape.first] == Components::AIR()) {
continue;
}

if (items[x + y * shape.first] !=
mCraftingItems[(x + xoff) + (y + yoff) * mCols]) {
goto breakinnerloop;
}
}
}

// Clear all air
while (true) {
auto i = std::ranges::find(items, Components::AIR());
if (i == items.end()) {
break;
}
std::swap(*i, items.back());
items.pop_back();
--toFind;
}

for (const auto& [item, count] : std::views::zip(mCraftingItems, mCraftingCount)) {
// The current cell is empty
if (item == Components::AIR()) {
Expand Down
11 changes: 10 additions & 1 deletion src/components/inventory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@ Inventory::Inventory(class Game* game, const rapidjson::Value& contents) : Scree
// A view of the main inventory
Inventory::Inventory(const Eigen::Vector2f& offset, const std::string& texture)
: Screen(Game::getInstance()), INVENTORY_SLOTS_OFFSET_X(offset.x()), INVENTORY_SLOTS_OFFSET_Y(offset.y()),
INVENTORY_SPRITE_FILE(texture) {}
INVENTORY_SPRITE_FILE(texture) {
mCountRegister[getID<Inventory>()] = &mCount;
mItemRegister[getID<Inventory>()] = &mItems;
}

void Inventory::save(rapidjson::Value& contents, rapidjson::Document::AllocatorType& allocator) {
contents.AddMember(rapidjson::StringRef(SIZE_KEY), mSize, allocator);
Expand Down Expand Up @@ -153,6 +156,12 @@ bool Inventory::update(class Scene* scene, float) {
}
} else {
for (const auto& [id, s] : mPath) {
if (mCountRegister[id]->size() <= s) {
SDL_Log("Patherror! Ignoring place for %" PRIu64 " at %" PRIu64, id, s);

continue;
}

(*mCountRegister[id])[s] += 1;
(*mItemRegister[id])[s] = static_cast<Components::Item>(scene->mMouse.item);
scene->mMouse.count--;
Expand Down
111 changes: 95 additions & 16 deletions src/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,55 @@
#include "scene.hpp"
#include "scenes/level.hpp"
#include "third_party/glad/glad.h"
#include "utils.hpp"

#include <SDL3/SDL.h>
#include <SDL3/SDL_audio.h>
#include <chrono>
#include <cinttypes>
#include <cstdint>
#include <memory>
#include <sstream>
#include <string>
#include <string_view>
#include <utility>

#ifdef IMGUI
#include <backends/imgui_impl_opengl3.h>
#include <backends/imgui_impl_sdl3.h>
#include <imgui.h>
#endif

std::vector<std::pair<std::uint8_t*, std::uint32_t>> mAudio;

#ifdef __EMSCRIPTEN__
#include <emscripten/fetch.h>

void audioSucceeded(emscripten_fetch_t* fetch) {
SDL_Log("Finished downloading %llu bytes from URL %s.\n", fetch->numBytes, fetch->url);
// The data is now available at fetch->data[0] through fetch->data[fetch->numBytes-1];
SDL_AudioSpec spec;
std::uint8_t* wav_data;
std::uint32_t wav_data_len;

if (SDL_LoadWAV_IO(SDL_IOFromConstMem(fetch->data, fetch->numBytes), true, &spec, &wav_data, &wav_data_len)) {
mAudio.emplace_back(std::make_pair(wav_data, wav_data_len));
} else {
SDL_Log("Couldn't load .wav file: %s", SDL_GetError());
}

emscripten_fetch_close(fetch);
}

void audioFailed(emscripten_fetch_t* fetch) {
SDL_Log("Downloading %s failed, HTTP failure status code: %d.\n", fetch->url, fetch->status);
emscripten_fetch_close(fetch); // Also free data on failure.
}
#endif

Game::Game()
: mEventManager(nullptr), mSystemManager(nullptr), mLocaleManager(nullptr), mCurrentLevel(nullptr),
mStorageManager(nullptr), mTicks(0) {}
mStorageManager(nullptr), mTicks(0), mStream(nullptr) {}

void Game::init() {
const auto begin = std::chrono::high_resolution_clock::now();
Expand Down Expand Up @@ -53,6 +83,44 @@ void Game::init() {
<< std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count() << "milis)";
SDL_Log("Startup took %s", time.str().data());

// static auto sign = Inventory(Eigen::Vector2f(0, 0), "ui/sign.png");
// mSystemManager->getUISystem()->addScreen(&sign);

SDL_AudioSpec spec;

#ifdef __EMSCRIPTEN__
emscripten_fetch_attr_t attr;
emscripten_fetch_attr_init(&attr);
strcpy(attr.requestMethod, "GET");
attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
attr.onsuccess = audioSucceeded;
attr.onerror = audioFailed;
emscripten_fetch(&attr, "assets/sounds/subwoofer_lullaby.wav");
emscripten_fetch(&attr, "assets/sounds/sweden.wav");
#else
std::uint8_t* wav_data;
std::uint32_t wav_data_len;

if (SDL_LoadWAV((getBasePath() + "assets/sounds/subwoofer_lullaby.wav").data(), &spec, &wav_data,
&wav_data_len)) {
mAudio.emplace_back(std::make_pair(wav_data, wav_data_len));
} else {
SDL_Log("Couldn't load .wav file: %s", SDL_GetError());
}

if (SDL_LoadWAV((getBasePath() + "assets/sounds/sweden.wav").data(), &spec, &wav_data, &wav_data_len)) {
mAudio.emplace_back(std::make_pair(wav_data, wav_data_len));
} else {
SDL_Log("Couldn't load .wav file: %s", SDL_GetError());
}
#endif

spec.format = SDL_AUDIO_S16LE;
spec.channels = 2;
spec.freq = 48000;
mStream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, nullptr, nullptr);
SDL_ResumeAudioStreamDevice(mStream);

#ifdef DEBUG
GLenum err = 0;
while ((err = glGetError()) != GL_NO_ERROR) {
Expand All @@ -61,17 +129,17 @@ void Game::init() {
SDL_LogError(SDL_LOG_CATEGORY_RENDER, "\033[31mInit GLError: Invalid enum\033[0m");
break;
case GL_INVALID_VALUE:
SDL_LogError(SDL_LOG_CATEGORY_RENDER, "\x1B[31mInit GLError: Invalid value\033[0m");
SDL_LogError(SDL_LOG_CATEGORY_RENDER, "\033[31mInit GLError: Invalid value\033[0m");
break;
case GL_INVALID_OPERATION:
SDL_LogError(SDL_LOG_CATEGORY_RENDER, "\x1B[31mInit GLError: Invalid operation\033[0m");
SDL_LogError(SDL_LOG_CATEGORY_RENDER, "\033[31mInit GLError: Invalid operation\033[0m");
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
SDL_LogError(SDL_LOG_CATEGORY_RENDER,
"\x1B[31mInit GLError: Invalid framebuffer op\033[0m");
"\033[31mInit GLError: Invalid framebuffer op\033[0m");
break;
case GL_OUT_OF_MEMORY:
SDL_LogError(SDL_LOG_CATEGORY_RENDER, "\x1B[31mInit GLError: Out of memory\033[0m");
SDL_LogError(SDL_LOG_CATEGORY_RENDER, "\033[31mInit GLError: Out of memory\033[0m");
break;
}
}
Expand All @@ -86,11 +154,32 @@ Game::~Game() {
ImGui_ImplSDL3_Shutdown();
ImGui::DestroyContext();
#endif

for (const auto [data, _] : mAudio) {
SDL_free(data);
}

SDL_Quit();
}

void Game::save() { mStorageManager->save(); }

SDL_AppResult Game::iterate() {
static std::size_t audioPtr = 0;
if (mStream && !mAudio.empty()) {
if (SDL_GetAudioStreamAvailable(mStream) < static_cast<int>(mAudio[audioPtr].second)) {
SDL_PutAudioStreamData(mStream, mAudio[audioPtr].first, mAudio[audioPtr].second);
SDL_ResumeAudioStreamDevice(mStream);

++audioPtr;
if (audioPtr == mAudio.size()) {
audioPtr = 0;
}

SDL_Log("Replenished sound buffer");
}
}

const auto begin = std::chrono::high_resolution_clock::now();

float delta = static_cast<float>(SDL_GetTicks() - mTicks) / 1000.0f;
Expand Down Expand Up @@ -170,15 +259,11 @@ void Game::gui() {

static bool wireframe = false;
static bool vsync = true;
static bool demo = false;

/* Main menu */ {
ImGui::Begin("Developer menu");

ImGuiIO& io = ImGui::GetIO();
ImGui::Text("%.3f ms %.1f FPS", (1000.f / io.Framerate), io.Framerate);

const char* locales[] = {"en", "fr", "zh-CN"};
const char* locales[] = {"en", "fr"};
static int current = 0;
if (ImGui::Combo("language", &current, locales, IM_ARRAYSIZE(locales))) {
mLocaleManager->changeLocale(locales[current]);
Expand All @@ -195,14 +280,8 @@ void Game::gui() {
}
}

ImGui::Checkbox("Demo", &demo);

ImGui::End();
}

if (demo) {
ImGui::ShowDemoWindow(&demo);
}
#endif
}

Expand Down
4 changes: 1 addition & 3 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ SDL_AppResult SDL_AppInit(void** appstate, int, char**) {
SDL_SetHint(SDL_HINT_RENDER_VSYNC, "1");
SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_CENTER, "0");

if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) {
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD | SDL_INIT_AUDIO)) {
SDL_LogCritical(SDL_LOG_CATEGORY_VIDEO, "Failed to init SDL: %s\n", SDL_GetError());
ERROR_BOX("Failed to initialize SDL, there is something wrong with your system");

Expand Down Expand Up @@ -116,8 +116,6 @@ SDL_AppResult SDL_AppIterate(void* appstate) {
void SDL_AppQuit(void*, const SDL_AppResult result) {
Game::getInstance()->save();

SDL_Quit();

if (result == SDL_APP_SUCCESS) {
SDL_Log("Cyao game engine terminated successfully");
} else {
Expand Down
Loading

0 comments on commit da2f145

Please sign in to comment.