Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
The diff you're trying to view is too large. We only load the first 3000 changed files.
49 changes: 49 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Build and Release

on:
push:
tags:
- 'v*.*.*'

jobs:
build:
name: Build distribution
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'

- name: Install build dependencies
run: python -m pip install build

- name: Build the package
run: python -m build

- name: Store the distribution packages
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/

release:
name: Create GitHub Release
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Download distribution packages
uses: actions/download-artifact@v4
with:
name: dist
path: dist/

- name: Create Release and upload assets
uses: softprops/action-gh-release@v2
with:
files: dist/*
147 changes: 147 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
cmake_minimum_required(VERSION 3.15)

project(endstone_addons CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# --------------------------------------------------------
# 1. OPTIMIZATION SETTINGS (Global)
# --------------------------------------------------------
# ปิดการสร้าง Test, Doc, Example ของทุก Lib แบบ Global
set(BUILD_TESTING OFF CACHE BOOL "" FORCE)
set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(BUILD_DOCS OFF CACHE BOOL "" FORCE)
set(INSTALL_GTEST OFF CACHE BOOL "" FORCE)

# Force static linking
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared libs" FORCE)
set(CMAKE_POSITION_INDEPENDENT_CODE ON CACHE BOOL "PIC" FORCE)

include(FetchContent)

# --------------------------------------------------------
# 2. DEPENDENCIES
# --------------------------------------------------------

# --- FMT ---
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG 9.1.0
GIT_SHALLOW TRUE # <--- โหลดเร็วขึ้น
)
# Config fmt ก่อน MakeAvailable
set(FMT_TEST OFF CACHE BOOL "" FORCE)
set(FMT_DOC OFF CACHE BOOL "" FORCE)
set(FMT_INSTALL OFF CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(fmt)

# --- Endstone ---
FetchContent_Declare(
endstone
GIT_REPOSITORY https://github.com/EndstoneMC/endstone.git
GIT_TAG v0.10
GIT_SHALLOW TRUE # <--- โหลดเร็วขึ้น
PATCH_COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/scripts/patch_logger.cmake
)
FetchContent_MakeAvailable(endstone)

# --- ZLIB ---
FetchContent_Declare(
zlib
GIT_REPOSITORY https://github.com/madler/zlib.git
GIT_TAG v1.3.1
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(zlib)

# Setup ZLIB alias for others
if(NOT TARGET ZLIB::ZLIB)
if(TARGET zlibstatic)
add_library(ZLIB::ZLIB ALIAS zlibstatic)
elseif(TARGET zlib)
add_library(ZLIB::ZLIB ALIAS zlib)
endif()
endif()

# Hack for Curl/CPR detection
link_directories(
"${zlib_BINARY_DIR}/Release"
"${zlib_BINARY_DIR}/Debug"
"${zlib_BINARY_DIR}/RelWithDebInfo"
"${zlib_BINARY_DIR}/MinSizeRel"
)
set(ZLIB_FOUND TRUE CACHE BOOL "ZLIB found" FORCE)
set(ZLIB_INCLUDE_DIR "${zlib_SOURCE_DIR};${zlib_BINARY_DIR}" CACHE PATH "ZLIB include dir" FORCE)
set(ZLIB_LIBRARY zlibstatic.lib CACHE STRING "ZLIB library" FORCE)
set(ZLIB_LIBRARIES ${ZLIB_LIBRARY} CACHE STRING "ZLIB libraries" FORCE)
set(HAVE_LIBZ TRUE CACHE BOOL "Force ZLIB detected" FORCE)
set(HAVE_ZLIB_H TRUE CACHE BOOL "Force ZLIB header detected" FORCE)

# --- nlohmann_json ---
FetchContent_Declare(
json
GIT_REPOSITORY https://github.com/nlohmann/json.git
GIT_TAG v3.11.3
GIT_SHALLOW TRUE
)
set(JSON_BuildTests OFF CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(json)

# --- CPR ---
FetchContent_Declare(
cpr
GIT_REPOSITORY https://github.com/libcpr/cpr.git
GIT_TAG 1.10.5
GIT_SHALLOW TRUE
)
# CPR Options (Minimize build)
set(CPR_USE_SYSTEM_CURL OFF CACHE BOOL "" FORCE)
set(CPR_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(USE_LIBIDN2 OFF CACHE BOOL "" FORCE)
set(CURL_ENABLE_EXPORT_TARGET OFF CACHE BOOL "" FORCE)
set(HTTP_ONLY ON CACHE BOOL "Disable FTP/LDAP etc in CURL to speed up build" FORCE) # <--- เพิ่มตัวนี้เพื่อให้ CURL เล็กลง
set(BUILD_CURL_EXE OFF CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(cpr)

# --- libzip ---
FetchContent_Declare(
libzip
GIT_REPOSITORY https://github.com/nih-at/libzip.git
GIT_TAG v1.10.1
GIT_SHALLOW TRUE
)
set(LIBZIP_DO_INSTALL OFF CACHE BOOL "" FORCE)
set(BUILD_TOOLS OFF CACHE BOOL "" FORCE)
set(BUILD_REGRESS OFF CACHE BOOL "" FORCE)
set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(BUILD_DOC OFF CACHE BOOL "" FORCE)
set(ENABLE_OPENSSL OFF CACHE BOOL "" FORCE)
set(ENABLE_BZIP2 OFF CACHE BOOL "" FORCE)
set(ENABLE_LZMA OFF CACHE BOOL "" FORCE)
set(ENABLE_ZSTD OFF CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(libzip)

# --------------------------------------------------------
# 3. PLUGIN TARGET
# --------------------------------------------------------
file(GLOB_RECURSE SOURCES
"src_cpp/*.cpp"
"src_cpp/*.h"
)

endstone_add_plugin(${PROJECT_NAME} ${SOURCES})

message(STATUS "Endstone Source Dir: ${endstone_SOURCE_DIR}")

target_link_libraries(${PROJECT_NAME} PRIVATE
nlohmann_json::nlohmann_json
cpr::cpr
libzip::zip
)

target_include_directories(${PROJECT_NAME} PRIVATE
src_cpp
${endstone_SOURCE_DIR}/include
)
64 changes: 36 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,70 +1,78 @@
# Addons
A plugin for [Endstone](https://github.com/EndstoneMC) written in Python. It installs Minecraft Bedrock addons automatically by extracting them from a local folder or downloading from the internet.

## Features
- Automatically extracts `.mcaddon` and `.zip` files from a local `addons/` folder.
- Downloads and installs remote addons from configured URLs.
- Supports both behavior packs and resource packs.
- Adds addons to the specified world folder.
A plugin for [Endstone](https://github.com/EndstoneMC) written in Python. It provides a robust system to automatically install and manage Minecraft Bedrock addons by extracting them from a local folder or downloading them from the internet.

## Key Features

- **Automated Installation:** Extracts `.mcpack`, `.mcaddon`, and `.zip` files from a local `addons/` folder.
- **Remote Addons:** Downloads and installs addons from URLs specified in the configuration.
- **Intelligent State Management:**
- **Modification Detection:** Automatically detects when an addon file is changed, cleans up the old version, and installs the new one.
- **Automatic Cleanup:** Removes installed packs if their source addon file is deleted from the `addons/` folder.
- **Self-Healing:** Re-installs packs if it detects they have been manually removed from the world folder.
- **Robust Extraction:** Reliably processes complex addons, including `.mcaddon` files that contain multiple nested `.mcpack` files.
- **Conflict-Free:** Generates unique folder names for each pack to prevent conflicts.
- **World Pack Configuration:** Automatically updates the `world_behavior_packs.json` and `world_resource_packs.json` for the specified world.

## Folder Structure

```
your-server/
├── plugins/
│ ├── addons.py # Your plugin file
│ └── configuration/
│ └── addons/
│ └── config.json # Configuration file
│ ├── config.json # Main configuration file
│ └── processed.json # Stores the state of processed addons (auto-generated)
├── addons/ # Local addons (.zip or .mcaddon) go here
├── addons/ # Place your local addons here
│ ├── ExampleAddon.zip
│ └── CoolPack.mcaddon
└── worlds/
└── Bedrock level/ # World folder (match "world" value from conf.json)
└── Bedrock level/ # Your world folder (must match "world" in config.json)
├── behavior_packs/
└── resource_packs/
```

## Configuration

Create a file at:
Create a file at:
`/plugins/configuration/addons/config.json`

```json
{
"world": "Bedrock level", // Name of the world folder
"world": "Bedrock level",
"addons": [
{
"name": "Some Cool Addon",
"name": "Some-Cool-Addon",
"url": "https://example.com/some-addon.zip"
},
{
"name": "Another Addon",
"name": "Another-Addon",
"url": "https://example.com/another-addon.mcaddon"
}
]
}
```
- **world**: The exact name of your world folder.
- **addons**: A list of remote addons to download. The `name` is used for the downloaded filename.

## Usage
## How It Works

1. **Local Addons**
Place any `.zip` or `.mcaddon` files into the `/addons/` folder.
The plugin is designed to be fully automatic after the initial setup.

2. **Remote Addons**
Define them in the `config.json` file under the `addons` list.
1. **On Server Start:**
- The plugin scans the `/addons/` folder for any new, modified, or removed addon files.
- It downloads any remote addons defined in `config.json` and checks if they have been updated.
- It processes all required addons, extracts their contents into unique folders within the world's `behavior_packs` and `resource_packs` directories.
- It cleans up any packs associated with deleted or modified addon files.
- Finally, it updates the world's configuration files to ensure all addons are enabled.

3. **On Server Start**
- The plugin extracts local addons from `/addons/`.
- Downloads and installs the remote addons listed in the config.
- Automatically puts behavior packs and resource packs into the corresponding subfolders of the specified world.
2. **State Tracking:**
The plugin keeps track of every addon it processes in the `processed.json` file. It stores a "footprint" (a hash or modification date) for each file. This allows it to intelligently determine what needs to be added, updated, or removed on each server start, making the process highly efficient.

## Notes
- `.mcaddon` files are treated as ZIP archives and extracted.
- If the archive includes both `behavior_packs` and `resource_packs`, contents will be copied to both folders.
- Make sure the `"world"` value matches your actual world folder name (case-sensitive).
- If u wanna use github as a source for addons, make sure the URL points to the raw file, e.g. `https://codeload.github.com/Mih4n/portfolio/zip/refs/heads/main`.
Cause plugin uses etag to identify if the file was changed, and github does not provide etag for `https://github.com/Mih4n/portfolio/archive/refs/heads/main.zip`.

- To use a GitHub repository as a source for an addon, ensure the URL points to a raw file download link, not the repository page. For public repositories, the "Code" -> "Download ZIP" link works well.
- The plugin uses file footprints (ETags for remote files, modification times for local files) to detect changes. Some hosts may not provide a stable ETag, which could cause the addon to be re-downloaded. Using direct download links is recommended.
40 changes: 40 additions & 0 deletions build.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
@echo off
chcp 65001 > nul
setlocal

:: Define CMake path - using the one from the virtual environment as per user environment
set CMAKE_EXE=.venv\Scripts\cmake.exe

echo [INFO] Build Script for Endstone Addons
echo [INFO] CMake Path: %CMAKE_EXE%

:: Check if CMake exists
if not exist "%CMAKE_EXE%" (
echo [ERROR] CMake not found at %CMAKE_EXE%
echo Please ensure the virtual environment is set up and cmake is installed.
exit /b 1
)

:: Check if build directory exists
if not exist build (
echo [INFO] Build directory not found. Configuring project...
"%CMAKE_EXE%" -S . -B build -DCMAKE_BUILD_TYPE=Release
if %errorlevel% neq 0 (
echo [ERROR] Configuration failed!
exit /b %errorlevel%
)
) else (
echo [INFO] Build directory exists. Skipping full configuration.
)

:: Build step
echo [INFO] Building Project (Release, Parallel)...
"%CMAKE_EXE%" --build build --config Release --parallel
if %errorlevel% neq 0 (
echo [ERROR] Build failed!
exit /b %errorlevel%
)

echo [SUCCESS] Build finished successfully!
echo You can now reload the plugin on the server.
endlocal
Loading