Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
e357e9d
fix(ci): add chpacker tool with manifest support and optimize deploy …
IOleg-crypto Aug 26, 2026
f0ddfc9
fix(editor): display compression threshold slider as percentage (0% -…
IOleg-crypto Aug 26, 2026
a321fdb
fix(editor): invert compression level slider (100% = max, 0% = raw) a…
IOleg-crypto Aug 26, 2026
869fdea
chore: remove deploy-pages.yml workflow
IOleg-crypto Aug 26, 2026
8101422
fix(assets): rename cyrillic-named file to ASCII to fix CI packing on…
IOleg-crypto Aug 27, 2026
4111514
feat: expand settings system
IOleg-crypto Aug 27, 2026
e9e15ba
fix: load settings.cfg on game startup, not just on options menu open
IOleg-crypto Aug 27, 2026
7e3364b
feat: major engine update - smart asset packing, UI redesign, spawn z…
IOleg-crypto Aug 28, 2026
67154bc
chore: add basis_universal as git submodule (BinomialLLC/basis_univer…
IOleg-crypto Aug 28, 2026
b9eb680
ci(deploy): exclude game bundle from GitHub Release - editor only
IOleg-crypto Aug 28, 2026
f061406
docs: add GitHub Pages landing page for Chained Decos
IOleg-crypto Aug 28, 2026
e69d01a
docs: add Mediafire download link
IOleg-crypto Aug 28, 2026
0903608
fix(ci): require chpacker for manifest mode, remove broken CLI fallback
IOleg-crypto Aug 28, 2026
2523a93
chore: remove chpacker from build, simplify deploy workflow
IOleg-crypto Aug 28, 2026
6d7695d
ci: add GitHub Pages deployment workflow
IOleg-crypto Aug 28, 2026
2853b50
docs: remove GitHub Pages, add download links to readme
IOleg-crypto Aug 28, 2026
f54cd69
refactor: migrate components to NativeProperty/NativeCall attributes …
IOleg-crypto Aug 28, 2026
c664077
Merge branch 'main' into opengl
IOleg-crypto Aug 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 14 additions & 88 deletions .github/workflows/deploy-sdk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ on:
- 'v*'
- 'release/*'
- 'release-*'
branches:
- main
- opengl
- develop
workflow_dispatch:
inputs:
tag_name:
Expand Down Expand Up @@ -95,6 +91,7 @@ jobs:
msystem: MINGW64
update: false
path-type: inherit
cache: true
install: mingw-w64-x86_64-clang lld mingw-w64-x86_64-lld mingw-w64-x86_64-gcc mingw-w64-x86_64-make

- name: Prefer MSYS2 toolchain over preinstalled MinGW
Expand Down Expand Up @@ -209,46 +206,7 @@ jobs:
set -euo pipefail
python tools/sync_resources.py --root . --bin "build/${{ matrix.preset }}/bin" --config ${{ matrix.config }}

- name: Pack game resources into .pack
run: |
set -euo pipefail
WS="${{ github.workspace }}"
BIN_DIR="$WS/build/${{ matrix.preset }}/bin/${{ matrix.config }}"
if [[ ! -d "$BIN_DIR" ]]; then
BIN_DIR="$WS/build/${{ matrix.preset }}/bin"
fi

# Find packer executable (multi-config or single-config)
PACKER="$BIN_DIR/packer"
[[ -f "$PACKER.exe" ]] && PACKER="$PACKER.exe"
if [[ ! -f "$PACKER" ]]; then
# Try searching the whole build tree
PACKER=$(find "$WS/build/${{ matrix.preset }}" -name "packer" -o -name "packer.exe" 2>/dev/null | head -1)
fi
if [[ ! -f "$PACKER" ]]; then
echo "ERROR: packer executable not found" >&2
exit 1
fi
echo "Using packer: $PACKER"

# Find .chproject file for the active game
CHPROJECT=""
for f in "$WS/game/chaineddecos"/*.chproject; do
[[ -f "$f" ]] && CHPROJECT="$f" && break
done

# Create resources.pack from assets/ + resources/
python "$WS/tools/create_pack.py" \
--packer "$PACKER" \
--output "$BIN_DIR" \
${CHPROJECT:+--chproject "$CHPROJECT"} \
--assets "$WS/game/chaineddecos/assets" \
--resources "$WS/resources" \
--split-mb 0 \
--zip-threshold 5 \
--data-version 0

- name: Package Game and Editor bundles
- name: Package Editor bundle
id: package
shell: bash
run: |
Expand Down Expand Up @@ -295,45 +253,17 @@ jobs:
[[ -f "$f" ]] && cp "$f" "$BUNDLE_DIR/" 2>/dev/null || true
done

local ARCHIVE_PATH="$WS/${BUNDLE_NAME}-${VERSION}-${PLATFORM}"
local ARCHIVE_BASE="$WS/${BUNDLE_NAME}-${VERSION}-${PLATFORM}"
if [[ "$IS_WINDOWS" == "true" ]]; then
pushd "$BUNDLE_DIR" > /dev/null
powershell -Command "Compress-Archive -Path * -DestinationPath '${ARCHIVE_PATH}.zip' -Force"
popd > /dev/null
python -c "import shutil; shutil.make_archive(r'${ARCHIVE_BASE}', 'zip', r'${BUNDLE_DIR}')"
echo "${BUNDLE_NAME}-${VERSION}-${PLATFORM}.zip"
else
pushd "$BUNDLE_DIR" > /dev/null
tar -czf "${ARCHIVE_PATH}.tar.gz" .
popd > /dev/null
chmod +x "$BUNDLE_DIR"/* 2>/dev/null || true
python3 -c "import shutil; shutil.make_archive('${ARCHIVE_BASE}', 'gztar', '${BUNDLE_DIR}')"
echo "${BUNDLE_NAME}-${VERSION}-${PLATFORM}.tar.gz"
fi
}

# ── Game bundle: executable + .pack + scripts/ ─────────────────────
GAME_EXE=""
[[ -f "$BIN_DIR/ChainedDecos.exe" ]] && GAME_EXE="$BIN_DIR/ChainedDecos.exe"
[[ -f "$BIN_DIR/ChainedDecos" ]] && GAME_EXE="$BIN_DIR/ChainedDecos"

# Collect .pack files created by create_pack.py
GAME_PACKS=()
for f in "$BIN_DIR"/resources*.pack; do
[[ -f "$f" ]] && GAME_PACKS+=("$f")
done

# Locate .chproject for the game
CHPROJECT=""
for f in "$WS/game/chaineddecos"/*.chproject; do
[[ -f "$f" ]] && CHPROJECT="$f" && break
done

GAME_ARCHIVE=$(make_bundle "ChainedDecos-Game" \
"$GAME_EXE" \
"${GAME_PACKS[@]}" \
${CHPROJECT:+"$CHPROJECT"} \
"$BIN_DIR/scripts" \
"$BIN_DIR/nethost")
echo "game_archive=$GAME_ARCHIVE" >> $GITHUB_OUTPUT

# ── Editor bundle: executable + engine resources only (no game assets) ──
EDITOR_EXE=""
[[ -f "$BIN_DIR/ChainedEditor.exe" ]] && EDITOR_EXE="$BIN_DIR/ChainedEditor.exe"
Expand All @@ -346,18 +276,12 @@ jobs:
"$BIN_DIR/nethost")
echo "editor_archive=$EDITOR_ARCHIVE" >> $GITHUB_OUTPUT

- name: Upload Game artifact
uses: actions/upload-artifact@v4
with:
name: Game-${{ matrix.platform }}
path: ${{ github.workspace }}/${{ steps.package.outputs.game_archive }}
retention-days: 30

- name: Upload Editor artifact
uses: actions/upload-artifact@v4
with:
name: Editor-${{ matrix.platform }}
path: ${{ github.workspace }}/${{ steps.package.outputs.editor_archive }}
compression-level: 1
retention-days: 30

release:
Expand Down Expand Up @@ -400,18 +324,20 @@ jobs:
echo "raw_tag=${RAW_TAG}" >> $GITHUB_OUTPUT
echo "version=${TAG_VERSION}" >> $GITHUB_OUTPUT

- name: Download all artifacts
- name: Download Editor artifacts only
uses: actions/download-artifact@v4
with:
path: release-assets
pattern: "{Game,Editor}-*"
pattern: "Editor-*"
merge-multiple: true

- name: Create Release
uses: softprops/action-gh-release@v2
with:
name: "Chained Engine v${{ steps.info.outputs.version }}"
tag_name: "${{ steps.info.outputs.raw_tag }}"
target_commitish: ${{ github.sha }}
generate_release_notes: true
files: release-assets/*
body: |
### Chained Engine v${{ steps.info.outputs.version }}
Expand All @@ -422,17 +348,17 @@ jobs:

| File | Platform | Contents |
|------|----------|----------|
| `ChainedDecos-Game-*-windows.zip` | Windows | Game executable + .chproject + resources.pack + scripts + nethost/ + DLLs |
| `ChainedDecos-Game-*-linux.tar.gz` | Linux | Game executable + .chproject + resources.pack + scripts + nethost/ |
| `ChainedEditor-*-windows.zip` | Windows | Editor executable + engine resources + nethost/ + DLLs |
| `ChainedEditor-*-linux.tar.gz` | Linux | Editor executable + engine resources + nethost/ |

> **Game** — packaged with `resources.pack` (assets + engine resources compressed), `.chproject` project config, `scripts/` (C# assemblies), and `nethost/` (.NET hosting). On Linux, requires .NET 9+ runtime.
> **Editor** — includes the editor binary, engine resources (shaders, fonts, icons), and `nethost/` (.NET hosting). On Linux, requires .NET 9+ runtime.

#### Requirements
- Windows: no extra dependencies (all DLLs included)
- Linux: requires .NET 9+ runtime for C# scripting

---
> 🎮 **Chained Decos** game is distributed separately.
draft: ${{ inputs.is_draft || false }}
prerelease: ${{ inputs.is_prerelease || false }}
env:
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,6 @@
[submodule "thirdparty/freetype-gl"]
path = thirdparty/freetype-gl
url = https://github.com/rougier/freetype-gl.git
[submodule "thirdparty/basis_universal"]
path = thirdparty/basis_universal
url = https://github.com/BinomialLLC/basis_universal.git
7 changes: 0 additions & 7 deletions .mailmap

This file was deleted.

3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ foreach(GAME_PROJECT_DIR ${GAME_PROJECT_LIST})
endforeach()

add_subdirectory(editor)
# Google Tests for engine
# ── Engine Tools ─────────────────────────────────────────────────────────────
# ── Google Tests for engine
enable_testing()
add_subdirectory(tests)

Expand Down
3 changes: 2 additions & 1 deletion cmake/Dependencies.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,14 @@ include(portable-file-dialogs)
# enet + sodium are the networking transport (added via engine/CMakeLists.txt)
include(reflect-cpp)
include(miniupnpc)
include(basis_universal)

# Disable unity builds for third-party libraries to avoid symbol redefinitions
# (e.g., zstd cover.h has no include guard, causing redefinition under unity build)
# Coral.Native is specifically excluded because MSVC's unity PCH in C++20 mode
# deletes operator<<(wchar_t*) which is used internally by Coral's cerr logging.
foreach(_ext_target
libzstd_static yaml-cpp
libzstd_static yaml-cpp engine_external_basisu_transcoder engine_external_basisu_encoder
glm entt cereal stb spdlog miniaudio
imgui imguizmo
glfw glad
Expand Down
5 changes: 3 additions & 2 deletions cmake/ProjectHelpers.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ endmacro()
function(chained_add_game TARGET_NAME)
set(options)
set(oneValueArgs PROJECT_GAME CSHARP_PROJECT)
set(multiValueArgs SOURCES)
set(multiValueArgs SOURCES RC_SOURCES)
cmake_parse_arguments(GAME "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

# 1. Locate the entry point (main.cpp) (OPTIONAL)
Expand All @@ -81,7 +81,8 @@ function(chained_add_game TARGET_NAME)

# 3. Create the EXECUTABLE target
if(HAS_ENTRY_POINT)
add_executable(${TARGET_NAME}Exe ${ENTRY_SOURCE})
# RC_SOURCES (e.g. app icon .rc) go directly into the exe, not the static lib
add_executable(${TARGET_NAME}Exe ${ENTRY_SOURCE} ${GAME_RC_SOURCES})
target_link_libraries(${TARGET_NAME}Exe PRIVATE engine_runtime_core)

if(GAME_SOURCES)
Expand Down
43 changes: 43 additions & 0 deletions cmake/external/basis_universal.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Basis Universal (Khronos KTX2 / Basis Transcoder & Encoder)
set(BASISU_ROOT "${CMAKE_SOURCE_DIR}/thirdparty/basis_universal")

if(EXISTS "${BASISU_ROOT}/transcoder/basisu_transcoder.cpp")
# 1. Transcoder library (used by Runtime & Engine Assets)
add_library(engine_external_basisu_transcoder STATIC
"${BASISU_ROOT}/transcoder/basisu_transcoder.cpp"
)
target_include_directories(engine_external_basisu_transcoder PUBLIC
"${BASISU_ROOT}/transcoder"
)
target_compile_definitions(engine_external_basisu_transcoder PUBLIC
BASISD_SUPPORT_KTX2=1
BASISD_SUPPORT_KTX2_ZSTD=1
)
target_link_libraries(engine_external_basisu_transcoder PRIVATE libzstd_static)

file(GLOB BASISU_ENCODER_SOURCES "${BASISU_ROOT}/encoder/*.cpp")
list(REMOVE_ITEM BASISU_ENCODER_SOURCES
"${BASISU_ROOT}/encoder/basisu_wasm_api.cpp"
"${BASISU_ROOT}/encoder/basisu_wasm_transcoder_api.cpp"
)
list(APPEND BASISU_ENCODER_SOURCES
"${BASISU_ROOT}/encoder/3rdparty/android_astc_decomp.cpp"
"${BASISU_ROOT}/transcoder/basisu_transcoder.cpp"
)
add_library(engine_external_basisu_encoder STATIC ${BASISU_ENCODER_SOURCES})
target_include_directories(engine_external_basisu_encoder PUBLIC
"${BASISU_ROOT}/encoder"
"${BASISU_ROOT}/transcoder"
)
target_compile_definitions(engine_external_basisu_encoder PUBLIC
BASISD_SUPPORT_KTX2=1
BASISD_SUPPORT_KTX2_ZSTD=1
BASISU_NO_ITERATOR_DEBUG=1
BASISU_SUPPORT_ASTCENC=0
)
target_link_libraries(engine_external_basisu_encoder PRIVATE libzstd_static)

# Disable unity build for basisu files to prevent internal namespace collisions
set_target_properties(engine_external_basisu_transcoder PROPERTIES UNITY_BUILD OFF)
set_target_properties(engine_external_basisu_encoder PROPERTIES UNITY_BUILD OFF)
endif()
45 changes: 43 additions & 2 deletions docs/SCRIPTING_INTEROP.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ The C++ glue functions and C# generator follow strict ABI type mapping rules:
| Float | `float` | `"float"` | `float` |
| Double | `double` | `"double"` | `double` |
| UTF-16 String | `Coral::UCChar*` / `char16_t*` | `"char*"` | `char*` |
| UTF-16 String (property) | `const Coral::UCChar*` / `std::string` | `"string"` | `Marshal.PtrToStringUni(new IntPtr(...))` |
| Vector2 Struct | `glm::vec2*` | `"Vector2"` / `"Vector2*"` | `Chained.Vector2*` (out-pointer) |
| Vector3 Struct | `glm::vec3*` | `"Vector3"` / `"Vector3*"` | `Chained.Vector3*` (out-pointer) |
| Vector4 Struct | `glm::vec4*` | `"Vector4"` / `"Vector4*"` | `Chained.Vector4*` (out-pointer) |
Expand Down Expand Up @@ -123,13 +124,16 @@ Then build normally — the generator runs as part of the build.

### Limitations

The generator handles **simple field access only**: reading/writing a single field on a component. It does NOT handle:
The generator handles **simple field access** and **string properties** automatically. It does NOT handle:
- Physics synchronization (e.g. updating Jolt body when translation changes)
- String conversion (e.g. `Coral::UCChar*` ↔ `std::string`)
- Complex logic (e.g. finding entities by tag, conditional behavior)

For these cases, use `[NativeCall]` and write hand-written glue in `script_glue_*.cpp` — see the manual tutorial below.

**String support**: `[NativeProperty]` with `"string"` type auto-generates `Coral::UCChar*` getters (via `GlueStringPool::ReturnString`) and setters (via `ch_u16_to_string`).

**`[NativeCall]` stubs**: The generator also emits C++ stub functions for `[NativeCall]` attributes. These are placeholders — the actual implementations should be hand-written in `script_glue_*.cpp` files which override the stubs at link time.

---

## 5. Step-by-Step Tutorial: Adding a New Native Property
Expand Down Expand Up @@ -214,6 +218,43 @@ CH_SCRIPT_FUNC void PlayerComponent_SetStamina(uint64_t entityID, float stamina)

Same as the automatic path — the C# Roslyn generator needs the attribute to create the `_Ptr` fields and property body.

### NativeCall methods (functions, not properties)

For methods that take parameters beyond simple field access (e.g. `Play()`, `Stop()`, `CrossFade(int, float)`), use `[NativeCall]`:

**Step 1: Add `[NativeCall]` to C# Component**

```csharp
[NativeCall("Chained.AudioComponent", "AudioComponent_Play", "void", "ulong")]
[NativeCall("Chained.AudioComponent", "AudioComponent_Stop", "void", "ulong")]
[NativeCall("Chained.AnimationComponent", "AnimationComponent_CrossFade", "void", "ulong", "int", "float")]
public partial class AudioComponent : Component
{
public void Play()
{
unsafe { if (AudioComponent_Play_Ptr != null) AudioComponent_Play_Ptr(Entity.ID); }
}
}
```

- **Format**: `[NativeCall("Namespace.Class", "FunctionName", "ReturnType", "param1Type", "param2Type", ...)]`
- First parameter is always `ulong` (entity ID)
- The generator creates `_Ptr` fields and emits C++ stub functions
- You still write the C# wrapper method and the C++ implementation manually

**Step 2: Implement C++ glue** in `script_glue_*.cpp`:

```cpp
CH_SCRIPT_FUNC void AudioComponent_Play(uint64_t entityID)
{
Entity entity = GetEntity(entityID);
if (entity && entity.HasComponent<AudioComponent>())
entity.GetComponent<AudioComponent>().Play();
}
```

Registration is handled automatically by `generate_glue.py`.

---

## 6. Troubleshooting & Generated Files
Expand Down
9 changes: 9 additions & 0 deletions editor/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ if(TARGET pack-static)
target_link_libraries(editor_core PUBLIC pack-static)
endif()

# Dictionary pack support — ZSTD dictionary compression for asset packs
if(TARGET engine_pack)
target_link_libraries(editor_core PUBLIC engine_pack)
endif()

if(TARGET engine_external_basisu_encoder)
target_link_libraries(editor_core PUBLIC engine_external_basisu_encoder)
endif()

# WinHTTP — used by NetworkPanel to fetch the public IP from api.ipify.org.
# Only needed when the networking module is enabled.
if(WIN32 AND CH_NETWORKING)
Expand Down
Loading
Loading