diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml new file mode 100644 index 00000000..62f20563 --- /dev/null +++ b/.github/workflows/appimage.yml @@ -0,0 +1,37 @@ +name: C/C++ AppImage + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build-appimage: + + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v2 + with: + submodules: 'true' + - name: install dependencies + run: | + sudo apt-get update + sudo apt-get install -y build-essential libglfw3-dev libglfw3 libglew-dev libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev cmake squashfs-tools + sudo ln -s /usr/lib/x86_64-linux-gnu/libluajit-5.1.a /usr/lib/x86_64-linux-gnu/liblua5.1.a + sudo ln -s /usr/include/luajit-2.1 /usr/include/lua + - name: configure + run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DVOXELENGINE_BUILD_APPDIR=1 + - name: build + run: cmake --build build -t install + - name: Build AppImage + uses: AppImageCrafters/build-appimage-action@fe2205a4d6056be47051f7b1b3811106e9814910 + env: + UPDATE_INFO: gh-releases-zsync|MihailRis|VoxelEngine-Cpp|latest|*x86_64.AppImage.zsync + with: + recipe: dev/AppImageBuilder.yml + - uses: actions/upload-artifact@v2 + with: + name: AppImage + path: './*.AppImage*' diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 0d8237ff..502dd486 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -21,9 +21,15 @@ jobs: - uses: actions/checkout@v3 - name: Install packages + # If libluajit-5.1-dev is not available, use this: + # git clone https://luajit.org/git/luajit.git + # cd luajit + # make && make install INSTALL_INC=/usr/include/lua run: | sudo apt-get update - sudo apt-get install libglfw3-dev libglfw3 libglew-dev libglm-dev libpng-dev libopenal-dev + sudo apt-get install libglfw3-dev libglfw3 libglew-dev libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev + sudo ln -s /usr/lib/x86_64-linux-gnu/libluajit-5.1.a /usr/lib/x86_64-linux-gnu/liblua-5.1.a + sudo ln -s /usr/include/luajit-2.1 /usr/include/lua - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. diff --git a/.gitignore b/.gitignore index a722d642..cae9b8a1 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,7 @@ Debug/voxel_engine .project .git /Default/ +AppDir +appimage-build/ + +*~ diff --git a/CMakeLists.txt b/CMakeLists.txt index d8b2f632..2702c218 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,8 @@ cmake_minimum_required(VERSION 3.15) project(VoxelEngine) +option(VOXELENGINE_BUILD_APPDIR OFF) + set(CMAKE_CXX_STANDARD 17) file(GLOB_RECURSE HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/*.hpp) @@ -8,6 +10,25 @@ file(GLOB_RECURSE SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) add_executable(${PROJECT_NAME} ${HEADERS} ${SOURCES}) +if(VOXELENGINE_BUILD_APPDIR) + file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/AppDir/usr/bin) + file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/AppDir/usr/lib) + file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/AppDir/usr/share/icons/hicolor/256x256) + file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/AppDir/usr/share/applications) + file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/AppDir/usr/share/VoxelEngine) + file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/res DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/AppDir/usr/share/VoxelEngine) + file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/dev/VoxelEngine.png DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/AppDir/usr/share/icons/hicolor/256x256) + file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/dev/VoxelEngine.png DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/AppDir) + file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/dev/VoxelEngine.png DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/AppDir/.dirIcon) + file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/dev/VoxelEngine.desktop DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/AppDir/usr/share/applications) + file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/dev/VoxelEngine.desktop DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/AppDir/) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/AppDir/usr/lib) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/AppDir/usr/lib) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/AppDir/usr/bin) + install(TARGETS VoxelEngine DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/AppDir/usr/bin) +endif() + + if(MSVC) target_compile_options(${PROJECT_NAME} PRIVATE /W4 /O2) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /source-charset:UTF-8") @@ -22,6 +43,8 @@ endif() find_package(OpenGL REQUIRED) find_package(GLEW REQUIRED) find_package(OpenAL REQUIRED) +# luajit has no CMakeLists.txt to use it as subdirectory, so install it +find_package(Lua REQUIRED) if (WIN32) set(PNGLIB spng) @@ -46,7 +69,8 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -no-pie") endif() - -target_link_libraries(${PROJECT_NAME} ${LIBS} glfw OpenGL::GL ${OPENAL_LIBRARY} GLEW::GLEW ${PNGLIB}) +include_directories(${LUA_INCLUDE_DIR}) +target_link_libraries(${PROJECT_NAME} ${LIBS} glfw OpenGL::GL ${OPENAL_LIBRARY} GLEW::GLEW ${PNGLIB} ${LUA_LIBRARIES} ${CMAKE_DL_LIBS}) file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/res DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + diff --git a/README.md b/README.md index f5a3fa3e..4240d6a6 100644 --- a/README.md +++ b/README.md @@ -22,26 +22,54 @@ git clone --recursive https://github.com/MihailRis/VoxelEngine-Cpp.git cd VoxelEngine-Cpp mkdir build cd build -cmake .. +cmake -DCMAKE_BUILD_TYPE=Release .. cmake --build . ``` ## Install libs: + #### Debian-based distro: -`$ sudo apt install libglfw3-dev libglfw3 libglew-dev libglm-dev libpng-dev libopenal-dev` +```sh +sudo apt install libglfw3-dev libglfw3 libglew-dev libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev +``` + +CMake missing LUA_INCLUDE_DIR and LUA_LIBRARIES fix: +```sh +sudo ln -s /usr/lib/x86_64-linux-gnu/libluajit-5.1.a /usr/lib/x86_64-linux-gnu/liblua5.1.a +sudo ln -s /usr/include/luajit-2.1 /usr/include/lua +``` #### RHEL-based distro: -`$ sudo dnf install glfw-devel glfw glew-devel glm-devel libpng-devel openal-devel` +```sh +sudo dnf install glfw-devel glfw glew-devel glm-devel libpng-devel openal-devel +``` + +\+ install LuaJIT #### Arch-based distro: If you use X11 -`$ sudo pacman -S glfw-x11 glew glm libpng openal` +```sh +sudo pacman -S glfw-x11 glew glm libpng openal +``` If you use Wayland -`$ sudo pacman -S glfw-wayland glew glm libpng openal` +```sh +sudo pacman -S glfw-wayland glew glm libpng openal +``` + +\+ install LuaJIT + +#### LuaJIT installation: +```sh +git clone https://luajit.org/git/luajit.git +cd luajit +make && sudo make install INSTALL_INC=/usr/include/lua +``` #### macOS: -`$ brew install glfw3 glew glm libpng` +``` +brew install glfw3 glew glm libpng +``` -Download, compile and install OpenAL +Download, compile and install OpenAL and LuaJIT diff --git a/dev/AppImageBuilder.yml b/dev/AppImageBuilder.yml new file mode 100644 index 00000000..6c4bc283 --- /dev/null +++ b/dev/AppImageBuilder.yml @@ -0,0 +1,75 @@ +version: 1 +AppDir: + path: AppDir + app_info: + id: VoxelEngine + name: VoxelEngine + icon: VoxelEngine + version: latest + exec: usr/bin/VoxelEngine + exec_args: --dir $HOME --res $APPDIR/usr/share/VoxelEngine/res $@ + apt: + arch: amd64 + sources: + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal main restricted universe multiverse + key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3b4fe6acc0b21f32' + key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x871920D1991BC93C' + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-updates main restricted universe multiverse + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-backports main restricted universe multiverse + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-security main restricted universe multiverse + include: + - libbz2-1.0 + - libexpat1 + - libfam0 + - libgcrypt20 + - libglfw3 + - libglew2.1 + - libpng16-16 + - libopenal1 + - libopengl0 + - libasound2 + - libglx0 + exclude: + - hicolor-icon-theme + - sound-theme-freedesktop + - perl + - perl-base + - kwayland-data + - libwacom2 + - libasound2 + - breeze + - breeze-icon-theme + - breeze-cursor-theme + - kwin-style-breeze + - kde-style-breeze + - plasma-integration + files: + exclude: + - usr/share/man + - usr/share/doc/*/README.* + - usr/share/doc/*/changelog.* + - usr/share/doc/*/NEWS.* + - usr/share/doc/*/TODO.* + test: + fedora: + image: appimagecrafters/tests-env:fedora-30 + command: ./AppRun + use_host_x: true + debian: + image: appimagecrafters/tests-env:debian-stable + command: ./AppRun + use_host_x: true + arch: + image: appimagecrafters/tests-env:archlinux-latest + command: ./AppRun + use_host_x: true + centos: + image: appimagecrafters/tests-env:centos-7 + command: ./AppRun + use_host_x: true + ubuntu: + image: appimagecrafters/tests-env:ubuntu-xenial + command: ./AppRun + use_host_x: true +AppImage: + arch: x86_64 diff --git a/dev/VoxelEngine.desktop b/dev/VoxelEngine.desktop new file mode 100644 index 00000000..e5cbb0ab --- /dev/null +++ b/dev/VoxelEngine.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Version=1.0 +Type=Application +Name=VoxelEngine +Comment=Minecraft-like game engine in C++ with OpenGL +TryExec=VoxelEngine +Exec=VoxelEngine +Icon=VoxelEngine +MimeType=image/x-foo; +Categories=Game; diff --git a/dev/VoxelEngine.png b/dev/VoxelEngine.png new file mode 100644 index 00000000..68924547 Binary files /dev/null and b/dev/VoxelEngine.png differ diff --git a/res/content/base/blocks/flower.json b/res/content/base/blocks/flower.json index 054bede9..7d2f9c73 100644 --- a/res/content/base/blocks/flower.json +++ b/res/content/base/blocks/flower.json @@ -4,6 +4,7 @@ "light-passing": true, "obstacle": false, "replaceable": true, + "grounded": true, "model": "X", "hitbox": [0.15, 0.0, 0.15, 0.7, 0.7, 0.7] -} \ No newline at end of file +} diff --git a/res/content/base/blocks/grass.json b/res/content/base/blocks/grass.json index 944a2a41..844d7baa 100644 --- a/res/content/base/blocks/grass.json +++ b/res/content/base/blocks/grass.json @@ -4,6 +4,7 @@ "light-passing": true, "obstacle": false, "replaceable": true, + "grounded": true, "model": "X", "hitbox": [0.15, 0.0, 0.15, 0.7, 0.7, 0.7] -} \ No newline at end of file +} diff --git a/res/content/base/blocks/torch.json b/res/content/base/blocks/torch.json index 93076841..b5b76e4f 100644 --- a/res/content/base/blocks/torch.json +++ b/res/content/base/blocks/torch.json @@ -9,7 +9,7 @@ ], "emission": [13, 13, 12], "model": "aabb", - "hitbox": [0.3, 0.0, 0.7, 0.125, 0.5, 0.125], + "hitbox": [0.4375, 0.0, 0.4375, 0.125, 0.5, 0.125], "light-passing": true, "obstacle": false, "rotation": "pipe" diff --git a/res/content/base/blocks/wallpaper.json b/res/content/base/blocks/wallpaper.json deleted file mode 100644 index d8624071..00000000 --- a/res/content/base/blocks/wallpaper.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "texture-faces": [ - "white", - "white", - "wallpaper_s", - "wallpaper_s", - "white", - "white" - ], - "emission": [0, 0, 0], - "model": "aabb", - "hitbox": [0.0, 0.0, 0.0, 1.0, 0.01, 1.0], - "light-passing": true, - "obstacle": false, - "rotation": "pipe" -} diff --git a/res/content/base/content.json b/res/content/base/content.json index 2429936e..71b5d16d 100644 --- a/res/content/base/content.json +++ b/res/content/base/content.json @@ -22,7 +22,6 @@ "pane", "pipe", "lightbulb", - "torch", - "wallpaper" + "torch" ] } diff --git a/res/content/base/scripts/grass_block.lua b/res/content/base/scripts/grass_block.lua new file mode 100644 index 00000000..f1072ab1 --- /dev/null +++ b/res/content/base/scripts/grass_block.lua @@ -0,0 +1,20 @@ +function on_random_update(x, y, z) + local dirtid = block_index('base:dirt'); + if is_solid_at(x, y+1, z) then + set_block(x, y, z, dirtid) + else + local grassblockid = block_index('base:grass_block') + for lx=-1,1 do + for ly=-1,1 do + for lz=-1,1 do + if get_block(x + lx, y + ly, z + lz) == dirtid then + if not is_solid_at(x + lx, y + ly + 1, z + lz) then + set_block(x + lx, y + ly, z + lz, grassblockid) + return + end + end + end + end + end + end +end diff --git a/res/content/base/textures/blocks/wallpaper_s.png b/res/content/base/textures/blocks/wallpaper_s.png deleted file mode 100644 index c13e1082..00000000 Binary files a/res/content/base/textures/blocks/wallpaper_s.png and /dev/null differ diff --git a/res/scripts/world.lua b/res/scripts/world.lua new file mode 100644 index 00000000..e69de29b diff --git a/src/assets/AssetsLoader.cpp b/src/assets/AssetsLoader.cpp index e8ec0996..8ffacf6a 100644 --- a/src/assets/AssetsLoader.cpp +++ b/src/assets/AssetsLoader.cpp @@ -50,18 +50,18 @@ void AssetsLoader::createDefaults(AssetsLoader& loader) { loader.addLoader(ASSET_ATLAS, assetload::atlas); } -void AssetsLoader::addDefaults(AssetsLoader& loader) { - loader.add(ASSET_SHADER, SHADERS_FOLDER"/main", "main"); - loader.add(ASSET_SHADER, SHADERS_FOLDER"/lines", "lines"); - loader.add(ASSET_SHADER, SHADERS_FOLDER"/ui", "ui"); - loader.add(ASSET_SHADER, SHADERS_FOLDER"/ui3d", "ui3d"); - loader.add(ASSET_SHADER, SHADERS_FOLDER"/background", "background"); - loader.add(ASSET_SHADER, SHADERS_FOLDER"/skybox_gen", "skybox_gen"); - - loader.add(ASSET_ATLAS, TEXTURES_FOLDER"/blocks", "blocks"); - loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/menubg.png", "menubg"); - - loader.add(ASSET_FONT, FONTS_FOLDER"/font", "normal"); +void AssetsLoader::addDefaults(AssetsLoader& loader, bool allAssets) { + if (allAssets) { + loader.add(ASSET_SHADER, SHADERS_FOLDER"/main", "main"); + loader.add(ASSET_SHADER, SHADERS_FOLDER"/lines", "lines"); + loader.add(ASSET_SHADER, SHADERS_FOLDER"/ui", "ui"); + loader.add(ASSET_SHADER, SHADERS_FOLDER"/ui3d", "ui3d"); + loader.add(ASSET_SHADER, SHADERS_FOLDER"/background", "background"); + loader.add(ASSET_SHADER, SHADERS_FOLDER"/skybox_gen", "skybox_gen"); + loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/menubg.png", "menubg"); + loader.add(ASSET_FONT, FONTS_FOLDER"/font", "normal"); + } + loader.add(ASSET_ATLAS, TEXTURES_FOLDER"/blocks", "blocks"); } const ResPaths* AssetsLoader::getPaths() const { diff --git a/src/assets/AssetsLoader.h b/src/assets/AssetsLoader.h index 9a731780..13ea12fa 100644 --- a/src/assets/AssetsLoader.h +++ b/src/assets/AssetsLoader.h @@ -36,7 +36,7 @@ public: bool loadNext(); static void createDefaults(AssetsLoader& loader); - static void addDefaults(AssetsLoader& loader); + static void addDefaults(AssetsLoader& loader, bool allAssets); const ResPaths* getPaths() const; }; diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index d7f0f8df..5a10b4c4 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -12,6 +12,7 @@ #include "../typedefs.h" #include "ContentPack.h" +#include "../logic/scripting/scripting.h" namespace fs = std::filesystem; @@ -84,6 +85,7 @@ Block* ContentLoader::loadBlock(std::string name, fs::path file) { root->flag("light-passing", def->lightPassing); root->flag("breakable", def->breakable); root->flag("selectable", def->selectable); + root->flag("grounded", def->grounded); root->flag("sky-light-passing", def->skyLightPassing); root->num("draw-group", def->drawGroup); @@ -100,8 +102,14 @@ void ContentLoader::load(ContentBuilder* builder) { if (blocksarr) { for (uint i = 0; i < blocksarr->size(); i++) { std::string name = blocksarr->str(i); + std::string prefix = pack->id+":"+name; fs::path blockfile = folder/fs::path("blocks/"+name+".json"); - builder->add(loadBlock(pack->id+":"+name, blockfile)); + Block* block = loadBlock(prefix, blockfile); + builder->add(block); + fs::path scriptfile = folder/fs::path("scripts/"+name+".lua"); + if (fs::is_regular_file(scriptfile)) { + scripting::load_block_script(prefix, scriptfile, &block->rt.funcsset); + } } } } diff --git a/src/engine.cpp b/src/engine.cpp index 8a4eecce..63762f09 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -33,16 +33,10 @@ #include "content/ContentPack.h" #include "content/ContentLoader.h" #include "frontend/locale/langs.h" +#include "logic/scripting/scripting.h" #include "definitions.h" -using std::unique_ptr; -using std::shared_ptr; -using std::string; -using std::vector; -using glm::vec3; -using gui::GUI; - namespace fs = std::filesystem; Engine::Engine(EngineSettings& settings, EnginePaths* paths) @@ -52,6 +46,7 @@ Engine::Engine(EngineSettings& settings, EnginePaths* paths) } auto resdir = paths->getResources(); + scripting::initialize(paths); std::cout << "-- loading assets" << std::endl; std::vector roots {resdir}; @@ -59,7 +54,7 @@ Engine::Engine(EngineSettings& settings, EnginePaths* paths) assets.reset(new Assets()); AssetsLoader loader(assets.get(), resPaths.get()); AssetsLoader::createDefaults(loader); - AssetsLoader::addDefaults(loader); + AssetsLoader::addDefaults(loader, true); Shader::preprocessor->setPaths(resPaths.get()); while (loader.hasNext()) { @@ -71,7 +66,7 @@ Engine::Engine(EngineSettings& settings, EnginePaths* paths) } Audio::initialize(); - gui = new GUI(); + gui = new gui::GUI(); if (settings.ui.language == "auto") { settings.ui.language = platform::detect_locale(); } @@ -88,7 +83,7 @@ void Engine::updateTimers() { void Engine::updateHotkeys() { if (Events::jpressed(keycode::F2)) { - unique_ptr image(Window::takeScreenshot()); + std::unique_ptr image(Window::takeScreenshot()); image->flipY(); fs::path filename = paths->getScreenshotFile("png"); png::write_image(filename.string(), image.get()); @@ -100,7 +95,7 @@ void Engine::updateHotkeys() { } void Engine::mainloop() { - setScreen(shared_ptr(new MenuScreen(this))); + setScreen(std::make_shared(this)); std::cout << "-- preparing systems" << std::endl; @@ -124,6 +119,7 @@ void Engine::mainloop() { } Engine::~Engine() { + scripting::close(); screen = nullptr; delete gui; @@ -135,46 +131,12 @@ Engine::~Engine() { std::cout << "-- engine finished" << std::endl; } -GUI* Engine::getGUI() { - return gui; -} - -EngineSettings& Engine::getSettings() { - return settings; -} - -Assets* Engine::getAssets() { - return assets.get(); -} - -void Engine::setScreen(shared_ptr screen) { - this->screen = screen; -} - -const Content* Engine::getContent() const { - return content.get(); -} - -vector& Engine::getContentPacks() { - return contentPacks; -} - -EnginePaths* Engine::getPaths() { - return paths; -} - -void Engine::setLanguage(string locale) { - settings.ui.language = locale; - langs::setup(paths->getResources(), locale, contentPacks); - menus::create_menus(this, gui->getMenu()); -} - void Engine::loadContent() { auto resdir = paths->getResources(); ContentBuilder contentBuilder; setup_definitions(&contentBuilder); - vector resRoots; + std::vector resRoots; for (auto& pack : contentPacks) { ContentLoader loader(&pack); loader.load(&contentBuilder); @@ -185,11 +147,11 @@ void Engine::loadContent() { Shader::preprocessor->setPaths(resPaths.get()); - unique_ptr new_assets(new Assets()); + std::unique_ptr new_assets(new Assets()); std::cout << "-- loading assets" << std::endl; AssetsLoader loader(new_assets.get(), resPaths.get()); AssetsLoader::createDefaults(loader); - AssetsLoader::addDefaults(loader); + AssetsLoader::addDefaults(loader, false); while (loader.hasNext()) { if (!loader.loadNext()) { new_assets.reset(); @@ -204,3 +166,37 @@ void Engine::loadAllPacks() { contentPacks.clear(); ContentPack::scan(resdir/fs::path("content"), contentPacks); } + +void Engine::setScreen(std::shared_ptr screen) { + this->screen = screen; +} + +void Engine::setLanguage(std::string locale) { + settings.ui.language = locale; + langs::setup(paths->getResources(), locale, contentPacks); + menus::create_menus(this, gui->getMenu()); +} + +gui::GUI* Engine::getGUI() { + return gui; +} + +EngineSettings& Engine::getSettings() { + return settings; +} + +Assets* Engine::getAssets() { + return assets.get(); +} + +const Content* Engine::getContent() const { + return content.get(); +} + +std::vector& Engine::getContentPacks() { + return contentPacks; +} + +EnginePaths* Engine::getPaths() { + return paths; +} diff --git a/src/files/WorldFiles.cpp b/src/files/WorldFiles.cpp index ea2e36d7..9e3742a6 100644 --- a/src/files/WorldFiles.cpp +++ b/src/files/WorldFiles.cpp @@ -24,15 +24,6 @@ #include #include -const int SECTION_POSITION = 1; -const int SECTION_ROTATION = 2; -const int SECTION_FLAGS = 3; -const int PLAYER_FLAG_FLIGHT = 0x1; -const int PLAYER_FLAG_NOCLIP = 0x2; - -const int WORLD_SECTION_MAIN = 1; -const int WORLD_SECTION_DAYNIGHT = 2; - namespace fs = std::filesystem; WorldRegion::WorldRegion() { @@ -94,7 +85,7 @@ WorldFiles::~WorldFiles(){ } WorldRegion* WorldFiles::getRegion(regionsmap& regions, int x, int z) { - auto found = regions.find(ivec2(x, z)); + auto found = regions.find(glm::ivec2(x, z)); if (found == regions.end()) return nullptr; return found->second; @@ -104,7 +95,7 @@ WorldRegion* WorldFiles::getOrCreateRegion(regionsmap& regions, int x, int z) { WorldRegion* region = getRegion(regions, x, z); if (region == nullptr) { region = new WorldRegion(); - regions[ivec2(x, z)] = region; + regions[glm::ivec2(x, z)] = region; } return region; } @@ -367,7 +358,7 @@ void WorldFiles::writeRegions(regionsmap& regions, const fs::path& folder, int l WorldRegion* region = it.second; if (region->getChunks() == nullptr || !region->isUnsaved()) continue; - ivec2 key = it.first; + glm::ivec2 key = it.first; writeRegion(key.x, key.y, region, folder, layer); } } @@ -458,7 +449,7 @@ bool WorldFiles::readWorldInfo(World* world) { } void WorldFiles::writePlayer(Player* player){ - vec3 position = player->hitbox->position; + glm::vec3 position = player->hitbox->position; json::JObject root; json::JArray& posarr = root.putArray("position"); posarr.put(position.x); @@ -484,7 +475,7 @@ bool WorldFiles::readPlayer(Player* player) { std::unique_ptr root(files::read_json(file)); json::JArray* posarr = root->arr("position"); - vec3& position = player->hitbox->position; + glm::vec3& position = player->hitbox->position; position.x = posarr->num(0); position.y = posarr->num(1); position.z = posarr->num(2); diff --git a/src/frontend/WorldRenderer.cpp b/src/frontend/WorldRenderer.cpp index 6893ffda..5a6fc812 100644 --- a/src/frontend/WorldRenderer.cpp +++ b/src/frontend/WorldRenderer.cpp @@ -168,7 +168,7 @@ void WorldRenderer::draw(const GfxContext& pctx, Camera* camera){ shader->uniform3f("u_cameraPos", camera->position); shader->uniform1i("u_cubemap", 1); { - blockid_t id = level->player->choosenBlock; + blockid_t id = level->player->chosenBlock; Block* block = contentIds->getBlockDef(id); assert(block != nullptr); float multiplier = 0.5f; diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp index ace6972c..2bc92468 100644 --- a/src/frontend/hud.cpp +++ b/src/frontend/hud.cpp @@ -256,7 +256,7 @@ void HudRenderer::drawContentAccess(const GfxContext& ctx, Player* player) { tint.g *= 1.2f; tint.b *= 1.2f; if (Events::jclicked(mousecode::BUTTON_1)) { - player->choosenBlock = i+1; + player->chosenBlock = i+1; } } else { tint = vec4(1.0f); @@ -335,7 +335,7 @@ void HudRenderer::draw(const GfxContext& ctx){ subctx.depthTest(true); subctx.cullFace(true); - Block* cblock = contentIds->getBlockDef(player->choosenBlock); + Block* cblock = contentIds->getBlockDef(player->chosenBlock); assert(cblock != nullptr); blocksPreview->draw(cblock, width - 56, uicamera->getFov() - 56, 48, vec4(1.0f)); //drawBlockPreview(cblock, width - 56, uicamera->fov - 56, 48, 48, vec4(1.0f)); diff --git a/src/logic/BlocksController.cpp b/src/logic/BlocksController.cpp new file mode 100644 index 00000000..fb9e82e7 --- /dev/null +++ b/src/logic/BlocksController.cpp @@ -0,0 +1,120 @@ +#include "BlocksController.h" + +#include "../voxels/voxel.h" +#include "../voxels/Block.h" +#include "../voxels/Chunk.h" +#include "../voxels/Chunks.h" +#include "../world/Level.h" +#include "../content/Content.h" +#include "../lighting/Lighting.h" +#include "../util/timeutil.h" + +#include "scripting/scripting.h" + +Clock::Clock(int tickRate, int tickParts) + : tickRate(tickRate), + tickParts(tickParts) { +} + +bool Clock::update(float delta) { + tickTimer += delta; + float delay = 1.0f / float(tickRate); + if (tickTimer > delay || tickPartsUndone) { + if (tickPartsUndone) { + tickPartsUndone--; + } else { + tickTimer = fmod(tickTimer, delay); + tickPartsUndone = tickParts-1; + } + return true; + } + return false; +} + +int Clock::getParts() const { + return tickParts; +} + +int Clock::getPart() const { + return tickParts-tickPartsUndone-1; +} + +BlocksController::BlocksController(Level* level, uint padding) + : level(level), + chunks(level->chunks), + lighting(level->lighting), + randTickClock(20, 3), + padding(padding) { +} + +void BlocksController::updateSides(int x, int y, int z) { + updateBlock(x-1, y, z); + updateBlock(x+1, y, z); + updateBlock(x, y-1, z); + updateBlock(x, y+1, z); + updateBlock(x, y, z-1); + updateBlock(x, y, z+1); +} + +void BlocksController::breakBlock(Player* player, const Block* def, int x, int y, int z) { + chunks->set(x,y,z, 0, 0); + lighting->onBlockSet(x,y,z, 0); + if (def->rt.funcsset.onbroken) { + scripting::on_block_broken(player, def, x, y, z); + } + updateSides(x, y, z); +} + +void BlocksController::updateBlock(int x, int y, int z) { + voxel* vox = chunks->get(x, y, z); + if (vox == nullptr) + return; + const Block* def = level->content->indices->getBlockDef(vox->id); + if (def->grounded && !chunks->isSolid(x, y-1, z)) { + breakBlock(nullptr, def, x, y, z); + return; + } + if (def->rt.funcsset.update) { + scripting::update_block(def, x, y, z); + } +} + +void BlocksController::update(float delta) { + if (randTickClock.update(delta)) { + randomTick(randTickClock.getPart(), randTickClock.getParts()); + } +} + +void BlocksController::randomTick(int tickid, int parts) { + // timeutil::ScopeLogTimer timer(5000+tickid); + const int w = chunks->w; + const int d = chunks->d; + auto indices = level->content->indices; + for (uint z = padding; z < d-padding; z++){ + for (uint x = padding; x < w-padding; x++){ + int index = z * w + x; + if ((index + tickid) % parts != 0) + continue; + std::shared_ptr chunk = chunks->chunks[index]; + if (chunk == nullptr || !chunk->isLighted()) + continue; + int segments = 4; + int segheight = CHUNK_H / segments; + for (int s = 0; s < segments; s++) { + for (int i = 0; i < 3; i++) { + int bx = rand() % CHUNK_W; + int by = rand() % segheight + s * segheight; + int bz = rand() % CHUNK_D; + const voxel& vox = chunk->voxels[(by * CHUNK_D + bz) * CHUNK_W + bx]; + Block* block = indices->getBlockDef(vox.id); + if (block->rt.funcsset.randupdate) { + scripting::random_update_block( + block, + chunk->x * CHUNK_W + bx, by, + chunk->z * CHUNK_D + bz); + } + } + } + } + } +} diff --git a/src/logic/BlocksController.h b/src/logic/BlocksController.h new file mode 100644 index 00000000..454bd16b --- /dev/null +++ b/src/logic/BlocksController.h @@ -0,0 +1,46 @@ +#ifndef LOGIC_BLOCKS_CONTROLLER_H_ +#define LOGIC_BLOCKS_CONTROLLER_H_ + +#include "../typedefs.h" + +class Player; +class Block; +class Level; +class Chunks; +class Lighting; + +class Clock { + int tickRate; + int tickParts; + + float tickTimer = 0.0f; + int tickId = 0; + int tickPartsUndone = 0; +public: + Clock(int tickRate, int tickParts); + + bool update(float delta); + + int getParts() const; + int getPart() const; +}; + +class BlocksController { + Level* level; + Chunks* chunks; + Lighting* lighting; + Clock randTickClock; + uint padding; +public: + BlocksController(Level* level, uint padding); + + void updateSides(int x, int y, int z); + void updateBlock(int x, int y, int z); + + void breakBlock(Player* player, const Block* def, int x, int y, int z); + + void update(float delta); + void randomTick(int tickid, int parts); +}; + +#endif // LOGIC_BLOCKS_CONTROLLER_H_ diff --git a/src/logic/ChunksController.cpp b/src/logic/ChunksController.cpp index 1d354608..c3d28ead 100644 --- a/src/logic/ChunksController.cpp +++ b/src/logic/ChunksController.cpp @@ -2,13 +2,13 @@ #include #include +#include #include "../voxels/Block.h" #include "../voxels/Chunk.h" #include "../voxels/Chunks.h" #include "../voxels/ChunksStorage.h" #include "../voxels/WorldGenerator.h" -#include "../content/Content.h" #include "../graphics/Mesh.h" #include "../lighting/Lighting.h" #include "../files/WorldFiles.h" @@ -17,19 +17,14 @@ #include "../maths/voxmaths.h" #include "../util/timeutil.h" + const uint MAX_WORK_PER_FRAME = 64; const uint MIN_SURROUNDING = 9; -using std::unique_ptr; -using std::shared_ptr; - -ChunksController::ChunksController(Level* level, - Chunks* chunks, - Lighting* lighting, - uint padding) +ChunksController::ChunksController(Level* level, uint padding) : level(level), - chunks(chunks), - lighting(lighting), + chunks(level->chunks), + lighting(level->lighting), padding(padding), generator(new WorldGenerator(level->content)) { } @@ -66,7 +61,7 @@ bool ChunksController::loadVisible(){ for (uint z = padding; z < d-padding; z++){ for (uint x = padding; x < w-padding; x++){ int index = z * w + x; - shared_ptr chunk = chunks->chunks[index]; + std::shared_ptr chunk = chunks->chunks[index]; if (chunk != nullptr){ int surrounding = 0; for (int oz = -1; oz <= 1; oz++){ @@ -98,7 +93,7 @@ bool ChunksController::loadVisible(){ } int index = nearZ * w + nearX; - shared_ptr chunk = chunks->chunks[index]; + std::shared_ptr chunk = chunks->chunks[index]; if (chunk != nullptr) { return false; } diff --git a/src/logic/ChunksController.h b/src/logic/ChunksController.h index af646163..a9fa0fa6 100644 --- a/src/logic/ChunksController.h +++ b/src/logic/ChunksController.h @@ -6,9 +6,6 @@ class Level; class Chunks; class Lighting; -class WorldFiles; -class VoxelRenderer; -class ChunksLoader; class WorldGenerator; /* ChunksController manages chunks dynamic loading/unloading */ @@ -26,7 +23,7 @@ private: /* Process one chunk: load it or calculate lights for it */ bool loadVisible(); public: - ChunksController(Level* level, Chunks* chunks, Lighting* lighting, uint padding); + ChunksController(Level* level, uint padding); ~ChunksController(); /* @param maxDuration milliseconds reserved for chunks loading */ diff --git a/src/logic/LevelController.cpp b/src/logic/LevelController.cpp index 05c134b6..2ab74f70 100644 --- a/src/logic/LevelController.cpp +++ b/src/logic/LevelController.cpp @@ -2,28 +2,29 @@ #include "../world/Level.h" #include "PlayerController.h" +#include "BlocksController.h" #include "ChunksController.h" +#include "scripting/scripting.h" + LevelController::LevelController(EngineSettings& settings, Level* level) : settings(settings), level(level) { - chunks = new ChunksController( - level, - level->chunks, - level->lighting, - settings.chunks.padding); - player = new PlayerController(level, settings); + blocks = new BlocksController(level, settings.chunks.padding); + chunks = new ChunksController(level, settings.chunks.padding); + player = new PlayerController(level, settings, blocks); + + scripting::on_world_load(level); } LevelController::~LevelController() { + scripting::on_world_quit(); delete player; delete chunks; } -void LevelController::update( - float delta, - bool input, - bool pause) { +void LevelController::update(float delta, bool input, bool pause) { player->update(delta, input, pause); level->update(); chunks->update(settings.chunks.loadSpeed); -} \ No newline at end of file + blocks->update(delta); +} diff --git a/src/logic/LevelController.h b/src/logic/LevelController.h index a889cd03..f319d554 100644 --- a/src/logic/LevelController.h +++ b/src/logic/LevelController.h @@ -4,6 +4,7 @@ #include "../settings.h" class Level; +class BlocksController; class ChunksController; class PlayerController; @@ -12,6 +13,7 @@ class LevelController { EngineSettings& settings; Level* level; // Sub-controllers + BlocksController* blocks; ChunksController* chunks; PlayerController* player; public: diff --git a/src/logic/PlayerController.cpp b/src/logic/PlayerController.cpp index d0caf2ef..ba243237 100644 --- a/src/logic/PlayerController.cpp +++ b/src/logic/PlayerController.cpp @@ -12,6 +12,8 @@ #include "../window/Camera.h" #include "../window/Events.h" #include "../window/input.h" +#include "scripting/scripting.h" +#include "BlocksController.h" #include "../core_defs.h" @@ -25,9 +27,6 @@ const float RUN_ZOOM = 1.1f; const float C_ZOOM = 0.1f; const float CROUCH_SHIFT_Y = -0.2f; -using glm::vec2; -using glm::vec3; -using std::string; CameraControl::CameraControl(Player* player, const CameraSettings& settings) : player(player), @@ -55,27 +54,27 @@ void CameraControl::updateMouse(PlayerInput& input) { float& camY = player->camY; camX += rotX; camY += rotY; - if (camY < -radians(89.9f)){ - camY = -radians(89.9f); + if (camY < -glm::radians(89.9f)){ + camY = -glm::radians(89.9f); } - if (camY > radians(89.9f)){ - camY = radians(89.9f); + if (camY > glm::radians(89.9f)){ + camY = glm::radians(89.9f); } - camera->rotation = mat4(1.0f); + camera->rotation = glm::mat4(1.0f); camera->rotate(camY, camX, 0); } void CameraControl::update(PlayerInput& input, float delta, Chunks* chunks) { Hitbox* hitbox = player->hitbox; - offset = vec3(0.0f, 0.7f, 0.0f); + offset = glm::vec3(0.0f, 0.7f, 0.0f); if (settings.shaking && !input.cheat) { const float k = CAM_SHAKE_DELTA_K; const float oh = CAM_SHAKE_OFFSET; const float ov = CAM_SHAKE_OFFSET_Y; - const vec3& vel = hitbox->velocity; + const glm::vec3& vel = hitbox->velocity; interpVel = interpVel * (1.0f - delta * 5) + vel * delta * 0.1f; if (hitbox->grounded && interpVel.y < 0.0f){ @@ -83,12 +82,12 @@ void CameraControl::update(PlayerInput& input, float delta, Chunks* chunks) { } shake = shake * (1.0f - delta * k); if (hitbox->grounded) { - float f = length(vec2(vel.x, vel.z)); + float f = glm::length(glm::vec2(vel.x, vel.z)); shakeTimer += delta * f * CAM_SHAKE_SPEED; shake += f * delta * k; } - offset += camera->right * sin(shakeTimer) * oh * shake; - offset += camera->up * abs(cos(shakeTimer)) * ov * shake; + offset += camera->right * glm::sin(shakeTimer) * oh * shake; + offset += camera->up * glm::abs(glm::cos(shakeTimer)) * ov * shake; offset -= glm::min(interpVel * 0.05f, 1.0f); } @@ -98,7 +97,7 @@ void CameraControl::update(PlayerInput& input, float delta, Chunks* chunks) { float dt = fmin(1.0f, delta * ZOOM_SPEED); float zoomValue = 1.0f; if (crouch){ - offset += vec3(0.f, CROUCH_SHIFT_Y, 0.f); + offset += glm::vec3(0.f, CROUCH_SHIFT_Y, 0.f); zoomValue = CROUCH_ZOOM; } else if (input.sprint){ zoomValue = RUN_ZOOM; @@ -128,16 +127,20 @@ void CameraControl::update(PlayerInput& input, float delta, Chunks* chunks) { } } -vec3 PlayerController::selectedBlockPosition; -vec3 PlayerController::selectedPointPosition; -ivec3 PlayerController::selectedBlockNormal; +glm::vec3 PlayerController::selectedBlockPosition; +glm::vec3 PlayerController::selectedPointPosition; +glm::ivec3 PlayerController::selectedBlockNormal; int PlayerController::selectedBlockId = -1; int PlayerController::selectedBlockStates = 0; -PlayerController::PlayerController(Level* level, const EngineSettings& settings) +PlayerController::PlayerController( + Level* level, + const EngineSettings& settings, + BlocksController* blocksController) : level(level), player(level->player), - camControl(level->player, settings.camera) { + camControl(level->player, settings.camera), + blocksController(blocksController) { } void PlayerController::update(float delta, bool input, bool pause) { @@ -177,7 +180,7 @@ void PlayerController::updateKeyboard() { // block choice for (int i = 1; i < 10; i++){ if (Events::jpressed(keycode::NUM_0+i)){ - player->choosenBlock = i; + player->chosenBlock = i; } } } @@ -211,9 +214,9 @@ void PlayerController::updateInteraction(){ Player* player = level->player; Lighting* lighting = level->lighting; Camera* camera = player->camera; - vec3 end; - ivec3 iend; - ivec3 norm; + glm::vec3 end; + glm::ivec3 iend; + glm::ivec3 norm; bool xkey = Events::pressed(keycode::X); bool lclick = Events::jactive(BIND_PLAYER_ATTACK) || @@ -241,9 +244,9 @@ void PlayerController::updateInteraction(){ int z = iend.z; uint8_t states = 0; - Block* def = contentIds->getBlockDef(player->choosenBlock); + Block* def = contentIds->getBlockDef(player->chosenBlock); if (def->rotatable){ - const string& name = def->rotations.name; + const std::string& name = def->rotations.name; if (name == "pipe") { if (norm.x < 0.0f) states = BLOCK_DIR_WEST; else if (norm.x > 0.0f) states = BLOCK_DIR_EAST; @@ -252,7 +255,7 @@ void PlayerController::updateInteraction(){ else if (norm.z > 0.0f) states = BLOCK_DIR_NORTH; else if (norm.z < 0.0f) states = BLOCK_DIR_SOUTH; } else if (name == "pane") { - vec3 vec = camera->dir; + glm::vec3 vec = camera->dir; if (abs(vec.x) > abs(vec.z)){ if (vec.x > 0.0f) states = BLOCK_DIR_EAST; if (vec.x < 0.0f) states = BLOCK_DIR_WEST; @@ -266,8 +269,7 @@ void PlayerController::updateInteraction(){ Block* block = contentIds->getBlockDef(vox->id); if (lclick && block->breakable){ - chunks->set(x,y,z, 0, 0); - lighting->onBlockSet(x,y,z, 0); + blocksController->breakBlock(player, block, x, y, z); } if (rclick){ if (block->model != BlockModel::xsprite){ @@ -276,19 +278,30 @@ void PlayerController::updateInteraction(){ z = (iend.z)+(norm.z); } vox = chunks->get(x, y, z); + int chosenBlock = player->chosenBlock; if (vox && (block = contentIds->getBlockDef(vox->id))->replaceable) { if (!level->physics->isBlockInside(x,y,z, player->hitbox) || !def->obstacle){ - chunks->set(x, y, z, player->choosenBlock, states); - lighting->onBlockSet(x,y,z, player->choosenBlock); + Block* def = contentIds->getBlockDef(chosenBlock); + if (def->grounded && !chunks->isSolid(x, y-1, z)) { + chosenBlock = 0; + } + if (chosenBlock != vox->id) { + chunks->set(x, y, z, chosenBlock, states); + lighting->onBlockSet(x,y,z, chosenBlock); + if (def->rt.funcsset.onplaced) { + scripting::on_block_placed(player, def, x, y, z); + } + blocksController->updateSides(x, y, z); + } } } } if (Events::jactive(BIND_PLAYER_PICK)){ - player->choosenBlock = chunks->get(x,y,z)->id; + player->chosenBlock = chunks->get(x,y,z)->id; } } else { selectedBlockId = -1; selectedBlockStates = 0; } -} \ No newline at end of file +} diff --git a/src/logic/PlayerController.h b/src/logic/PlayerController.h index 6893382b..ffab1c56 100644 --- a/src/logic/PlayerController.h +++ b/src/logic/PlayerController.h @@ -8,6 +8,7 @@ class Camera; class Level; +class BlocksController; class CameraControl { Player* player; @@ -29,6 +30,7 @@ class PlayerController { Player* player; PlayerInput input; CameraControl camControl; + BlocksController* blocksController; void updateKeyboard(); void updateCamera(float delta, bool movement); @@ -42,7 +44,9 @@ public: static int selectedBlockId; static int selectedBlockStates; - PlayerController(Level* level, const EngineSettings& settings); + PlayerController(Level* level, + const EngineSettings& settings, + BlocksController* blocksController); void update(float delta, bool input, bool pause); }; diff --git a/src/logic/scripting/api_lua.cpp b/src/logic/scripting/api_lua.cpp new file mode 100644 index 00000000..766bc7d0 --- /dev/null +++ b/src/logic/scripting/api_lua.cpp @@ -0,0 +1,65 @@ +#include "api_lua.h" +#include "scripting.h" + +#include "../../world/Level.h" +#include "../../content/Content.h" +#include "../../voxels/Block.h" +#include "../../voxels/Chunks.h" +#include "../../voxels/voxel.h" + +int l_block_name(lua_State* L) { + int id = lua_tointeger(L, 1); + lua_pushstring(L, scripting::content->indices->getBlockDef(id)->name.c_str()); + return 1; +} + +int l_is_solid_at(lua_State* L) { + int x = lua_tointeger(L, 1); + int y = lua_tointeger(L, 2); + int z = lua_tointeger(L, 3); + + lua_pushboolean(L, scripting::level->chunks->isSolid(x, y, z)); + return 1; +} + +int l_blocks_count(lua_State* L) { + lua_pushinteger(L, scripting::content->indices->countBlockDefs()); + return 1; +} + +int l_block_index(lua_State* L) { + auto name = lua_tostring(L, 1); + lua_pushinteger(L, scripting::content->requireBlock(name)->rt.id); + return 1; +} + +int l_set_block(lua_State* L) { + int x = lua_tointeger(L, 1); + int y = lua_tointeger(L, 2); + int z = lua_tointeger(L, 3); + int id = lua_tointeger(L, 4); + scripting::level->chunks->set(x, y, z, id, 0); + return 0; +} + +int l_get_block(lua_State* L) { + int x = lua_tointeger(L, 1); + int y = lua_tointeger(L, 2); + int z = lua_tointeger(L, 3); + voxel* vox = scripting::level->chunks->get(x, y, z); + int id = vox == nullptr ? -1 : vox->id; + lua_pushinteger(L, id); + return 1; +} + +#define lua_addfunc(L, FUNC, NAME) (lua_pushcfunction(L, FUNC),\ + lua_setglobal(L, NAME)) + +void apilua::create_funcs(lua_State* L) { + lua_addfunc(L, l_block_index, "block_index"); + lua_addfunc(L, l_block_name, "block_name"); + lua_addfunc(L, l_blocks_count, "blocks_count"); + lua_addfunc(L, l_is_solid_at, "is_solid_at"); + lua_addfunc(L, l_set_block, "set_block"); + lua_addfunc(L, l_get_block, "get_block"); +} diff --git a/src/logic/scripting/api_lua.h b/src/logic/scripting/api_lua.h new file mode 100644 index 00000000..e48a6548 --- /dev/null +++ b/src/logic/scripting/api_lua.h @@ -0,0 +1,14 @@ +#ifndef LOGIC_SCRIPTING_API_LUA_H_ +#define LOGIC_SCRIPTING_API_LUA_H_ + +#include + +#ifndef LUAJIT_VERSION +#pragma message("better use LuaJIT instead of plain Lua") +#endif // LUAJIT_VERSION + +namespace apilua { + extern void create_funcs(lua_State* L); +} + +#endif // LOGIC_SCRIPTING_API_LUA_H_ diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp new file mode 100644 index 00000000..ed0b19e0 --- /dev/null +++ b/src/logic/scripting/scripting.cpp @@ -0,0 +1,140 @@ +#include "scripting.h" + +#include +#include +#include + +#include "../../files/engine_paths.h" +#include "../../files/files.h" +#include "../../util/timeutil.h" +#include "../../world/Level.h" +#include "../../voxels/Block.h" +#include "api_lua.h" + +using namespace scripting; + +namespace scripting { + extern lua_State* L; + extern EnginePaths* paths; +} + +lua_State* scripting::L = nullptr; +Level* scripting::level = nullptr; +const Content* scripting::content = nullptr; +EnginePaths* scripting::paths = nullptr; + +void delete_global(lua_State* L, const char* name) { + lua_pushnil(L); + lua_setglobal(L, name); +} + +bool rename_global(lua_State* L, const char* src, const char* dst) { + lua_getglobal(L, src); + if (lua_isnil(L, lua_gettop(L))) { + lua_pop(L, lua_gettop(L)); + return false; + } + lua_setglobal(L, dst); + delete_global(L, src); + return true; +} + +void call_func(lua_State* L, int argc, const std::string& name) { + if (lua_pcall(L, argc, LUA_MULTRET, 0)) { + std::cerr << "Lua error in " << name << ": "; + std::cerr << lua_tostring(L,-1) << std::endl; + } +} + +void scripting::initialize(EnginePaths* paths) { + scripting::paths = paths; + + L = luaL_newstate(); + if (L == nullptr) { + throw std::runtime_error("could not to initialize Lua"); + } + luaopen_base(L); + luaopen_math(L); + + std::cout << LUA_VERSION << std::endl; +# ifdef LUAJIT_VERSION + luaopen_jit(L); + std::cout << LUAJIT_VERSION << std::endl; +# endif // LUAJIT_VERSION + + apilua::create_funcs(L); +} + +void scripting::on_world_load(Level* level) { + scripting::level = level; + scripting::content = level->content; + + fs::path file = paths->getResources()/fs::path("scripts/world.lua"); + std::string src = files::read_string(file); + luaL_loadbuffer(L, src.c_str(), src.length(), file.string().c_str()); + call_func(L, 0, "