1
0

Compare commits

..

No commits in common. "master" and "2.0.1" have entirely different histories.

612 changed files with 8006 additions and 18609 deletions

View File

@ -6,9 +6,9 @@ labels: Unconfirmed bug
assignees: ''
---
##### MultiCraft version
##### Minetest version
<!--
Paste MultiCraft version between quotes below
Paste Minetest version between quotes below
If you are on a devel version, please add git commit hash
You can use `minetest --version` to find it.
-->

View File

@ -8,7 +8,7 @@ on:
- 'lib/**.cpp'
- 'src/**.[ch]'
- 'src/**.cpp'
- 'Android/**'
- 'build/android/**'
- '.github/workflows/android.yml'
pull_request:
paths:
@ -16,7 +16,7 @@ on:
- 'lib/**.cpp'
- 'src/**.[ch]'
- 'src/**.cpp'
- 'Android/**'
- 'build/android/**'
- '.github/workflows/android.yml'
jobs:
@ -24,22 +24,22 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 17
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
java-version: '11'
- name: Install GNU gettext
run: sudo apt install gettext
- name: Build with Gradle
run: cd Android; ./gradlew assemblerelease
run: cd build/android; ./gradlew assemblerelease
- name: Save armeabi artifact
uses: actions/upload-artifact@v3
with:
name: MultiCraft-armeabi-v7a.apk
path: Android/app/build/outputs/apk/release/app-armeabi-v7a-release-unsigned.apk
path: build/android/app/build/outputs/apk/release/app-armeabi-v7a-release-unsigned.apk
- name: Save arm64 artifact
uses: actions/upload-artifact@v3
with:
name: MultiCraft-arm64-v8a.apk
path: Android/app/build/outputs/apk/release/app-arm64-v8a-release-unsigned.apk
path: build/android/app/build/outputs/apk/release/app-arm64-v8a-release-unsigned.apk

View File

@ -48,22 +48,22 @@ jobs:
# run: |
# ./bin/multicraft --run-unittests
# This is the current gcc compiler (available in jammy)
gcc_10:
runs-on: ubuntu-22.04
# This is the current gcc compiler (available in bionic)
gcc_8:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
- name: Install deps
run: |
source ./util/ci/common.sh
install_linux_deps g++-10
install_linux_deps g++-8
- name: Build
run: |
./util/ci/build.sh
env:
CC: gcc-10
CXX: g++-10
CC: gcc-8
CXX: g++-8
- name: Test
run: |
@ -92,30 +92,30 @@ jobs:
# ./bin/multicraft --run-unittests
# This is the current clang version
clang_11:
runs-on: ubuntu-22.04
clang_9:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
- name: Install deps
run: |
source ./util/ci/common.sh
install_linux_deps clang-11 valgrind libluajit-5.1-dev
install_linux_deps clang-9 valgrind libluajit-5.1-dev
- name: Build
run: |
./util/ci/build.sh
env:
CC: clang-11
CXX: clang++-11
CC: clang-9
CXX: clang++-9
CMAKE_FLAGS: "-DREQUIRE_LUAJIT=1"
- name: Test
run: |
./bin/multicraft --run-unittests
# - name: Valgrind
# run: |
# valgrind --leak-check=full --leak-check-heuristics=all --undef-value-errors=no --error-exitcode=9 ./bin/multicraft --run-unittests
- name: Valgrind
run: |
valgrind --leak-check=full --leak-check-heuristics=all --undef-value-errors=no --error-exitcode=9 ./bin/multicraft --run-unittests
# Build with prometheus-cpp (server-only)
# clang_9_prometheus:
@ -171,18 +171,18 @@ jobs:
docker:
name: "Docker image"
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
- name: Build docker image
run: |
docker build .
win32:
name: "MinGW cross-compiler (32-bit)"
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
- name: Install compiler
run: |
sudo apt-get update -q && sudo apt-get install gettext -qyy
@ -198,9 +198,9 @@ jobs:
win64:
name: "MinGW cross-compiler (64-bit)"
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
- name: Install compiler
run: |
sudo apt-get update -q && sudo apt-get install gettext -qyy
@ -214,67 +214,25 @@ jobs:
NO_MINETEST_GAME: 1
NO_PACKAGE: 1
msys2-mingw64:
name: "MSYS2-MinGW (64-bit)"
runs-on: windows-latest
defaults:
run:
shell: msys2 {0}
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install MSYS2
uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
update: true
install: mingw-w64-x86_64-clang mingw-w64-x86_64-cmake mingw-w64-x86_64-autotools mingw-w64-x86_64-tcl git zip
- name: Build
run: |
cd ./Windows
./Start.sh
cmake --build . -j
cd -
strip -s ./bin/multicraft.exe
- name: Create ZIP
run: |
mkdir MultiCraft
cp -r ./bin MultiCraft/
cp -r ./builtin MultiCraft/
cp -r ./client MultiCraft/
cp -r ./fonts MultiCraft/
cp -r ./locale MultiCraft/
cp -r ./textures MultiCraft/
zip -r MultiCraft.zip MultiCraft
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: MultiCraft-msys2-mingw64
path: ./MultiCraft.zip
msvc:
name: VS 2022 ${{ matrix.config.arch }}-${{ matrix.type }}
runs-on: windows-2022
name: VS 2019 ${{ matrix.config.arch }}-${{ matrix.type }}
runs-on: windows-2019
env:
VCPKG_VERSION: a42af01b72c28a8e1d7b48107b33e4f286a55ef6
# 2023.11.20
vcpkg_packages: irrlicht zlib curl[winssl] openal-soft libvorbis libogg sqlite3 freetype luajit gmp jsoncpp
VCPKG_VERSION: af2287382b1991dbdcb7e5112d236f3323b9dd7a
# 2022.03.10
vcpkg_packages: irrlicht zlib curl[winssl] openal-soft libvorbis libogg sqlite3 freetype luajit libiconv gettext jsoncpp
strategy:
fail-fast: false
matrix:
config:
- {
arch: x86,
generator: "-G'Visual Studio 17 2022' -A Win32",
generator: "-G'Visual Studio 16 2019' -A Win32",
vcpkg_triplet: x86-windows
}
- {
arch: x64,
generator: "-G'Visual Studio 17 2022' -A x64",
generator: "-G'Visual Studio 16 2019' -A x64",
vcpkg_triplet: x64-windows
}
type: [portable]
@ -284,7 +242,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v2
- name: Restore from cache and run vcpkg
uses: lukka/run-vcpkg@v7
@ -301,6 +259,7 @@ jobs:
-DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}\vcpkg\scripts\buildsystems\vcpkg.cmake" `
-DCMAKE_BUILD_TYPE=Release `
-DENABLE_POSTGRESQL=OFF `
-DENABLE_SYSTEM_JSONCPP=ON `
-DRUN_IN_PLACE=${{ contains(matrix.type, 'portable') }} .
- name: Build
@ -324,5 +283,5 @@ jobs:
- uses: actions/upload-artifact@v3
with:
name: MultiCraft-msvc-${{ matrix.config.arch }}-${{ matrix.type }}
name: msvc-${{ matrix.config.arch }}-${{ matrix.type }}
path: .\Package\

View File

@ -25,27 +25,27 @@ on:
jobs:
clang_format:
runs-on: ubuntu-22.04
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
- name: Install clang-format
run: |
sudo apt-get install clang-format-11 -qyy
sudo apt-get install clang-format-9 -qyy
- name: Run clang-format
run: |
source ./util/ci/lint.sh
perform_lint
env:
CLANG_FORMAT: clang-format-11
CLANG_FORMAT: clang-format-9
clang_tidy:
runs-on: ubuntu-22.04
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
- name: Install deps
run: |
sudo apt-get install clang-tidy-11 -qyy
sudo apt-get install clang-tidy-9 -qyy
source ./util/ci/common.sh
install_linux_deps

View File

@ -14,7 +14,7 @@ on:
jobs:
luacheck:
name: "Builtin Luacheck and Unit Tests"
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: leafo/gh-actions-lua@v9

3
.gitignore vendored
View File

@ -41,7 +41,6 @@ build/.cmake/
/bin/
/games/*
!/games/devtest/
!/games/minetest
/cache
/textures/*
!/textures/base/
@ -56,6 +55,8 @@ build/.cmake/
/clientmods/*
!/clientmods/preview/
/client/mod_storage/
/builtin/mainmenu/hosting/
/textures/base/pack/hosting/
## Configuration/log files
multicraft.conf

View File

@ -1,216 +1,293 @@
---
# Github repository is really at minetest.org using the poikilos git.minetest.io
# https://gitlab.com/minenux/minetest-engine-minetest
# Pipelines URL: https://gitlab.com/minenux/minetest-engine-minetest/pipelines
# packages moved to https://build.opensuse.org/project/show/home:venenux:minenux
# in future we only build here, or made apk packs for alpine
variables:
GIT_SUBMODULE_STRATEGY: recursive
GIT_SUBMODULE_DEPTH: 1
GIT_STRATEGY: clone
# Github repository is cloned every day on Gitlab.com
# https://gitlab.com/minetest/minetest
# Pipelines URL: https://gitlab.com/minetest/minetest/pipelines
stages:
- build
- package
- deploy
.build_template: &build_definition
variables:
MINETEST_GAME_REPO: "https://github.com/minetest/minetest_game.git"
CONTAINER_IMAGE: registry.gitlab.com/$CI_PROJECT_PATH
.build_template:
stage: build
script:
- mkdir cmakebuild
- mkdir -p artifact/multicraft/usr/
- mkdir -p artifact/minetest/usr/
- cd cmakebuild
- cmake -DCMAKE_INSTALL_PREFIX=../artifact/multicraft/usr/ -DBUILD_SERVER=ON -DBUILD_CLIENT=ON -DRUN_IN_PLACE=OFF -DENABLE_CURL=ON -DENABLE_SOUND=ON -DENABLE_LUAJIT=ON -DENABLE_GETTEXT=ON -DENABLE_FREETYPE=ON -DENABLE_SYSTEM_GMP=ON -DENABLE_SYSTEM_JSONCPP=ON -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_POSTGRESQL=ON ..
- make -j$(nproc)
- cmake -DCMAKE_INSTALL_PREFIX=../artifact/minetest/usr/ -DCMAKE_BUILD_TYPE=Release -DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE -DENABLE_SYSTEM_JSONCPP=TRUE -DBUILD_SERVER=TRUE ..
- make -j2
- make install
artifacts:
when: on_success
expire_in: 1y
expire_in: 1h
paths:
- artifact/*
##
## Alpine the limited distro for nonsocial geeks
##
build:alpine-312:
extends: .build_template
image: alpine:3.12
before_script:
- apk update
- apk add build-base cmake pkgconf gettext-dev bzip2-dev curl-dev libnl3-dev rtmpdump-dev libidn2-dev ncurses-dev freetype-dev mesa-dev gmp-dev irrlicht-dev libjpeg-turbo-dev jsoncpp-dev leveldb-dev luajit-dev lua5.1-dev libogg-dev openal-soft-dev libpng-dev postgresql-dev hiredis-dev sqlite-dev libvorbis-dev libxi-dev zlib-dev doxygen libxrandr-dev libx11-dev zstd-dev openssl-dev samurai
build:alpine-314:
extends: .build_template
image: alpine:3.14
before_script:
- apk update
- apk add build-base cmake pkgconf gettext-dev bzip2-dev curl-dev libnl3-dev rtmpdump-dev libidn2-dev ncurses-dev freetype-dev mesa-dev gmp-dev irrlicht-dev libjpeg-turbo-dev jsoncpp-dev leveldb-dev luajit-dev lua5.1-dev libogg-dev openal-soft-dev libpng-dev postgresql-dev hiredis-dev sqlite-dev libvorbis-dev libxi-dev zlib-dev doxygen libxrandr-dev libx11-dev zstd-dev openssl-dev samurai
build:alpine-316:
extends: .build_template
image: alpine:3.16
before_script:
- apk update
- apk add build-base cmake pkgconf gettext-dev bzip2-dev curl-dev libnl3-dev rtmpdump-dev libidn2-dev ncurses-dev freetype-dev mesa-dev gmp-dev irrlicht-dev libjpeg-turbo-dev jsoncpp-dev leveldb-dev luajit-dev lua5.1-dev libogg-dev openal-soft-dev libpng-dev postgresql-dev hiredis-dev sqlite-dev libvorbis-dev libxi-dev zlib-dev doxygen libxrandr-dev libx11-dev zstd-dev openssl-dev samurai
build:alpine-319:
extends: .build_template
image: alpine:3.19
before_script:
- apk update
- apk add build-base cmake pkgconf gettext-dev bzip2-dev curl-dev libnl3-dev rtmpdump-dev libidn2-dev ncurses-dev freetype-dev mesa-dev gmp-dev irrlicht-dev libjpeg-turbo-dev jsoncpp-dev leveldb-dev luajit-dev lua5.1-dev libogg-dev openal-soft-dev libpng-dev postgresql-dev hiredis-dev sqlite-dev libvorbis-dev libxi-dev zlib-dev doxygen libxrandr-dev libx11-dev zstd-dev openssl-dev samurai
# error - need to enable testing repo due spatial index
#build:alpine-edge:
# extends: .build_template
# image: alpine:edge
# before_script:
# - apk update
# - apk add build-base git cmake pkgconf gettext-dev bzip2-dev curl-dev libnl3-dev rtmpdump-dev libidn2-dev ncurses-dev freetype-dev mesa-dev gmp-dev irrlicht-dev libjpeg-turbo-dev jsoncpp-dev leveldb-dev luajit-dev lua5.1-dev libogg-dev openal-soft-dev libpng-dev postgresql-dev hiredis-dev sqlite-dev libvorbis-dev libxi-dev zlib-dev doxygen libxrandr-dev libx11-dev zstd-dev openssl1.1-compat-dev samurai libspatialindex-dev
.debpkg_template:
stage: package
before_script:
- apt-get update -y
- apt-get install -y git
- mkdir -p build/deb/minetest/DEBIAN/
- cp misc/debpkg-control build/deb/minetest/DEBIAN/control
- cp -a artifact/minetest/usr build/deb/minetest/
script:
- git clone $MINETEST_GAME_REPO build/deb/minetest/usr/share/minetest/games/minetest_game
- rm -rf build/deb/minetest/usr/share/minetest/games/minetest/.git
- sed -i 's/DATEPLACEHOLDER/'$(date +%y.%m.%d)'/g' build/deb/minetest/DEBIAN/control
- sed -i 's/LEVELDB_PLACEHOLDER/'$LEVELDB_PKG'/g' build/deb/minetest/DEBIAN/control
- cd build/deb/ && dpkg-deb -b minetest/ && mv minetest.deb ../../
artifacts:
expire_in: 90 day
paths:
- ./*.deb
.debpkg_install:
stage: deploy
before_script:
- apt-get update -y
script:
- apt-get install -y ./*.deb
- minetest --version
##
## Debian mother of many distros
## Debian
##
# Jessie
build:debian-8-64:
<<: *build_definition
image: amd64/debian:8
before_script:
- echo "" > /etc/apt/apt.conf.d/50venenuxcustom
- echo "APT::Get::AllowUnauthenticated \"true\";" >> /etc/apt/apt.conf.d/50venenuxcustom
- echo "Acquire::AllowInsecureRepositories \"true\";" >> /etc/apt/apt.conf.d/50venenuxcustom
- echo "Acquire::AllowDowngradeToInsecureRepositories \"true\";" >> /etc/apt/apt.conf.d/50venenuxcustom
- echo "Acquire::AllowReleaseInfoChange::Suite \"true\";" >> /etc/apt/apt.conf.d/50venenuxcustom
- echo "Acquire::Check-Valid-Until \"false\";" >> /etc/apt/apt.conf.d/50venenuxcustom
- echo "Acquire::Languages \"en\";" >> /etc/apt/apt.conf.d/50venenuxcustom
- echo "Aptitude::CmdLine::Ignore-Trust-Violations \"true\";" >> /etc/apt/apt.conf.d/50venenuxcustom
- rm -rf /etc/apt/sources.list
- echo "deb http://archive.debian.org/debian/ jessie main contrib" > /etc/apt/sources.list.d/50debianoficial.list
- echo "deb http://archive.debian.org/debian/ jessie-backports main contrib non-free" >> /etc/apt/sources.list.d/50debianoficial.list
- echo "deb http://deb.freexian.com/extended-lts jessie main contrib non-free" >> /etc/apt/sources.list.d/50debianoficial.list
- DEBIAN_FRONTEND=noninteractive apt-get update -y || true
- DEBIAN_FRONTEND=noninteractive apt-get -y --force-yes install build-essential cmake pkg-config cmake-data debhelper lsb-release gettext libbz2-dev libcurl4-gnutls-dev libnl-genl-3-dev libnl-3-dev librtmp-dev libidn11-dev libncurses-dev libfreetype6-dev libglu1-mesa-dev libgmp-dev libirrlicht-dev libjpeg-dev libleveldb-dev libluajit-5.1-dev liblua5.1-dev libogg-dev libopenal-dev libpng-dev libpq-dev libhiredis-dev libspatialindex-dev libsqlite3-dev libvorbis-dev libx11-dev libxxf86vm-dev libpq-dev postgresql-server-dev-all libhiredis-dev zlib1g-dev doxygen libxrandr-dev x11proto-xf86vidmode-dev
- DEBIAN_FRONTEND=noninteractive apt-get -y --force-yes -t jessie-backports install libjsoncpp-dev
build:debian-8-32:
<<: *build_definition
image: i386/debian:8
before_script:
- echo "" > /etc/apt/apt.conf.d/50venenuxcustom
- echo "APT::Get::AllowUnauthenticated \"true\";" >> /etc/apt/apt.conf.d/50venenuxcustom
- echo "Acquire::AllowInsecureRepositories \"true\";" >> /etc/apt/apt.conf.d/50venenuxcustom
- echo "Acquire::AllowDowngradeToInsecureRepositories \"true\";" >> /etc/apt/apt.conf.d/50venenuxcustom
- echo "Acquire::AllowReleaseInfoChange::Suite \"true\";" >> /etc/apt/apt.conf.d/50venenuxcustom
- echo "Acquire::Check-Valid-Until \"false\";" >> /etc/apt/apt.conf.d/50venenuxcustom
- echo "Acquire::Languages \"en\";" >> /etc/apt/apt.conf.d/50venenuxcustom
- echo "Aptitude::CmdLine::Ignore-Trust-Violations \"true\";" >> /etc/apt/apt.conf.d/50venenuxcustom
- rm -rf /etc/apt/sources.list
- echo "deb http://archive.debian.org/debian/ jessie main contrib" > /etc/apt/sources.list.d/50debianoficial.list
- echo "deb http://archive.debian.org/debian/ jessie-backports main contrib non-free" >> /etc/apt/sources.list.d/50debianoficial.list
- echo "deb http://deb.freexian.com/extended-lts jessie main contrib non-free" >> /etc/apt/sources.list.d/50debianoficial.list
- DEBIAN_FRONTEND=noninteractive apt-get update -y || true
- DEBIAN_FRONTEND=noninteractive apt-get -y --force-yes install build-essential cmake pkg-config cmake-data debhelper lsb-release gettext libbz2-dev libcurl4-gnutls-dev libnl-genl-3-dev libnl-3-dev librtmp-dev libidn11-dev libncurses-dev libfreetype6-dev libglu1-mesa-dev libgmp-dev libirrlicht-dev libjpeg-dev libleveldb-dev libluajit-5.1-dev liblua5.1-dev libogg-dev libopenal-dev libpng-dev libpq-dev libhiredis-dev libspatialindex-dev libsqlite3-dev libvorbis-dev libx11-dev libxxf86vm-dev libpq-dev postgresql-server-dev-all libhiredis-dev zlib1g-dev doxygen libxrandr-dev x11proto-xf86vidmode-dev
- DEBIAN_FRONTEND=noninteractive apt-get -y --force-yes -t jessie-backports install libjsoncpp-dev
# Stretch
build:debian-9-32:
<<: *build_definition
image: i386/debian:9
build:debian-9:
extends: .build_template
image: debian:9
before_script:
- echo "" > /etc/apt/apt.conf.d/50venenuxcustom
- echo "APT::Get::AllowUnauthenticated \"true\";" >> /etc/apt/apt.conf.d/50venenuxcustom
- echo "Acquire::AllowInsecureRepositories \"true\";" >> /etc/apt/apt.conf.d/50venenuxcustom
- echo "deb http://archive.debian.org/debian/ stretch main contrib" > /etc/apt/sources.list.d/50debianoficial.list
- echo "deb http://deb.freexian.com/extended-lts stretch main contrib non-free" > /etc/apt/sources.list
- DEBIAN_FRONTEND=noninteractive apt-get update -y || true
- DEBIAN_FRONTEND=noninteractive apt-get -y --force-yes install build-essential cmake pkg-config debhelper lsb-release gettext libbz2-dev libcurl4-gnutls-dev libnl-genl-3-dev libnl-3-dev librtmp-dev libidn11-dev libncurses-dev libfreetype6-dev libglu1-mesa-dev libgmp-dev libirrlicht-dev libjpeg-dev libjsoncpp-dev libleveldb-dev libluajit-5.1-dev liblua5.1-dev libogg-dev libopenal-dev libpng-dev libpq-dev libhiredis-dev libspatialindex-dev libsqlite3-dev libvorbis-dev libx11-dev libxxf86vm-dev postgresql-server-dev-all libpq-dev libhiredis-dev zlib1g-dev doxygen libxrandr-dev mesa-common-dev x11proto-xf86vidmode-dev libzstd-dev
- apt-get update -y
- apt-get -y install build-essential libirrlicht-dev cmake libbz2-dev libpng-dev libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev
# Bullseye
package:debian-9:
extends: .debpkg_template
image: debian:9
needs:
- build:debian-9
variables:
LEVELDB_PKG: libleveldb1v5
build:debian-11:
<<: *build_definition
image: debian:11
deploy:debian-9:
extends: .debpkg_install
image: debian:9
needs:
- package:debian-9
# Buster
build:debian-10:
extends: .build_template
image: debian:10
before_script:
- apt-get update -y || true
- apt-get -y install build-essential cmake pkg-config debhelper lsb-release gettext libbz2-dev libcurl4-gnutls-dev libnl-genl-3-dev libnl-3-dev librtmp-dev libidn11-dev libncurses-dev libfreetype6-dev libglu1-mesa-dev libgmp-dev libirrlicht-dev libjpeg-dev libjsoncpp-dev libleveldb-dev libluajit-5.1-dev liblua5.1-dev libogg-dev libopenal-dev libpng-dev libpq-dev postgresql-server-dev-all libhiredis-dev libspatialindex-dev libsqlite3-dev libvorbis-dev libx11-dev libxxf86vm-dev postgresql-server-dev-all libpq-dev libhiredis-dev zlib1g-dev doxygen libxrandr-dev mesa-common-dev x11proto-xf86vidmode-dev libzstd-dev
- apt-get update -y
- apt-get -y install build-essential libirrlicht-dev cmake libbz2-dev libpng-dev libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev
# Bookworm
package:debian-10:
extends: .debpkg_template
image: debian:10
needs:
- build:debian-10
variables:
LEVELDB_PKG: libleveldb1d
build:debian-12:
<<: *build_definition
image: debian:12
before_script:
- apt-get update -y || true
- apt-get -y install build-essential cmake pkg-config debhelper lsb-release gettext libbz2-dev libcurl4-gnutls-dev libnl-genl-3-dev libnl-3-dev librtmp-dev libidn11-dev libncurses-dev libfreetype6-dev libglu1-mesa-dev libgmp-dev libirrlicht-dev libjpeg-dev libjsoncpp-dev libleveldb-dev libluajit-5.1-dev liblua5.1-dev libogg-dev libopenal-dev libpng-dev libpq-dev libhiredis-dev libspatialindex-dev libsqlite3-dev libvorbis-dev libx11-dev libxxf86vm-dev postgresql-server-dev-all libpq-dev libhiredis-dev zlib1g-dev doxygen libxrandr-dev mesa-common-dev x11proto-xf86vidmode-dev libzstd-dev
deploy:debian-10:
extends: .debpkg_install
image: debian:10
needs:
- package:debian-10
##
## winbuntu the distro for stupid users
## Ubuntu
##
build:ubuntu-22.04:
<<: *build_definition
image: ubuntu:jammy
before_script:
- DEBIAN_FRONTEND=noninteractive apt-get update -y
- DEBIAN_FRONTEND=noninteractive apt-get -y install build-essential cmake pkg-config debhelper lsb-release gettext libbz2-dev libcurl4-gnutls-dev libnl-genl-3-dev librtmp-dev libidn11-dev libncurses-dev libfreetype6-dev libglu1-mesa-dev libgmp-dev libirrlicht-dev libjpeg-dev libjsoncpp-dev libleveldb-dev libluajit-5.1-dev liblua5.1-dev libogg-dev libopenal-dev libpng-dev libpq-dev libhiredis-dev libspatialindex-dev libsqlite3-dev libvorbis-dev libx11-dev libxxf86vm-dev postgresql-server-dev-all zlib1g-dev doxygen libxrandr-dev mesa-common-dev x11proto-xf86vidmode-dev libzstd-dev
# Xenial
# Focal most close to bullseye
build:ubuntu-20.04:
<<: *build_definition
image: ubuntu:focal
before_script:
- DEBIAN_FRONTEND=noninteractive apt-get update -y
- DEBIAN_FRONTEND=noninteractive apt-get -y install build-essential cmake pkg-config debhelper lsb-release gettext libbz2-dev libcurl4-gnutls-dev libnl-genl-3-dev librtmp-dev libidn11-dev libncurses-dev libfreetype6-dev libglu1-mesa-dev libgmp-dev libirrlicht-dev libjpeg-dev libjsoncpp-dev libleveldb-dev libluajit-5.1-dev liblua5.1-dev libogg-dev libopenal-dev libpng-dev libpq-dev libhiredis-dev libspatialindex-dev libsqlite3-dev libvorbis-dev libx11-dev libxxf86vm-dev postgresql-server-dev-all zlib1g-dev doxygen libxrandr-dev mesa-common-dev x11proto-xf86vidmode-dev libzstd-dev
# yakkety most close to jessie
build:ubuntu-16.10:
<<: *build_definition
image: ubuntu:yakkety
before_script:
- echo "" > /etc/apt/apt.conf.d/50venenuxcustom
- echo "APT::Get::AllowUnauthenticated \"true\";" >> /etc/apt/apt.conf.d/50venenuxcustom
- echo "Acquire::AllowInsecureRepositories \"true\";" >> /etc/apt/apt.conf.d/50venenuxcustom
- rm -rf /etc/apt/sources.list
- echo "deb http://old-releases.ubuntu.com/ubuntu/ yakkety main restricted universe multiverse" > /etc/apt/sources.list.d/50debianoficial.list
- echo "deb http://old-releases.ubuntu.com/ubuntu/ yakkety-updates main restricted universe multiverse" >> /etc/apt/sources.list.d/50debianoficial.list
- echo "deb http://old-releases.ubuntu.com/ubuntu yakkety-security main restricted universe multiverse" >> /etc/apt/sources.list.d/50debianoficial.list
- apt-get update -y || true
- apt-get -y --force-yes install build-essential cmake pkg-config debhelper lsb-release gettext libbz2-dev libcurl4-gnutls-dev libnl-genl-3-dev libnl-3-dev librtmp-dev libidn11-dev libncurses-dev libfreetype6-dev libglu1-mesa-dev libgmp-dev libirrlicht-dev libjpeg-dev libjsoncpp-dev libleveldb-dev libluajit-5.1-dev liblua5.1-dev libogg-dev libopenal-dev libpng-dev libpq-dev libhiredis-dev libsqlite3-dev libvorbis-dev libx11-dev libxxf86vm-dev libpq-dev libhiredis-dev zlib1g-dev doxygen libxrandr-dev mesa-common-dev x11proto-xf86vidmode-dev
# Zesty most close to stretch
build:ubuntu-17.04:
<<: *build_definition
image: ubuntu:zesty
before_script:
- echo "" > /etc/apt/apt.conf.d/50venenuxcustom
- echo "APT::Get::AllowUnauthenticated \"true\";" >> /etc/apt/apt.conf.d/50venenuxcustom
- echo "Acquire::AllowInsecureRepositories \"true\";" >> /etc/apt/apt.conf.d/50venenuxcustom
- rm -rf /etc/apt/sources.list
- echo "deb http://old-releases.ubuntu.com/ubuntu/ zesty main restricted universe multiverse" > /etc/apt/sources.list.d/50debianoficial.list
- echo "deb http://old-releases.ubuntu.com/ubuntu/ zesty-updates main restricted universe multiverse" >> /etc/apt/sources.list.d/50debianoficial.list
- echo "deb http://old-releases.ubuntu.com/ubuntu zesty-security main restricted universe multiverse" >> /etc/apt/sources.list.d/50debianoficial.list
- apt-get update -y || true
- apt-get -y --force-yes install build-essential cmake pkg-config debhelper dh-systemd dh-autoreconf lsb-release gettext libbz2-dev libcurl4-gnutls-dev libnl-genl-3-dev libnl-3-dev librtmp-dev libidn11-dev libncurses-dev libfreetype6-dev libglu1-mesa-dev libgmp-dev libirrlicht-dev libjpeg-dev libjsoncpp-dev libleveldb-dev libluajit-5.1-dev liblua5.1-dev libogg-dev libopenal-dev libpng-dev libpq-dev libhiredis-dev libspatialindex-dev libsqlite3-dev libvorbis-dev libx11-dev libxxf86vm-dev postgresql-server-dev-all libpq-dev libhiredis-dev zlib1g-dev doxygen libxrandr-dev mesa-common-dev x11proto-xf86vidmode-dev libzstd-dev
##
## Feladora shit distro
##
build:fedora-36:
<<: *build_definition
image: fedora:36
build:ubuntu-16.04:
extends: .build_template
image: ubuntu:xenial
before_script:
- dnf -y install make automake gcc gcc-c++ kernel-devel cmake pkgconfig bzip2-devel gettext-devel sqlite-devel zlib-devel libpng-devel libjpeg-turbo-devel libXxf86vm-devel mesa-libGL-devel irrlicht-devel desktop-file-utils systemd openal* libvorbis* jsoncpp-devel libcurl-devel libcurl luajit-devel leveldb-devel gmp-devel libappstream-glib freetype-devel spatialindex-devel openssl-devel libogg-devel hiredis-devel libzstd-devel libXi-devel ncurses-devel doxygen
- apt-get update -y
- apt-get -y install build-essential libirrlicht-dev cmake libbz2-dev libpng-dev libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev
build:fedora-37:
<<: *build_definition
image: fedora:37
package:ubuntu-16.04:
extends: .debpkg_template
image: ubuntu:xenial
needs:
- build:ubuntu-16.04
variables:
LEVELDB_PKG: libleveldb1v5
deploy:ubuntu-16.04:
extends: .debpkg_install
image: ubuntu:xenial
needs:
- package:ubuntu-16.04
# Bionic
build:ubuntu-18.04:
extends: .build_template
image: ubuntu:bionic
before_script:
- dnf -y install make automake gcc gcc-c++ kernel-devel cmake pkgconfig bzip2-devel gettext-devel sqlite-devel zlib-devel libpng-devel libjpeg-turbo-devel libXxf86vm-devel mesa-libGL-devel irrlicht-devel desktop-file-utils systemd openal* libvorbis* jsoncpp-devel libcurl-devel libcurl luajit-devel leveldb-devel gmp-devel libappstream-glib freetype-devel spatialindex-devel openssl-devel libogg-devel hiredis-devel libzstd-devel libXi-devel ncurses-devel doxygen
- apt-get update -y
- apt-get -y install build-essential libirrlicht-dev cmake libbz2-dev libpng-dev libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev
package:ubuntu-18.04:
extends: .debpkg_template
image: ubuntu:bionic
needs:
- build:ubuntu-18.04
variables:
LEVELDB_PKG: libleveldb1v5
deploy:ubuntu-18.04:
extends: .debpkg_install
image: ubuntu:bionic
needs:
- package:ubuntu-18.04
##
## Fedora
##
# Fedora 28 <-> RHEL 8
build:fedora-28:
extends: .build_template
image: fedora:28
before_script:
- dnf -y install make automake gcc gcc-c++ kernel-devel cmake libcurl-devel openal-soft-devel libvorbis-devel libXxf86vm-devel libogg-devel freetype-devel mesa-libGL-devel zlib-devel jsoncpp-devel irrlicht-devel gmp-devel sqlite-devel luajit-devel leveldb-devel ncurses-devel spatialindex-devel
##
## MinGW for Windows
##
.generic_win_template:
image: ubuntu:bionic
before_script:
- apt-get update -y
- apt-get install -y wget xz-utils unzip git cmake gettext
- wget -nv http://minetest.kitsunemimi.pw/mingw-w64-${WIN_ARCH}_9.2.0_ubuntu18.04.tar.xz -O mingw.tar.xz
- tar -xaf mingw.tar.xz -C /usr
.build_win_template:
extends: .generic_win_template
stage: build
artifacts:
expire_in: 1h
paths:
- build/minetest/_build/*
.package_win_template:
extends: .generic_win_template
stage: package
script:
- unzip build/minetest/_build/minetest-*.zip
- cp -p /usr/${WIN_ARCH}-w64-mingw32/bin/libgcc*.dll minetest-*-win*/bin/
- cp -p /usr/${WIN_ARCH}-w64-mingw32/bin/libstdc++*.dll minetest-*-win*/bin/
- cp -p /usr/${WIN_ARCH}-w64-mingw32/bin/libwinpthread*.dll minetest-*-win*/bin/
artifacts:
expire_in: 90 day
paths:
- minetest-*-win*/*
build:win32:
extends: .build_win_template
script:
- ./util/buildbot/buildwin32.sh build
variables:
WIN_ARCH: "i686"
package:win32:
extends: .package_win_template
needs:
- build:win32
variables:
WIN_ARCH: "i686"
build:win64:
extends: .build_win_template
script:
- ./util/buildbot/buildwin64.sh build
variables:
WIN_ARCH: "x86_64"
package:win64:
extends: .package_win_template
needs:
- build:win64
variables:
WIN_ARCH: "x86_64"
##
## Docker
##
package:docker:
stage: package
image: docker:stable
services:
- docker:dind
before_script:
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN registry.gitlab.com
script:
- docker build . -t ${CONTAINER_IMAGE}/server:$CI_COMMIT_SHA -t ${CONTAINER_IMAGE}/server:$CI_COMMIT_REF_NAME -t ${CONTAINER_IMAGE}/server:latest
- docker push ${CONTAINER_IMAGE}/server:$CI_COMMIT_SHA
- docker push ${CONTAINER_IMAGE}/server:$CI_COMMIT_REF_NAME
- docker push ${CONTAINER_IMAGE}/server:latest
##
## Gitlab Pages (Lua API documentation)
##
pages:
stage: deploy
image: python:3.8
before_script:
- pip install git+https://github.com/Python-Markdown/markdown.git
- pip install git+https://github.com/mkdocs/mkdocs.git
- pip install pygments
script:
- cd doc/mkdocs && ./build.sh
artifacts:
paths:
- public
only:
- master
##
## AppImage
##
package:appimage-client:
stage: package
image: appimagecrafters/appimage-builder
needs:
- build:ubuntu-18.04
before_script:
- apt-get update -y
- apt-get install -y git wget
# Collect files
- mkdir AppDir
- cp -a artifact/minetest/usr/ AppDir/usr/
- rm AppDir/usr/bin/minetestserver
- cp -a clientmods AppDir/usr/share/minetest
script:
- git clone $MINETEST_GAME_REPO AppDir/usr/share/minetest/games/minetest_game
- rm -rf AppDir/usr/share/minetest/games/minetest/.git
- export VERSION=$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA
# Remove PrefersNonDefaultGPU property due to validation errors
- sed -i '/PrefersNonDefaultGPU/d' AppDir/usr/share/applications/net.minetest.minetest.desktop
- appimage-builder --skip-test
artifacts:
expire_in: 90 day
paths:
- ./*.AppImage

4
.gitmodules vendored
View File

@ -1,4 +0,0 @@
[submodule "games/minetest"]
path = games/minetest
url = https://codeberg.org/minenux/minetest-game-minetest
branch = stable-5.2

View File

@ -20,7 +20,7 @@ read_globals = {
string = {fields = {"split", "trim"}},
table = {fields = {"copy", "getn", "indexof", "insert_all"}},
math = {fields = {"hypot", "round"}},
math = {fields = {"hypot"}},
}
globals = {

View File

@ -1,307 +0,0 @@
/*
MultiCraft
Copyright (C) 2014-2023 MoNTE48, Maksim Gamarnik <Maksym48@pm.me>
Copyright (C) 2014-2023 ubulem, Bektur Mambetov <berkut87@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3.0 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package com.multicraft.game
import android.content.res.Configuration
import android.net.Uri
import android.os.*
import android.text.InputType
import android.view.*
import android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN
import android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.browser.customtabs.CustomTabsIntent
import androidx.browser.customtabs.CustomTabsIntent.SHARE_STATE_OFF
import com.multicraft.game.MainActivity.Companion.radius
import com.multicraft.game.databinding.*
import com.multicraft.game.helpers.*
import com.multicraft.game.helpers.ApiLevelHelper.isOreo
import org.libsdl.app.SDLActivity
import java.util.*
import kotlin.system.exitProcess
class GameActivity : SDLActivity() {
companion object {
var isMultiPlayer = false
var isInputActive = false
@JvmStatic
external fun pauseGame()
@JvmStatic
external fun keyboardEvent(keyboard: Boolean)
}
private var messageReturnValue = ""
private var hasKeyboard = false
override fun getLibraries() = arrayOf("MultiCraft")
override fun getMainSharedObject() =
"${getContext().applicationInfo.nativeLibraryDir}/libMultiCraft.so"
override fun onCreate(savedInstanceState: Bundle?) {
try {
super.onCreate(savedInstanceState)
} catch (e: Error) {
exitProcess(0)
} catch (e: Exception) {
exitProcess(0)
}
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
hasKeyboard = hasHardKeyboard()
}
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if (hasFocus) window.makeFullScreen()
}
@Deprecated("Deprecated in Java")
override fun onBackPressed() {
// Ignore the back press so MultiCraft can handle it
}
override fun onPause() {
super.onPause()
pauseGame()
}
override fun onResume() {
super.onResume()
if (hasKeyboard) keyboardEvent(true)
window.makeFullScreen()
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
val statusKeyboard = hasHardKeyboard()
if (hasKeyboard != statusKeyboard) {
hasKeyboard = statusKeyboard
keyboardEvent(hasKeyboard)
}
}
@Suppress("unused")
fun showDialog(hint: String?, current: String?, editType: Int) {
isInputActive = true
messageReturnValue = ""
if (editType == 1)
runOnUiThread { showMultiLineDialog(hint, current) }
else
runOnUiThread { showSingleDialog(hint, current, editType) }
}
private fun showSingleDialog(hint: String?, current: String?, editType: Int) {
val builder = AlertDialog.Builder(this, R.style.FullScreenDialogStyle)
val binding = InputTextBinding.inflate(layoutInflater)
var hintText: String = hint?.ifEmpty {
resources.getString(if (editType == 3) R.string.input_password else R.string.input_text)
}.toString()
hintText = hintText.replace(":$".toRegex(), "")
binding.input.hint = hintText
builder.setView(binding.root)
val alertDialog = builder.create()
val editText = binding.editText
editText.requestFocus()
editText.setText(current.toString())
editText.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN
val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
var inputType = InputType.TYPE_CLASS_TEXT
if (editType == 3) {
inputType = inputType or InputType.TYPE_TEXT_VARIATION_PASSWORD
if (isOreo())
editText.importantForAutofill = View.IMPORTANT_FOR_AUTOFILL_NO
}
editText.inputType = inputType
editText.setSelection(editText.text?.length ?: 0)
// for Android OS
editText.setOnEditorActionListener { _: TextView?, keyCode: Int, _: KeyEvent? ->
if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_ENDCALL) {
imm.hideSoftInputFromWindow(editText.windowToken, 0)
messageReturnValue = editText.text.toString()
alertDialog.dismiss()
isInputActive = false
return@setOnEditorActionListener true
}
return@setOnEditorActionListener false
}
if (isChromebook()) {
editText.setOnKeyListener { _: View?, keyCode: Int, _: KeyEvent? ->
if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_ENDCALL) {
imm.hideSoftInputFromWindow(editText.windowToken, 0)
messageReturnValue = editText.text.toString()
alertDialog.dismiss()
isInputActive = false
return@setOnKeyListener true
}
return@setOnKeyListener false
}
}
binding.input.setEndIconOnClickListener {
imm.hideSoftInputFromWindow(editText.windowToken, 0)
messageReturnValue = editText.text.toString()
alertDialog.dismiss()
isInputActive = false
}
binding.rl.setOnClickListener {
window.setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_HIDDEN)
messageReturnValue = current.toString()
alertDialog.dismiss()
isInputActive = false
}
val alertWindow = alertDialog.window!!
// should be above `show()`
alertWindow.setSoftInputMode(SOFT_INPUT_STATE_VISIBLE)
alertDialog.show()
if (!isTablet())
alertWindow.makeFullScreenAlert()
alertDialog.setOnCancelListener {
window.setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_HIDDEN)
messageReturnValue = current.toString()
isInputActive = false
}
}
private fun showMultiLineDialog(hint: String?, current: String?) {
val builder = AlertDialog.Builder(this, R.style.FullScreenDialogStyle)
val binding = MultilineInputBinding.inflate(layoutInflater)
var hintText: String = hint?.ifEmpty {
resources.getString(R.string.input_text)
}.toString()
hintText = hintText.replace(":$".toRegex(), "")
binding.multiInput.hint = hintText
builder.setView(binding.root)
val alertDialog = builder.create()
val editText = binding.multiEditText
editText.requestFocus()
editText.setText(current.toString())
editText.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN
editText.setSelection(editText.text?.length ?: 0)
val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
// for Android OS
editText.setOnEditorActionListener { _: TextView?, keyCode: Int, _: KeyEvent? ->
if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_ENDCALL) {
imm.hideSoftInputFromWindow(editText.windowToken, 0)
messageReturnValue = editText.text.toString()
alertDialog.dismiss()
isInputActive = false
return@setOnEditorActionListener true
}
return@setOnEditorActionListener false
}
if (isChromebook()) {
editText.setOnKeyListener { _: View?, keyCode: Int, _: KeyEvent? ->
if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_ENDCALL) {
imm.hideSoftInputFromWindow(editText.windowToken, 0)
messageReturnValue = editText.text.toString()
alertDialog.dismiss()
isInputActive = false
return@setOnKeyListener true
}
return@setOnKeyListener false
}
}
binding.multiInput.setEndIconOnClickListener {
imm.hideSoftInputFromWindow(editText.windowToken, 0)
messageReturnValue = editText.text.toString()
alertDialog.dismiss()
isInputActive = false
}
binding.multiRl.setOnClickListener {
window.setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_HIDDEN)
messageReturnValue = current.toString()
alertDialog.dismiss()
isInputActive = false
}
// should be above `show()`
val alertWindow = alertDialog.window!!
alertWindow.setSoftInputMode(SOFT_INPUT_STATE_VISIBLE)
alertDialog.show()
if (!isTablet())
alertWindow.makeFullScreenAlert()
alertDialog.setOnCancelListener {
window.setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_HIDDEN)
messageReturnValue = current.toString()
isInputActive = false
}
}
@Suppress("unused")
fun isDialogActive() = isInputActive
@Suppress("unused")
fun getDialogValue(): String {
val value = messageReturnValue
messageReturnValue = ""
return value
}
@Suppress("unused")
fun getDensity() = resources.displayMetrics.density
@Suppress("unused")
fun notifyServerConnect(multiplayer: Boolean) {
isMultiPlayer = multiplayer
}
@Suppress("unused")
fun notifyExitGame() {
}
@Suppress("unused")
fun openURI(uri: String?) {
val builder = CustomTabsIntent.Builder()
builder.setShareState(SHARE_STATE_OFF)
.setStartAnimations(this, R.anim.slide_in_bottom, R.anim.slide_out_top)
.setExitAnimations(this, R.anim.slide_in_top, R.anim.slide_out_bottom)
val customTabsIntent = builder.build()
try {
customTabsIntent.launchUrl(this, Uri.parse(uri))
} catch (ignored: Exception) {
}
}
@Suppress("unused")
fun finishGame(exc: String?) {
finishApp(true)
}
@Suppress("unused")
fun handleError(exc: String?) {
}
@Suppress("unused")
fun upgrade(item: String) {
}
@Suppress("unused")
fun getSecretKey(key: String): String {
return key
}
@Suppress("unused")
fun getRoundScreen(): Int {
return radius
}
}

View File

@ -1,235 +0,0 @@
/*
MultiCraft
Copyright (C) 2014-2023 MoNTE48, Maksim Gamarnik <Maksym48@pm.me>
Copyright (C) 2014-2023 ubulem, Bektur Mambetov <berkut87@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3.0 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package com.multicraft.game
import android.content.Intent
import android.content.SharedPreferences
import android.graphics.drawable.AnimationDrawable
import android.os.Bundle
import android.provider.Settings.ACTION_WIFI_SETTINGS
import android.provider.Settings.ACTION_WIRELESS_SETTINGS
import android.view.RoundedCorner
import android.view.WindowManager
import androidx.activity.OnBackPressedCallback
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.work.WorkInfo
import com.multicraft.game.databinding.ActivityMainBinding
import com.multicraft.game.dialogs.ConnectionDialog
import com.multicraft.game.helpers.*
import com.multicraft.game.helpers.ApiLevelHelper.isAndroid12
import com.multicraft.game.helpers.ApiLevelHelper.isPie
import com.multicraft.game.helpers.PreferenceHelper.TAG_BUILD_VER
import com.multicraft.game.helpers.PreferenceHelper.getStringValue
import com.multicraft.game.helpers.PreferenceHelper.set
import com.multicraft.game.workmanager.UnzipWorker.Companion.PROGRESS
import com.multicraft.game.workmanager.WorkerViewModel
import com.multicraft.game.workmanager.WorkerViewModelFactory
import kotlinx.coroutines.launch
import java.io.File
import java.io.IOException
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private var externalStorage: File? = null
private val sep = File.separator
private lateinit var prefs: SharedPreferences
private lateinit var restartStartForResult: ActivityResultLauncher<Intent>
private lateinit var connStartForResult: ActivityResultLauncher<Intent>
private val versionCode = BuildConfig.VERSION_CODE
private val versionName = "${BuildConfig.VERSION_NAME}+$versionCode"
companion object {
var radius = 0
const val NO_SPACE_LEFT = "ENOSPC"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
}
})
connStartForResult = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
checkAppVersion()
}
restartStartForResult = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
if (it.resultCode == RESULT_OK)
finishApp(true)
else
finishApp(false)
}
try {
prefs = PreferenceHelper.init(this)
externalStorage = getExternalFilesDir(null)
listOf(filesDir, cacheDir, externalStorage).requireNoNulls()
checkConnection()
} catch (e: Exception) {
val isRestart = e.message?.contains(NO_SPACE_LEFT) != true
showRestartDialog(restartStartForResult, isRestart)
}
}
override fun onResume() {
super.onResume()
window.makeFullScreen()
}
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if (hasFocus) window.makeFullScreen()
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
if (isPie()) {
val cutout = window.decorView.rootWindowInsets.displayCutout
if (cutout != null) {
radius = 40
}
if (isAndroid12()) {
val insets = window.decorView.rootWindowInsets
if (insets != null) {
val tl = insets.getRoundedCorner(RoundedCorner.POSITION_TOP_LEFT)
radius = tl?.radius ?: if (cutout != null) 40 else 0
}
}
}
val animation = binding.loadingAnim.drawable as AnimationDrawable
animation.start()
}
private fun startNative() {
val initLua = File(filesDir, "builtin${sep}mainmenu${sep}init.lua")
if (initLua.exists() && initLua.canRead()) {
val intent = Intent(this, GameActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
} else {
prefs[TAG_BUILD_VER] = "0"
showRestartDialog(restartStartForResult)
}
}
private fun prepareToRun() {
val filesList = mutableListOf<File>().apply {
addAll(listOf(
"builtin",
"client${sep}shaders",
"fonts",
"games${sep}default",
"textures${sep}base"
).map { File(filesDir, it) })
}
val zips = mutableListOf("assets.zip")
lifecycleScope.launch {
filesList.forEach { it.deleteRecursively() }
zips.forEach {
try {
assets.open(it).use { input ->
File(cacheDir, it).copyInputStreamToFile(input)
}
} catch (e: IOException) {
val isNotEnoughSpace = e.message!!.contains(NO_SPACE_LEFT)
runOnUiThread { showRestartDialog(restartStartForResult, !isNotEnoughSpace) }
return@forEach
}
}
try {
startUnzipWorker(zips.toTypedArray())
} catch (e: Exception) {
runOnUiThread { showRestartDialog(restartStartForResult) }
}
}
}
private fun checkAppVersion() {
val prefVersion = prefs.getStringValue(TAG_BUILD_VER)
if (prefVersion == versionName)
startNative()
else
prepareToRun()
}
private fun showConnectionDialog() {
val intent = Intent(this, ConnectionDialog::class.java)
val startForResult = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
when (it.resultCode) {
RESULT_OK -> connStartForResult.launch(Intent(ACTION_WIFI_SETTINGS))
RESULT_FIRST_USER -> connStartForResult.launch(Intent(ACTION_WIRELESS_SETTINGS))
else -> checkAppVersion()
}
}
startForResult.launch(intent)
}
// check connection available
private fun checkConnection() = lifecycleScope.launch {
if (isConnected()) checkAppVersion()
else try {
showConnectionDialog()
} catch (e: Exception) {
checkAppVersion()
}
}
private fun startUnzipWorker(file: Array<String>) {
val viewModelFactory = WorkerViewModelFactory(application, file)
val viewModel = ViewModelProvider(this, viewModelFactory)[WorkerViewModel::class.java]
viewModel.unzippingWorkObserver
.observe(this, Observer { workInfo ->
if (workInfo == null)
return@Observer
val progress = workInfo.progress.getInt(PROGRESS, 0)
if (progress > 0) {
val progressMessage = "${getString(R.string.loading)} $progress%"
binding.tvProgress.text = progressMessage
}
if (workInfo.state.isFinished) {
if (workInfo.state == WorkInfo.State.FAILED) {
val isRestart = workInfo.outputData.getBoolean("restart", true)
showRestartDialog(restartStartForResult, isRestart)
} else if (workInfo.state == WorkInfo.State.SUCCEEDED) {
prefs[TAG_BUILD_VER] = versionName
startNative()
}
}
})
viewModel.startOneTimeWorkRequest()
}
}

View File

@ -1,96 +0,0 @@
/*
MultiCraft
Copyright (C) 2014-2023 MoNTE48, Maksim Gamarnik <Maksym48@pm.me>
Copyright (C) 2014-2023 ubulem, Bektur Mambetov <berkut87@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3.0 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package com.multicraft.game.dialogs
import android.app.Activity
import android.content.Context
import android.content.res.Configuration
import android.os.Bundle
import android.telephony.TelephonyManager
import android.telephony.TelephonyManager.SIM_STATE_READY
import android.view.View
import android.widget.LinearLayout
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity
import com.multicraft.game.databinding.ConnectionDialogBinding
import com.multicraft.game.helpers.ApiLevelHelper.isOreo
import com.multicraft.game.helpers.makeFullScreen
import org.libsdl.app.SDLActivity
class ConnectionDialog : AppCompatActivity() {
private fun isSimCardPresent(): Boolean {
val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
if (!isOreo())
return telephonyManager.simState == SIM_STATE_READY
val isFirstSimPresent = telephonyManager.getSimState(0) == SIM_STATE_READY
val isSecondSimPresent = telephonyManager.getSimState(1) == SIM_STATE_READY
return isFirstSimPresent || isSecondSimPresent
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ConnectionDialogBinding.inflate(layoutInflater)
if (SDLActivity.isTablet()) {
val param = LinearLayout.LayoutParams(
0,
LinearLayout.LayoutParams.WRAP_CONTENT,
0.5f
)
binding.connRoot.layoutParams = param
}
if (isSimCardPresent())
binding.mobile.visibility = View.VISIBLE
setContentView(binding.root)
onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
}
})
binding.wifi.setOnClickListener {
setResult(Activity.RESULT_OK)
finish()
}
binding.mobile.setOnClickListener {
setResult(Activity.RESULT_FIRST_USER)
finish()
}
binding.ignore.setOnClickListener {
setResult(Activity.RESULT_CANCELED)
finish()
}
}
override fun onResume() {
super.onResume()
window.makeFullScreen()
}
override fun attachBaseContext(base: Context?) {
val configuration = Configuration(base?.resources?.configuration)
configuration.fontScale = 1.0f
applyOverrideConfiguration(configuration)
super.attachBaseContext(base)
}
}

View File

@ -1,76 +0,0 @@
/*
MultiCraft
Copyright (C) 2014-2023 MoNTE48, Maksim Gamarnik <Maksym48@pm.me>
Copyright (C) 2014-2023 ubulem, Bektur Mambetov <berkut87@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3.0 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package com.multicraft.game.dialogs
import android.app.Activity
import android.content.Context
import android.content.res.Configuration
import android.os.Bundle
import android.widget.LinearLayout
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity
import com.multicraft.game.databinding.RestartDialogBinding
import com.multicraft.game.helpers.makeFullScreen
import org.libsdl.app.SDLActivity
class RestartDialog : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = RestartDialogBinding.inflate(layoutInflater)
if (SDLActivity.isTablet()) {
val param = LinearLayout.LayoutParams(
0,
LinearLayout.LayoutParams.WRAP_CONTENT,
0.5f
)
binding.restartRoot.layoutParams = param
}
setContentView(binding.root)
onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
}
})
val message = intent.getStringExtra("message")!!
binding.errorDesc.text = message
binding.restart.setOnClickListener {
setResult(Activity.RESULT_OK)
finish()
}
binding.close.setOnClickListener {
setResult(Activity.RESULT_CANCELED)
finish()
}
}
override fun onResume() {
super.onResume()
window.makeFullScreen()
}
override fun attachBaseContext(base: Context?) {
val configuration = Configuration(base?.resources?.configuration)
configuration.fontScale = 1.0f
applyOverrideConfiguration(configuration)
super.attachBaseContext(base)
}
}

View File

@ -1,103 +0,0 @@
/*
MultiCraft
Copyright (C) 2014-2023 MoNTE48, Maksim Gamarnik <Maksym48@pm.me>
Copyright (C) 2014-2023 ubulem, Bektur Mambetov <berkut87@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3.0 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package com.multicraft.game.helpers
import android.app.*
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.net.*
import android.os.*
import android.view.Window
import androidx.activity.result.ActivityResultLauncher
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.*
import com.multicraft.game.*
import com.multicraft.game.databinding.*
import com.multicraft.game.dialogs.RestartDialog
import com.multicraft.game.helpers.ApiLevelHelper.isAndroid12
import com.multicraft.game.helpers.ApiLevelHelper.isMarshmallow
import java.io.*
import java.util.*
// Activity extensions
fun Activity.finishApp(restart: Boolean) {
if (restart) {
val intent = Intent(this, this::class.java)
val mPendingIntentId = 1337
val flag =
if (isAndroid12()) PendingIntent.FLAG_IMMUTABLE else PendingIntent.FLAG_CANCEL_CURRENT
val mgr = getSystemService(Context.ALARM_SERVICE) as AlarmManager
mgr.set(
AlarmManager.RTC, System.currentTimeMillis(), PendingIntent.getActivity(
this, mPendingIntentId, intent, flag
)
)
}
finish()
}
fun Activity.isConnected(): Boolean {
val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
if (isMarshmallow()) {
val activeNetwork = cm.activeNetwork ?: return false
val capabilities = cm.getNetworkCapabilities(activeNetwork) ?: return false
return capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
} else @Suppress("DEPRECATION") {
val activeNetworkInfo = cm.activeNetworkInfo ?: return false
return activeNetworkInfo.isConnected
}
}
fun AppCompatActivity.showRestartDialog(
startForResult: ActivityResultLauncher<Intent>,
isRestart: Boolean = true
) {
val message =
if (isRestart) getString(R.string.restart) else getString(R.string.no_space)
val intent = Intent(this, RestartDialog::class.java)
intent.putExtra("message", message)
startForResult.launch(intent)
}
fun Activity.hasHardKeyboard() =
resources.configuration.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO
// Other extensions
fun File.copyInputStreamToFile(inputStream: InputStream) =
outputStream().use { fileOut -> inputStream.copyTo(fileOut, 8192) }
fun Window.makeFullScreen() {
WindowCompat.setDecorFitsSystemWindows(this, false)
WindowInsetsControllerCompat(this, decorView).let {
it.hide(WindowInsetsCompat.Type.statusBars() or WindowInsetsCompat.Type.navigationBars())
it.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
}
fun Window.makeFullScreenAlert() {
WindowInsetsControllerCompat(this, decorView).let {
it.hide(WindowInsetsCompat.Type.statusBars() or WindowInsetsCompat.Type.navigationBars())
it.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
}

View File

@ -1,22 +0,0 @@
package org.libsdl.app;
import android.hardware.usb.UsbDevice;
interface HIDDevice
{
public int getId();
public int getVendorId();
public int getProductId();
public String getSerialNumber();
public int getVersion();
public String getManufacturerName();
public String getProductName();
public UsbDevice getDevice();
public boolean open();
public int sendFeatureReport(byte[] report);
public int sendOutputReport(byte[] report);
public boolean getFeatureReport(byte[] report);
public void setFrozen(boolean frozen);
public void close();
public void shutdown();
}

View File

@ -1,650 +0,0 @@
package org.libsdl.app;
import android.content.Context;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothGattService;
import android.hardware.usb.UsbDevice;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.os.*;
//import com.android.internal.util.HexDump;
import java.lang.Runnable;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.UUID;
class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDevice {
private static final String TAG = "hidapi";
private HIDDeviceManager mManager;
private BluetoothDevice mDevice;
private int mDeviceId;
private BluetoothGatt mGatt;
private boolean mIsRegistered = false;
private boolean mIsConnected = false;
private boolean mIsChromebook = false;
private boolean mIsReconnecting = false;
private boolean mFrozen = false;
private LinkedList<GattOperation> mOperations;
GattOperation mCurrentOperation = null;
private Handler mHandler;
private static final int TRANSPORT_AUTO = 0;
private static final int TRANSPORT_BREDR = 1;
private static final int TRANSPORT_LE = 2;
private static final int CHROMEBOOK_CONNECTION_CHECK_INTERVAL = 10000;
static public final UUID steamControllerService = UUID.fromString("100F6C32-1735-4313-B402-38567131E5F3");
static public final UUID inputCharacteristic = UUID.fromString("100F6C33-1735-4313-B402-38567131E5F3");
static public final UUID reportCharacteristic = UUID.fromString("100F6C34-1735-4313-B402-38567131E5F3");
static private final byte[] enterValveMode = new byte[] { (byte)0xC0, (byte)0x87, 0x03, 0x08, 0x07, 0x00 };
static class GattOperation {
private enum Operation {
CHR_READ,
CHR_WRITE,
ENABLE_NOTIFICATION
}
Operation mOp;
UUID mUuid;
byte[] mValue;
BluetoothGatt mGatt;
boolean mResult = true;
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid) {
mGatt = gatt;
mOp = operation;
mUuid = uuid;
}
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid, byte[] value) {
mGatt = gatt;
mOp = operation;
mUuid = uuid;
mValue = value;
}
public void run() {
// This is executed in main thread
BluetoothGattCharacteristic chr;
switch (mOp) {
case CHR_READ:
chr = getCharacteristic(mUuid);
//Log.v(TAG, "Reading characteristic " + chr.getUuid());
if (!mGatt.readCharacteristic(chr)) {
Log.e(TAG, "Unable to read characteristic " + mUuid.toString());
mResult = false;
break;
}
mResult = true;
break;
case CHR_WRITE:
chr = getCharacteristic(mUuid);
//Log.v(TAG, "Writing characteristic " + chr.getUuid() + " value=" + HexDump.toHexString(value));
chr.setValue(mValue);
if (!mGatt.writeCharacteristic(chr)) {
Log.e(TAG, "Unable to write characteristic " + mUuid.toString());
mResult = false;
break;
}
mResult = true;
break;
case ENABLE_NOTIFICATION:
chr = getCharacteristic(mUuid);
//Log.v(TAG, "Writing descriptor of " + chr.getUuid());
if (chr != null) {
BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
if (cccd != null) {
int properties = chr.getProperties();
byte[] value;
if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == BluetoothGattCharacteristic.PROPERTY_NOTIFY) {
value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
} else if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) == BluetoothGattCharacteristic.PROPERTY_INDICATE) {
value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;
} else {
Log.e(TAG, "Unable to start notifications on input characteristic");
mResult = false;
return;
}
mGatt.setCharacteristicNotification(chr, true);
cccd.setValue(value);
if (!mGatt.writeDescriptor(cccd)) {
Log.e(TAG, "Unable to write descriptor " + mUuid.toString());
mResult = false;
return;
}
mResult = true;
}
}
}
}
public boolean finish() {
return mResult;
}
private BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
BluetoothGattService valveService = mGatt.getService(steamControllerService);
if (valveService == null)
return null;
return valveService.getCharacteristic(uuid);
}
static public GattOperation readCharacteristic(BluetoothGatt gatt, UUID uuid) {
return new GattOperation(gatt, Operation.CHR_READ, uuid);
}
static public GattOperation writeCharacteristic(BluetoothGatt gatt, UUID uuid, byte[] value) {
return new GattOperation(gatt, Operation.CHR_WRITE, uuid, value);
}
static public GattOperation enableNotification(BluetoothGatt gatt, UUID uuid) {
return new GattOperation(gatt, Operation.ENABLE_NOTIFICATION, uuid);
}
}
public HIDDeviceBLESteamController(HIDDeviceManager manager, BluetoothDevice device) {
mManager = manager;
mDevice = device;
mDeviceId = mManager.getDeviceIDForIdentifier(getIdentifier());
mIsRegistered = false;
mIsChromebook = mManager.getContext().getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
mOperations = new LinkedList<GattOperation>();
mHandler = new Handler(Looper.getMainLooper());
mGatt = connectGatt();
// final HIDDeviceBLESteamController finalThis = this;
// mHandler.postDelayed(new Runnable() {
// @Override
// public void run() {
// finalThis.checkConnectionForChromebookIssue();
// }
// }, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
}
public String getIdentifier() {
return String.format("SteamController.%s", mDevice.getAddress());
}
public BluetoothGatt getGatt() {
return mGatt;
}
// Because on Chromebooks we show up as a dual-mode device, it will attempt to connect TRANSPORT_AUTO, which will use TRANSPORT_BREDR instead
// of TRANSPORT_LE. Let's force ourselves to connect low energy.
private BluetoothGatt connectGatt(boolean managed) {
if (Build.VERSION.SDK_INT >= 23 /* Android 6.0 (M) */) {
try {
return mDevice.connectGatt(mManager.getContext(), managed, this, TRANSPORT_LE);
} catch (Exception e) {
return mDevice.connectGatt(mManager.getContext(), managed, this);
}
} else {
return mDevice.connectGatt(mManager.getContext(), managed, this);
}
}
private BluetoothGatt connectGatt() {
return connectGatt(false);
}
protected int getConnectionState() {
Context context = mManager.getContext();
if (context == null) {
// We are lacking any context to get our Bluetooth information. We'll just assume disconnected.
return BluetoothProfile.STATE_DISCONNECTED;
}
BluetoothManager btManager = (BluetoothManager)context.getSystemService(Context.BLUETOOTH_SERVICE);
if (btManager == null) {
// This device doesn't support Bluetooth. We should never be here, because how did
// we instantiate a device to start with?
return BluetoothProfile.STATE_DISCONNECTED;
}
return btManager.getConnectionState(mDevice, BluetoothProfile.GATT);
}
public void reconnect() {
if (getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
mGatt.disconnect();
mGatt = connectGatt();
}
}
protected void checkConnectionForChromebookIssue() {
if (!mIsChromebook) {
// We only do this on Chromebooks, because otherwise it's really annoying to just attempt
// over and over.
return;
}
int connectionState = getConnectionState();
switch (connectionState) {
case BluetoothProfile.STATE_CONNECTED:
if (!mIsConnected) {
// We are in the Bad Chromebook Place. We can force a disconnect
// to try to recover.
Log.v(TAG, "Chromebook: We are in a very bad state; the controller shows as connected in the underlying Bluetooth layer, but we never received a callback. Forcing a reconnect.");
mIsReconnecting = true;
mGatt.disconnect();
mGatt = connectGatt(false);
break;
}
else if (!isRegistered()) {
if (mGatt.getServices().size() > 0) {
Log.v(TAG, "Chromebook: We are connected to a controller, but never got our registration. Trying to recover.");
probeService(this);
}
else {
Log.v(TAG, "Chromebook: We are connected to a controller, but never discovered services. Trying to recover.");
mIsReconnecting = true;
mGatt.disconnect();
mGatt = connectGatt(false);
break;
}
}
else {
Log.v(TAG, "Chromebook: We are connected, and registered. Everything's good!");
return;
}
break;
case BluetoothProfile.STATE_DISCONNECTED:
Log.v(TAG, "Chromebook: We have either been disconnected, or the Chromebook BtGatt.ContextMap bug has bitten us. Attempting a disconnect/reconnect, but we may not be able to recover.");
mIsReconnecting = true;
mGatt.disconnect();
mGatt = connectGatt(false);
break;
case BluetoothProfile.STATE_CONNECTING:
Log.v(TAG, "Chromebook: We're still trying to connect. Waiting a bit longer.");
break;
}
final HIDDeviceBLESteamController finalThis = this;
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
finalThis.checkConnectionForChromebookIssue();
}
}, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
}
private boolean isRegistered() {
return mIsRegistered;
}
private void setRegistered() {
mIsRegistered = true;
}
private boolean probeService(HIDDeviceBLESteamController controller) {
if (isRegistered()) {
return true;
}
if (!mIsConnected) {
return false;
}
Log.v(TAG, "probeService controller=" + controller);
for (BluetoothGattService service : mGatt.getServices()) {
if (service.getUuid().equals(steamControllerService)) {
Log.v(TAG, "Found Valve steam controller service " + service.getUuid());
for (BluetoothGattCharacteristic chr : service.getCharacteristics()) {
if (chr.getUuid().equals(inputCharacteristic)) {
Log.v(TAG, "Found input characteristic");
// Start notifications
BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
if (cccd != null) {
enableNotification(chr.getUuid());
}
}
}
return true;
}
}
if ((mGatt.getServices().size() == 0) && mIsChromebook && !mIsReconnecting) {
Log.e(TAG, "Chromebook: Discovered services were empty; this almost certainly means the BtGatt.ContextMap bug has bitten us.");
mIsConnected = false;
mIsReconnecting = true;
mGatt.disconnect();
mGatt = connectGatt(false);
}
return false;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
private void finishCurrentGattOperation() {
GattOperation op = null;
synchronized (mOperations) {
if (mCurrentOperation != null) {
op = mCurrentOperation;
mCurrentOperation = null;
}
}
if (op != null) {
boolean result = op.finish(); // TODO: Maybe in main thread as well?
// Our operation failed, let's add it back to the beginning of our queue.
if (!result) {
mOperations.addFirst(op);
}
}
executeNextGattOperation();
}
private void executeNextGattOperation() {
synchronized (mOperations) {
if (mCurrentOperation != null)
return;
if (mOperations.isEmpty())
return;
mCurrentOperation = mOperations.removeFirst();
}
// Run in main thread
mHandler.post(new Runnable() {
@Override
public void run() {
synchronized (mOperations) {
if (mCurrentOperation == null) {
Log.e(TAG, "Current operation null in executor?");
return;
}
mCurrentOperation.run();
// now wait for the GATT callback and when it comes, finish this operation
}
}
});
}
private void queueGattOperation(GattOperation op) {
synchronized (mOperations) {
mOperations.add(op);
}
executeNextGattOperation();
}
private void enableNotification(UUID chrUuid) {
GattOperation op = HIDDeviceBLESteamController.GattOperation.enableNotification(mGatt, chrUuid);
queueGattOperation(op);
}
public void writeCharacteristic(UUID uuid, byte[] value) {
GattOperation op = HIDDeviceBLESteamController.GattOperation.writeCharacteristic(mGatt, uuid, value);
queueGattOperation(op);
}
public void readCharacteristic(UUID uuid) {
GattOperation op = HIDDeviceBLESteamController.GattOperation.readCharacteristic(mGatt, uuid);
queueGattOperation(op);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
////////////// BluetoothGattCallback overridden methods
//////////////////////////////////////////////////////////////////////////////////////////////////////
public void onConnectionStateChange(BluetoothGatt g, int status, int newState) {
//Log.v(TAG, "onConnectionStateChange status=" + status + " newState=" + newState);
mIsReconnecting = false;
if (newState == 2) {
mIsConnected = true;
// Run directly, without GattOperation
if (!isRegistered()) {
mHandler.post(new Runnable() {
@Override
public void run() {
mGatt.discoverServices();
}
});
}
}
else if (newState == 0) {
mIsConnected = false;
}
// Disconnection is handled in SteamLink using the ACTION_ACL_DISCONNECTED Intent.
}
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
//Log.v(TAG, "onServicesDiscovered status=" + status);
if (status == 0) {
if (gatt.getServices().size() == 0) {
Log.v(TAG, "onServicesDiscovered returned zero services; something has gone horribly wrong down in Android's Bluetooth stack.");
mIsReconnecting = true;
mIsConnected = false;
gatt.disconnect();
mGatt = connectGatt(false);
}
else {
probeService(this);
}
}
}
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
//Log.v(TAG, "onCharacteristicRead status=" + status + " uuid=" + characteristic.getUuid());
if (characteristic.getUuid().equals(reportCharacteristic) && !mFrozen) {
mManager.HIDDeviceFeatureReport(getId(), characteristic.getValue());
}
finishCurrentGattOperation();
}
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
//Log.v(TAG, "onCharacteristicWrite status=" + status + " uuid=" + characteristic.getUuid());
if (characteristic.getUuid().equals(reportCharacteristic)) {
// Only register controller with the native side once it has been fully configured
if (!isRegistered()) {
Log.v(TAG, "Registering Steam Controller with ID: " + getId());
mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0, 0, 0, 0);
setRegistered();
}
}
finishCurrentGattOperation();
}
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
// Enable this for verbose logging of controller input reports
//Log.v(TAG, "onCharacteristicChanged uuid=" + characteristic.getUuid() + " data=" + HexDump.dumpHexString(characteristic.getValue()));
if (characteristic.getUuid().equals(inputCharacteristic) && !mFrozen) {
mManager.HIDDeviceInputReport(getId(), characteristic.getValue());
}
}
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
//Log.v(TAG, "onDescriptorRead status=" + status);
}
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
BluetoothGattCharacteristic chr = descriptor.getCharacteristic();
//Log.v(TAG, "onDescriptorWrite status=" + status + " uuid=" + chr.getUuid() + " descriptor=" + descriptor.getUuid());
if (chr.getUuid().equals(inputCharacteristic)) {
boolean hasWrittenInputDescriptor = true;
BluetoothGattCharacteristic reportChr = chr.getService().getCharacteristic(reportCharacteristic);
if (reportChr != null) {
Log.v(TAG, "Writing report characteristic to enter valve mode");
reportChr.setValue(enterValveMode);
gatt.writeCharacteristic(reportChr);
}
}
finishCurrentGattOperation();
}
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
//Log.v(TAG, "onReliableWriteCompleted status=" + status);
}
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
//Log.v(TAG, "onReadRemoteRssi status=" + status);
}
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
//Log.v(TAG, "onMtuChanged status=" + status);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////// Public API
//////////////////////////////////////////////////////////////////////////////////////////////////////
@Override
public int getId() {
return mDeviceId;
}
@Override
public int getVendorId() {
// Valve Corporation
final int VALVE_USB_VID = 0x28DE;
return VALVE_USB_VID;
}
@Override
public int getProductId() {
// We don't have an easy way to query from the Bluetooth device, but we know what it is
final int D0G_BLE2_PID = 0x1106;
return D0G_BLE2_PID;
}
@Override
public String getSerialNumber() {
// This will be read later via feature report by Steam
return "12345";
}
@Override
public int getVersion() {
return 0;
}
@Override
public String getManufacturerName() {
return "Valve Corporation";
}
@Override
public String getProductName() {
return "Steam Controller";
}
@Override
public UsbDevice getDevice() {
return null;
}
@Override
public boolean open() {
return true;
}
@Override
public int sendFeatureReport(byte[] report) {
if (!isRegistered()) {
Log.e(TAG, "Attempted sendFeatureReport before Steam Controller is registered!");
if (mIsConnected) {
probeService(this);
}
return -1;
}
// We need to skip the first byte, as that doesn't go over the air
byte[] actual_report = Arrays.copyOfRange(report, 1, report.length - 1);
//Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(actual_report));
writeCharacteristic(reportCharacteristic, actual_report);
return report.length;
}
@Override
public int sendOutputReport(byte[] report) {
if (!isRegistered()) {
Log.e(TAG, "Attempted sendOutputReport before Steam Controller is registered!");
if (mIsConnected) {
probeService(this);
}
return -1;
}
//Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(report));
writeCharacteristic(reportCharacteristic, report);
return report.length;
}
@Override
public boolean getFeatureReport(byte[] report) {
if (!isRegistered()) {
Log.e(TAG, "Attempted getFeatureReport before Steam Controller is registered!");
if (mIsConnected) {
probeService(this);
}
return false;
}
//Log.v(TAG, "getFeatureReport");
readCharacteristic(reportCharacteristic);
return true;
}
@Override
public void close() {
}
@Override
public void setFrozen(boolean frozen) {
mFrozen = frozen;
}
@Override
public void shutdown() {
close();
BluetoothGatt g = mGatt;
if (g != null) {
g.disconnect();
g.close();
mGatt = null;
}
mManager = null;
mIsRegistered = false;
mIsConnected = false;
mOperations.clear();
}
}

View File

@ -1,683 +0,0 @@
package org.libsdl.app;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.os.Build;
import android.util.Log;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.hardware.usb.*;
import android.os.Handler;
import android.os.Looper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
public class HIDDeviceManager {
private static final String TAG = "hidapi";
private static final String ACTION_USB_PERMISSION = "org.libsdl.app.USB_PERMISSION";
private static HIDDeviceManager sManager;
private static int sManagerRefCount = 0;
public static HIDDeviceManager acquire(Context context) {
if (sManagerRefCount == 0) {
sManager = new HIDDeviceManager(context);
}
++sManagerRefCount;
return sManager;
}
public static void release(HIDDeviceManager manager) {
if (manager == sManager) {
--sManagerRefCount;
if (sManagerRefCount == 0) {
sManager.close();
sManager = null;
}
}
}
private Context mContext;
private HashMap<Integer, HIDDevice> mDevicesById = new HashMap<Integer, HIDDevice>();
private HashMap<BluetoothDevice, HIDDeviceBLESteamController> mBluetoothDevices = new HashMap<BluetoothDevice, HIDDeviceBLESteamController>();
private int mNextDeviceId = 0;
private SharedPreferences mSharedPreferences = null;
private boolean mIsChromebook = false;
private UsbManager mUsbManager;
private Handler mHandler;
private BluetoothManager mBluetoothManager;
private List<BluetoothDevice> mLastBluetoothDevices;
private final BroadcastReceiver mUsbBroadcast = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
handleUsbDeviceAttached(usbDevice);
} else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
handleUsbDeviceDetached(usbDevice);
} else if (action.equals(HIDDeviceManager.ACTION_USB_PERMISSION)) {
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
handleUsbDevicePermission(usbDevice, intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false));
}
}
};
private final BroadcastReceiver mBluetoothBroadcast = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// Bluetooth device was connected. If it was a Steam Controller, handle it
if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.d(TAG, "Bluetooth device connected: " + device);
if (isSteamController(device)) {
connectBluetoothDevice(device);
}
}
// Bluetooth device was disconnected, remove from controller manager (if any)
if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.d(TAG, "Bluetooth device disconnected: " + device);
disconnectBluetoothDevice(device);
}
}
};
private HIDDeviceManager(final Context context) {
mContext = context;
HIDDeviceRegisterCallback();
mSharedPreferences = mContext.getSharedPreferences("hidapi", Context.MODE_PRIVATE);
mIsChromebook = mContext.getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
// if (shouldClear) {
// SharedPreferences.Editor spedit = mSharedPreferences.edit();
// spedit.clear();
// spedit.commit();
// }
// else
{
mNextDeviceId = mSharedPreferences.getInt("next_device_id", 0);
}
}
public Context getContext() {
return mContext;
}
public int getDeviceIDForIdentifier(String identifier) {
SharedPreferences.Editor spedit = mSharedPreferences.edit();
int result = mSharedPreferences.getInt(identifier, 0);
if (result == 0) {
result = mNextDeviceId++;
spedit.putInt("next_device_id", mNextDeviceId);
}
spedit.putInt(identifier, result);
spedit.commit();
return result;
}
private void initializeUSB() {
mUsbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE);
if (mUsbManager == null) {
return;
}
/*
// Logging
for (UsbDevice device : mUsbManager.getDeviceList().values()) {
Log.i(TAG,"Path: " + device.getDeviceName());
Log.i(TAG,"Manufacturer: " + device.getManufacturerName());
Log.i(TAG,"Product: " + device.getProductName());
Log.i(TAG,"ID: " + device.getDeviceId());
Log.i(TAG,"Class: " + device.getDeviceClass());
Log.i(TAG,"Protocol: " + device.getDeviceProtocol());
Log.i(TAG,"Vendor ID " + device.getVendorId());
Log.i(TAG,"Product ID: " + device.getProductId());
Log.i(TAG,"Interface count: " + device.getInterfaceCount());
Log.i(TAG,"---------------------------------------");
// Get interface details
for (int index = 0; index < device.getInterfaceCount(); index++) {
UsbInterface mUsbInterface = device.getInterface(index);
Log.i(TAG," ***** *****");
Log.i(TAG," Interface index: " + index);
Log.i(TAG," Interface ID: " + mUsbInterface.getId());
Log.i(TAG," Interface class: " + mUsbInterface.getInterfaceClass());
Log.i(TAG," Interface subclass: " + mUsbInterface.getInterfaceSubclass());
Log.i(TAG," Interface protocol: " + mUsbInterface.getInterfaceProtocol());
Log.i(TAG," Endpoint count: " + mUsbInterface.getEndpointCount());
// Get endpoint details
for (int epi = 0; epi < mUsbInterface.getEndpointCount(); epi++)
{
UsbEndpoint mEndpoint = mUsbInterface.getEndpoint(epi);
Log.i(TAG," ++++ ++++ ++++");
Log.i(TAG," Endpoint index: " + epi);
Log.i(TAG," Attributes: " + mEndpoint.getAttributes());
Log.i(TAG," Direction: " + mEndpoint.getDirection());
Log.i(TAG," Number: " + mEndpoint.getEndpointNumber());
Log.i(TAG," Interval: " + mEndpoint.getInterval());
Log.i(TAG," Packet size: " + mEndpoint.getMaxPacketSize());
Log.i(TAG," Type: " + mEndpoint.getType());
}
}
}
Log.i(TAG," No more devices connected.");
*/
// Register for USB broadcasts and permission completions
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
filter.addAction(HIDDeviceManager.ACTION_USB_PERMISSION);
mContext.registerReceiver(mUsbBroadcast, filter);
for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) {
handleUsbDeviceAttached(usbDevice);
}
}
UsbManager getUSBManager() {
return mUsbManager;
}
private void shutdownUSB() {
try {
mContext.unregisterReceiver(mUsbBroadcast);
} catch (Exception e) {
// We may not have registered, that's okay
}
}
private boolean isHIDDeviceInterface(UsbDevice usbDevice, UsbInterface usbInterface) {
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) {
return true;
}
if (isXbox360Controller(usbDevice, usbInterface) || isXboxOneController(usbDevice, usbInterface)) {
return true;
}
return false;
}
private boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterface) {
final int XB360_IFACE_SUBCLASS = 93;
final int XB360_IFACE_PROTOCOL = 1; // Wired
final int XB360W_IFACE_PROTOCOL = 129; // Wireless
final int[] SUPPORTED_VENDORS = {
0x0079, // GPD Win 2
0x044f, // Thrustmaster
0x045e, // Microsoft
0x046d, // Logitech
0x056e, // Elecom
0x06a3, // Saitek
0x0738, // Mad Catz
0x07ff, // Mad Catz
0x0e6f, // PDP
0x0f0d, // Hori
0x1038, // SteelSeries
0x11c9, // Nacon
0x12ab, // Unknown
0x1430, // RedOctane
0x146b, // BigBen
0x1532, // Razer Sabertooth
0x15e4, // Numark
0x162e, // Joytech
0x1689, // Razer Onza
0x1949, // Lab126, Inc.
0x1bad, // Harmonix
0x20d6, // PowerA
0x24c6, // PowerA
0x2c22, // Qanba
0x2dc8, // 8BitDo
0x9886, // ASTRO Gaming
};
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
usbInterface.getInterfaceSubclass() == XB360_IFACE_SUBCLASS &&
(usbInterface.getInterfaceProtocol() == XB360_IFACE_PROTOCOL ||
usbInterface.getInterfaceProtocol() == XB360W_IFACE_PROTOCOL)) {
int vendor_id = usbDevice.getVendorId();
for (int supportedVid : SUPPORTED_VENDORS) {
if (vendor_id == supportedVid) {
return true;
}
}
}
return false;
}
private boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterface) {
final int XB1_IFACE_SUBCLASS = 71;
final int XB1_IFACE_PROTOCOL = 208;
final int[] SUPPORTED_VENDORS = {
0x044f, // Thrustmaster
0x045e, // Microsoft
0x0738, // Mad Catz
0x0e6f, // PDP
0x0f0d, // Hori
0x10f5, // Turtle Beach
0x1532, // Razer Wildcat
0x20d6, // PowerA
0x24c6, // PowerA
0x2dc8, // 8BitDo
0x2e24, // Hyperkin
};
if (usbInterface.getId() == 0 &&
usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
usbInterface.getInterfaceSubclass() == XB1_IFACE_SUBCLASS &&
usbInterface.getInterfaceProtocol() == XB1_IFACE_PROTOCOL) {
int vendor_id = usbDevice.getVendorId();
for (int supportedVid : SUPPORTED_VENDORS) {
if (vendor_id == supportedVid) {
return true;
}
}
}
return false;
}
private void handleUsbDeviceAttached(UsbDevice usbDevice) {
connectHIDDeviceUSB(usbDevice);
}
private void handleUsbDeviceDetached(UsbDevice usbDevice) {
List<Integer> devices = new ArrayList<Integer>();
for (HIDDevice device : mDevicesById.values()) {
if (usbDevice.equals(device.getDevice())) {
devices.add(device.getId());
}
}
for (int id : devices) {
HIDDevice device = mDevicesById.get(id);
mDevicesById.remove(id);
device.shutdown();
HIDDeviceDisconnected(id);
}
}
private void handleUsbDevicePermission(UsbDevice usbDevice, boolean permission_granted) {
for (HIDDevice device : mDevicesById.values()) {
if (usbDevice.equals(device.getDevice())) {
boolean opened = false;
if (permission_granted) {
opened = device.open();
}
HIDDeviceOpenResult(device.getId(), opened);
}
}
}
private void connectHIDDeviceUSB(UsbDevice usbDevice) {
synchronized (this) {
int interface_mask = 0;
for (int interface_index = 0; interface_index < usbDevice.getInterfaceCount(); interface_index++) {
UsbInterface usbInterface = usbDevice.getInterface(interface_index);
if (isHIDDeviceInterface(usbDevice, usbInterface)) {
// Check to see if we've already added this interface
// This happens with the Xbox Series X controller which has a duplicate interface 0, which is inactive
int interface_id = usbInterface.getId();
if ((interface_mask & (1 << interface_id)) != 0) {
continue;
}
interface_mask |= (1 << interface_id);
HIDDeviceUSB device = new HIDDeviceUSB(this, usbDevice, interface_index);
int id = device.getId();
mDevicesById.put(id, device);
HIDDeviceConnected(id, device.getIdentifier(), device.getVendorId(), device.getProductId(), device.getSerialNumber(), device.getVersion(), device.getManufacturerName(), device.getProductName(), usbInterface.getId(), usbInterface.getInterfaceClass(), usbInterface.getInterfaceSubclass(), usbInterface.getInterfaceProtocol());
}
}
}
}
private void initializeBluetooth() {
Log.d(TAG, "Initializing Bluetooth");
if (Build.VERSION.SDK_INT <= 30 /* Android 11.0 (R) */ &&
mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH");
return;
}
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) || (Build.VERSION.SDK_INT < 18 /* Android 4.3 (JELLY_BEAN_MR2) */)) {
Log.d(TAG, "Couldn't initialize Bluetooth, this version of Android does not support Bluetooth LE");
return;
}
// Find bonded bluetooth controllers and create SteamControllers for them
mBluetoothManager = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager == null) {
// This device doesn't support Bluetooth.
return;
}
BluetoothAdapter btAdapter = mBluetoothManager.getAdapter();
if (btAdapter == null) {
// This device has Bluetooth support in the codebase, but has no available adapters.
return;
}
// Get our bonded devices.
for (BluetoothDevice device : btAdapter.getBondedDevices()) {
Log.d(TAG, "Bluetooth device available: " + device);
if (isSteamController(device)) {
connectBluetoothDevice(device);
}
}
// NOTE: These don't work on Chromebooks, to my undying dismay.
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
mContext.registerReceiver(mBluetoothBroadcast, filter);
if (mIsChromebook) {
mHandler = new Handler(Looper.getMainLooper());
mLastBluetoothDevices = new ArrayList<BluetoothDevice>();
// final HIDDeviceManager finalThis = this;
// mHandler.postDelayed(new Runnable() {
// @Override
// public void run() {
// finalThis.chromebookConnectionHandler();
// }
// }, 5000);
}
}
private void shutdownBluetooth() {
try {
mContext.unregisterReceiver(mBluetoothBroadcast);
} catch (Exception e) {
// We may not have registered, that's okay
}
}
// Chromebooks do not pass along ACTION_ACL_CONNECTED / ACTION_ACL_DISCONNECTED properly.
// This function provides a sort of dummy version of that, watching for changes in the
// connected devices and attempting to add controllers as things change.
public void chromebookConnectionHandler() {
if (!mIsChromebook) {
return;
}
ArrayList<BluetoothDevice> disconnected = new ArrayList<BluetoothDevice>();
ArrayList<BluetoothDevice> connected = new ArrayList<BluetoothDevice>();
List<BluetoothDevice> currentConnected = mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT);
for (BluetoothDevice bluetoothDevice : currentConnected) {
if (!mLastBluetoothDevices.contains(bluetoothDevice)) {
connected.add(bluetoothDevice);
}
}
for (BluetoothDevice bluetoothDevice : mLastBluetoothDevices) {
if (!currentConnected.contains(bluetoothDevice)) {
disconnected.add(bluetoothDevice);
}
}
mLastBluetoothDevices = currentConnected;
for (BluetoothDevice bluetoothDevice : disconnected) {
disconnectBluetoothDevice(bluetoothDevice);
}
for (BluetoothDevice bluetoothDevice : connected) {
connectBluetoothDevice(bluetoothDevice);
}
final HIDDeviceManager finalThis = this;
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
finalThis.chromebookConnectionHandler();
}
}, 10000);
}
public boolean connectBluetoothDevice(BluetoothDevice bluetoothDevice) {
Log.v(TAG, "connectBluetoothDevice device=" + bluetoothDevice);
synchronized (this) {
if (mBluetoothDevices.containsKey(bluetoothDevice)) {
Log.v(TAG, "Steam controller with address " + bluetoothDevice + " already exists, attempting reconnect");
HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
device.reconnect();
return false;
}
HIDDeviceBLESteamController device = new HIDDeviceBLESteamController(this, bluetoothDevice);
int id = device.getId();
mBluetoothDevices.put(bluetoothDevice, device);
mDevicesById.put(id, device);
// The Steam Controller will mark itself connected once initialization is complete
}
return true;
}
public void disconnectBluetoothDevice(BluetoothDevice bluetoothDevice) {
synchronized (this) {
HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
if (device == null)
return;
int id = device.getId();
mBluetoothDevices.remove(bluetoothDevice);
mDevicesById.remove(id);
device.shutdown();
HIDDeviceDisconnected(id);
}
}
public boolean isSteamController(BluetoothDevice bluetoothDevice) {
// Sanity check. If you pass in a null device, by definition it is never a Steam Controller.
if (bluetoothDevice == null) {
return false;
}
// If the device has no local name, we really don't want to try an equality check against it.
if (bluetoothDevice.getName() == null) {
return false;
}
return bluetoothDevice.getName().equals("SteamController") && ((bluetoothDevice.getType() & BluetoothDevice.DEVICE_TYPE_LE) != 0);
}
private void close() {
shutdownUSB();
shutdownBluetooth();
synchronized (this) {
for (HIDDevice device : mDevicesById.values()) {
device.shutdown();
}
mDevicesById.clear();
mBluetoothDevices.clear();
HIDDeviceReleaseCallback();
}
}
public void setFrozen(boolean frozen) {
synchronized (this) {
for (HIDDevice device : mDevicesById.values()) {
device.setFrozen(frozen);
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
private HIDDevice getDevice(int id) {
synchronized (this) {
HIDDevice result = mDevicesById.get(id);
if (result == null) {
Log.v(TAG, "No device for id: " + id);
Log.v(TAG, "Available devices: " + mDevicesById.keySet());
}
return result;
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
////////// JNI interface functions
//////////////////////////////////////////////////////////////////////////////////////////////////////
public boolean initialize(boolean usb, boolean bluetooth) {
Log.v(TAG, "initialize(" + usb + ", " + bluetooth + ")");
if (usb) {
initializeUSB();
}
if (bluetooth) {
initializeBluetooth();
}
return true;
}
public boolean openDevice(int deviceID) {
Log.v(TAG, "openDevice deviceID=" + deviceID);
HIDDevice device = getDevice(deviceID);
if (device == null) {
HIDDeviceDisconnected(deviceID);
return false;
}
// Look to see if this is a USB device and we have permission to access it
UsbDevice usbDevice = device.getDevice();
if (usbDevice != null && !mUsbManager.hasPermission(usbDevice)) {
HIDDeviceOpenPending(deviceID);
try {
final int FLAG_MUTABLE = 0x02000000; // PendingIntent.FLAG_MUTABLE, but don't require SDK 31
int flags;
if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) {
flags = FLAG_MUTABLE;
} else {
flags = 0;
}
mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, new Intent(HIDDeviceManager.ACTION_USB_PERMISSION), flags));
} catch (Exception e) {
Log.v(TAG, "Couldn't request permission for USB device " + usbDevice);
HIDDeviceOpenResult(deviceID, false);
}
return false;
}
try {
return device.open();
} catch (Exception e) {
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
}
return false;
}
public int sendOutputReport(int deviceID, byte[] report) {
try {
//Log.v(TAG, "sendOutputReport deviceID=" + deviceID + " length=" + report.length);
HIDDevice device;
device = getDevice(deviceID);
if (device == null) {
HIDDeviceDisconnected(deviceID);
return -1;
}
return device.sendOutputReport(report);
} catch (Exception e) {
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
}
return -1;
}
public int sendFeatureReport(int deviceID, byte[] report) {
try {
//Log.v(TAG, "sendFeatureReport deviceID=" + deviceID + " length=" + report.length);
HIDDevice device;
device = getDevice(deviceID);
if (device == null) {
HIDDeviceDisconnected(deviceID);
return -1;
}
return device.sendFeatureReport(report);
} catch (Exception e) {
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
}
return -1;
}
public boolean getFeatureReport(int deviceID, byte[] report) {
try {
//Log.v(TAG, "getFeatureReport deviceID=" + deviceID);
HIDDevice device;
device = getDevice(deviceID);
if (device == null) {
HIDDeviceDisconnected(deviceID);
return false;
}
return device.getFeatureReport(report);
} catch (Exception e) {
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
}
return false;
}
public void closeDevice(int deviceID) {
try {
Log.v(TAG, "closeDevice deviceID=" + deviceID);
HIDDevice device;
device = getDevice(deviceID);
if (device == null) {
HIDDeviceDisconnected(deviceID);
return;
}
device.close();
} catch (Exception e) {
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////// Native methods
//////////////////////////////////////////////////////////////////////////////////////////////////////
private native void HIDDeviceRegisterCallback();
private native void HIDDeviceReleaseCallback();
native void HIDDeviceConnected(int deviceID, String identifier, int vendorId, int productId, String serial_number, int release_number, String manufacturer_string, String product_string, int interface_number, int interface_class, int interface_subclass, int interface_protocol);
native void HIDDeviceOpenPending(int deviceID);
native void HIDDeviceOpenResult(int deviceID, boolean opened);
native void HIDDeviceDisconnected(int deviceID);
native void HIDDeviceInputReport(int deviceID, byte[] report);
native void HIDDeviceFeatureReport(int deviceID, byte[] report);
}

View File

@ -1,309 +0,0 @@
package org.libsdl.app;
import android.hardware.usb.*;
import android.os.Build;
import android.util.Log;
import java.util.Arrays;
class HIDDeviceUSB implements HIDDevice {
private static final String TAG = "hidapi";
protected HIDDeviceManager mManager;
protected UsbDevice mDevice;
protected int mInterfaceIndex;
protected int mInterface;
protected int mDeviceId;
protected UsbDeviceConnection mConnection;
protected UsbEndpoint mInputEndpoint;
protected UsbEndpoint mOutputEndpoint;
protected InputThread mInputThread;
protected boolean mRunning;
protected boolean mFrozen;
public HIDDeviceUSB(HIDDeviceManager manager, UsbDevice usbDevice, int interface_index) {
mManager = manager;
mDevice = usbDevice;
mInterfaceIndex = interface_index;
mInterface = mDevice.getInterface(mInterfaceIndex).getId();
mDeviceId = manager.getDeviceIDForIdentifier(getIdentifier());
mRunning = false;
}
public String getIdentifier() {
return String.format("%s/%x/%x/%d", mDevice.getDeviceName(), mDevice.getVendorId(), mDevice.getProductId(), mInterfaceIndex);
}
@Override
public int getId() {
return mDeviceId;
}
@Override
public int getVendorId() {
return mDevice.getVendorId();
}
@Override
public int getProductId() {
return mDevice.getProductId();
}
@Override
public String getSerialNumber() {
String result = null;
if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) {
try {
result = mDevice.getSerialNumber();
}
catch (SecurityException exception) {
//Log.w(TAG, "App permissions mean we cannot get serial number for device " + getDeviceName() + " message: " + exception.getMessage());
}
}
if (result == null) {
result = "";
}
return result;
}
@Override
public int getVersion() {
return 0;
}
@Override
public String getManufacturerName() {
String result = null;
if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) {
result = mDevice.getManufacturerName();
}
if (result == null) {
result = String.format("%x", getVendorId());
}
return result;
}
@Override
public String getProductName() {
String result = null;
if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) {
result = mDevice.getProductName();
}
if (result == null) {
result = String.format("%x", getProductId());
}
return result;
}
@Override
public UsbDevice getDevice() {
return mDevice;
}
public String getDeviceName() {
return getManufacturerName() + " " + getProductName() + "(0x" + String.format("%x", getVendorId()) + "/0x" + String.format("%x", getProductId()) + ")";
}
@Override
public boolean open() {
mConnection = mManager.getUSBManager().openDevice(mDevice);
if (mConnection == null) {
Log.w(TAG, "Unable to open USB device " + getDeviceName());
return false;
}
// Force claim our interface
UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
if (!mConnection.claimInterface(iface, true)) {
Log.w(TAG, "Failed to claim interfaces on USB device " + getDeviceName());
close();
return false;
}
// Find the endpoints
for (int j = 0; j < iface.getEndpointCount(); j++) {
UsbEndpoint endpt = iface.getEndpoint(j);
switch (endpt.getDirection()) {
case UsbConstants.USB_DIR_IN:
if (mInputEndpoint == null) {
mInputEndpoint = endpt;
}
break;
case UsbConstants.USB_DIR_OUT:
if (mOutputEndpoint == null) {
mOutputEndpoint = endpt;
}
break;
}
}
// Make sure the required endpoints were present
if (mInputEndpoint == null || mOutputEndpoint == null) {
Log.w(TAG, "Missing required endpoint on USB device " + getDeviceName());
close();
return false;
}
// Start listening for input
mRunning = true;
mInputThread = new InputThread();
mInputThread.start();
return true;
}
@Override
public int sendFeatureReport(byte[] report) {
int res = -1;
int offset = 0;
int length = report.length;
boolean skipped_report_id = false;
byte report_number = report[0];
if (report_number == 0x0) {
++offset;
--length;
skipped_report_id = true;
}
res = mConnection.controlTransfer(
UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_OUT,
0x09/*HID set_report*/,
(3/*HID feature*/ << 8) | report_number,
mInterface,
report, offset, length,
1000/*timeout millis*/);
if (res < 0) {
Log.w(TAG, "sendFeatureReport() returned " + res + " on device " + getDeviceName());
return -1;
}
if (skipped_report_id) {
++length;
}
return length;
}
@Override
public int sendOutputReport(byte[] report) {
int r = mConnection.bulkTransfer(mOutputEndpoint, report, report.length, 1000);
if (r != report.length) {
Log.w(TAG, "sendOutputReport() returned " + r + " on device " + getDeviceName());
}
return r;
}
@Override
public boolean getFeatureReport(byte[] report) {
int res = -1;
int offset = 0;
int length = report.length;
boolean skipped_report_id = false;
byte report_number = report[0];
if (report_number == 0x0) {
/* Offset the return buffer by 1, so that the report ID
will remain in byte 0. */
++offset;
--length;
skipped_report_id = true;
}
res = mConnection.controlTransfer(
UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_IN,
0x01/*HID get_report*/,
(3/*HID feature*/ << 8) | report_number,
mInterface,
report, offset, length,
1000/*timeout millis*/);
if (res < 0) {
Log.w(TAG, "getFeatureReport() returned " + res + " on device " + getDeviceName());
return false;
}
if (skipped_report_id) {
++res;
++length;
}
byte[] data;
if (res == length) {
data = report;
} else {
data = Arrays.copyOfRange(report, 0, res);
}
mManager.HIDDeviceFeatureReport(mDeviceId, data);
return true;
}
@Override
public void close() {
mRunning = false;
if (mInputThread != null) {
while (mInputThread.isAlive()) {
mInputThread.interrupt();
try {
mInputThread.join();
} catch (InterruptedException e) {
// Keep trying until we're done
}
}
mInputThread = null;
}
if (mConnection != null) {
UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
mConnection.releaseInterface(iface);
mConnection.close();
mConnection = null;
}
}
@Override
public void shutdown() {
close();
mManager = null;
}
@Override
public void setFrozen(boolean frozen) {
mFrozen = frozen;
}
protected class InputThread extends Thread {
@Override
public void run() {
int packetSize = mInputEndpoint.getMaxPacketSize();
byte[] packet = new byte[packetSize];
while (mRunning) {
int r;
try
{
r = mConnection.bulkTransfer(mInputEndpoint, packet, packetSize, 1000);
}
catch (Exception e)
{
Log.v(TAG, "Exception in UsbDeviceConnection bulktransfer: " + e);
break;
}
if (r < 0) {
// Could be a timeout or an I/O error
}
if (r > 0) {
byte[] data;
if (r == packetSize) {
data = packet;
} else {
data = Arrays.copyOfRange(packet, 0, r);
}
if (!mFrozen) {
mManager.HIDDeviceInputReport(mDeviceId, data);
}
}
}
}
}
}

View File

@ -1,86 +0,0 @@
package org.libsdl.app;
import android.content.Context;
import java.lang.Class;
import java.lang.reflect.Method;
/**
SDL library initialization
*/
public class SDL {
// This function should be called first and sets up the native code
// so it can call into the Java classes
public static void setupJNI() {
SDLActivity.nativeSetupJNI();
SDLAudioManager.nativeSetupJNI();
SDLControllerManager.nativeSetupJNI();
}
// This function should be called each time the activity is started
public static void initialize() {
setContext(null);
SDLActivity.initialize();
SDLAudioManager.initialize();
SDLControllerManager.initialize();
}
// This function stores the current activity (SDL or not)
public static void setContext(Context context) {
SDLAudioManager.setContext(context);
mContext = context;
}
public static Context getContext() {
return mContext;
}
public static void loadLibrary(String libraryName) throws UnsatisfiedLinkError, SecurityException, NullPointerException {
if (libraryName == null) {
throw new NullPointerException("No library name provided.");
}
try {
// Let's see if we have ReLinker available in the project. This is necessary for
// some projects that have huge numbers of local libraries bundled, and thus may
// trip a bug in Android's native library loader which ReLinker works around. (If
// loadLibrary works properly, ReLinker will simply use the normal Android method
// internally.)
//
// To use ReLinker, just add it as a dependency. For more information, see
// https://github.com/KeepSafe/ReLinker for ReLinker's repository.
//
Class<?> relinkClass = mContext.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker");
Class<?> relinkListenerClass = mContext.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker$LoadListener");
Class<?> contextClass = mContext.getClassLoader().loadClass("android.content.Context");
Class<?> stringClass = mContext.getClassLoader().loadClass("java.lang.String");
// Get a 'force' instance of the ReLinker, so we can ensure libraries are reinstalled if
// they've changed during updates.
Method forceMethod = relinkClass.getDeclaredMethod("force");
Object relinkInstance = forceMethod.invoke(null);
Class<?> relinkInstanceClass = relinkInstance.getClass();
// Actually load the library!
Method loadMethod = relinkInstanceClass.getDeclaredMethod("loadLibrary", contextClass, stringClass, stringClass, relinkListenerClass);
loadMethod.invoke(relinkInstance, mContext, libraryName, null, null);
}
catch (final Throwable e) {
// Fall back
try {
System.loadLibrary(libraryName);
}
catch (final UnsatisfiedLinkError ule) {
throw ule;
}
catch (final SecurityException se) {
throw se;
}
}
}
protected static Context mContext;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,514 +0,0 @@
package org.libsdl.app;
import android.content.Context;
import android.media.AudioDeviceCallback;
import android.media.AudioDeviceInfo;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.os.Build;
import android.util.Log;
import java.util.Arrays;
public class SDLAudioManager {
protected static final String TAG = "SDLAudio";
protected static AudioTrack mAudioTrack;
protected static AudioRecord mAudioRecord;
protected static Context mContext;
private static final int[] NO_DEVICES = {};
private static AudioDeviceCallback mAudioDeviceCallback;
public static void initialize() {
mAudioTrack = null;
mAudioRecord = null;
mAudioDeviceCallback = null;
if(Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */)
{
mAudioDeviceCallback = new AudioDeviceCallback() {
@Override
public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
Arrays.stream(addedDevices).forEach(deviceInfo -> addAudioDevice(deviceInfo.isSink(), deviceInfo.getId()));
}
@Override
public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
Arrays.stream(removedDevices).forEach(deviceInfo -> removeAudioDevice(deviceInfo.isSink(), deviceInfo.getId()));
}
};
}
}
public static void setContext(Context context) {
mContext = context;
if (context != null) {
registerAudioDeviceCallback();
}
}
public static void release(Context context) {
unregisterAudioDeviceCallback(context);
}
// Audio
protected static String getAudioFormatString(int audioFormat) {
switch (audioFormat) {
case AudioFormat.ENCODING_PCM_8BIT:
return "8-bit";
case AudioFormat.ENCODING_PCM_16BIT:
return "16-bit";
case AudioFormat.ENCODING_PCM_FLOAT:
return "float";
default:
return Integer.toString(audioFormat);
}
}
protected static int[] open(boolean isCapture, int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
int channelConfig;
int sampleSize;
int frameSize;
Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", requested " + desiredFrames + " frames of " + desiredChannels + " channel " + getAudioFormatString(audioFormat) + " audio at " + sampleRate + " Hz");
/* On older devices let's use known good settings */
if (Build.VERSION.SDK_INT < 21 /* Android 5.0 (LOLLIPOP) */) {
if (desiredChannels > 2) {
desiredChannels = 2;
}
}
/* AudioTrack has sample rate limitation of 48000 (fixed in 5.0.2) */
if (Build.VERSION.SDK_INT < 22 /* Android 5.1 (LOLLIPOP_MR1) */) {
if (sampleRate < 8000) {
sampleRate = 8000;
} else if (sampleRate > 48000) {
sampleRate = 48000;
}
}
if (audioFormat == AudioFormat.ENCODING_PCM_FLOAT) {
int minSDKVersion = (isCapture ? 23 /* Android 6.0 (M) */ : 21 /* Android 5.0 (LOLLIPOP) */);
if (Build.VERSION.SDK_INT < minSDKVersion) {
audioFormat = AudioFormat.ENCODING_PCM_16BIT;
}
}
switch (audioFormat)
{
case AudioFormat.ENCODING_PCM_8BIT:
sampleSize = 1;
break;
case AudioFormat.ENCODING_PCM_16BIT:
sampleSize = 2;
break;
case AudioFormat.ENCODING_PCM_FLOAT:
sampleSize = 4;
break;
default:
Log.v(TAG, "Requested format " + audioFormat + ", getting ENCODING_PCM_16BIT");
audioFormat = AudioFormat.ENCODING_PCM_16BIT;
sampleSize = 2;
break;
}
if (isCapture) {
switch (desiredChannels) {
case 1:
channelConfig = AudioFormat.CHANNEL_IN_MONO;
break;
case 2:
channelConfig = AudioFormat.CHANNEL_IN_STEREO;
break;
default:
Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo");
desiredChannels = 2;
channelConfig = AudioFormat.CHANNEL_IN_STEREO;
break;
}
} else {
switch (desiredChannels) {
case 1:
channelConfig = AudioFormat.CHANNEL_OUT_MONO;
break;
case 2:
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
break;
case 3:
channelConfig = AudioFormat.CHANNEL_OUT_STEREO | AudioFormat.CHANNEL_OUT_FRONT_CENTER;
break;
case 4:
channelConfig = AudioFormat.CHANNEL_OUT_QUAD;
break;
case 5:
channelConfig = AudioFormat.CHANNEL_OUT_QUAD | AudioFormat.CHANNEL_OUT_FRONT_CENTER;
break;
case 6:
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
break;
case 7:
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1 | AudioFormat.CHANNEL_OUT_BACK_CENTER;
break;
case 8:
if (Build.VERSION.SDK_INT >= 23 /* Android 6.0 (M) */) {
channelConfig = AudioFormat.CHANNEL_OUT_7POINT1_SURROUND;
} else {
Log.v(TAG, "Requested " + desiredChannels + " channels, getting 5.1 surround");
desiredChannels = 6;
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
}
break;
default:
Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo");
desiredChannels = 2;
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
break;
}
/*
Log.v(TAG, "Speaker configuration (and order of channels):");
if ((channelConfig & 0x00000004) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_LEFT");
}
if ((channelConfig & 0x00000008) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_RIGHT");
}
if ((channelConfig & 0x00000010) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_CENTER");
}
if ((channelConfig & 0x00000020) != 0) {
Log.v(TAG, " CHANNEL_OUT_LOW_FREQUENCY");
}
if ((channelConfig & 0x00000040) != 0) {
Log.v(TAG, " CHANNEL_OUT_BACK_LEFT");
}
if ((channelConfig & 0x00000080) != 0) {
Log.v(TAG, " CHANNEL_OUT_BACK_RIGHT");
}
if ((channelConfig & 0x00000100) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_LEFT_OF_CENTER");
}
if ((channelConfig & 0x00000200) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_RIGHT_OF_CENTER");
}
if ((channelConfig & 0x00000400) != 0) {
Log.v(TAG, " CHANNEL_OUT_BACK_CENTER");
}
if ((channelConfig & 0x00000800) != 0) {
Log.v(TAG, " CHANNEL_OUT_SIDE_LEFT");
}
if ((channelConfig & 0x00001000) != 0) {
Log.v(TAG, " CHANNEL_OUT_SIDE_RIGHT");
}
*/
}
frameSize = (sampleSize * desiredChannels);
// Let the user pick a larger buffer if they really want -- but ye
// gods they probably shouldn't, the minimums are horrifyingly high
// latency already
int minBufferSize;
if (isCapture) {
minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
} else {
minBufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat);
}
desiredFrames = Math.max(desiredFrames, (minBufferSize + frameSize - 1) / frameSize);
int[] results = new int[4];
if (isCapture) {
if (mAudioRecord == null) {
mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate,
channelConfig, audioFormat, desiredFrames * frameSize);
// see notes about AudioTrack state in audioOpen(), above. Probably also applies here.
if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
Log.e(TAG, "Failed during initialization of AudioRecord");
mAudioRecord.release();
mAudioRecord = null;
return null;
}
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */ && deviceId != 0) {
mAudioRecord.setPreferredDevice(getOutputAudioDeviceInfo(deviceId));
}
mAudioRecord.startRecording();
}
results[0] = mAudioRecord.getSampleRate();
results[1] = mAudioRecord.getAudioFormat();
results[2] = mAudioRecord.getChannelCount();
} else {
if (mAudioTrack == null) {
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
// Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid
// Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java
// Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState()
if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
/* Try again, with safer values */
Log.e(TAG, "Failed during initialization of Audio Track");
mAudioTrack.release();
mAudioTrack = null;
return null;
}
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */ && deviceId != 0) {
mAudioTrack.setPreferredDevice(getInputAudioDeviceInfo(deviceId));
}
mAudioTrack.play();
}
results[0] = mAudioTrack.getSampleRate();
results[1] = mAudioTrack.getAudioFormat();
results[2] = mAudioTrack.getChannelCount();
}
results[3] = desiredFrames;
Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", got " + results[3] + " frames of " + results[2] + " channel " + getAudioFormatString(results[1]) + " audio at " + results[0] + " Hz");
return results;
}
private static AudioDeviceInfo getInputAudioDeviceInfo(int deviceId) {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS))
.filter(deviceInfo -> deviceInfo.getId() == deviceId)
.findFirst()
.orElse(null);
} else {
return null;
}
}
private static AudioDeviceInfo getOutputAudioDeviceInfo(int deviceId) {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS))
.filter(deviceInfo -> deviceInfo.getId() == deviceId)
.findFirst()
.orElse(null);
} else {
return null;
}
}
private static void registerAudioDeviceCallback() {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
audioManager.registerAudioDeviceCallback(mAudioDeviceCallback, null);
}
}
private static void unregisterAudioDeviceCallback(Context context) {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
audioManager.unregisterAudioDeviceCallback(mAudioDeviceCallback);
}
}
/**
* This method is called by SDL using JNI.
*/
public static int[] getAudioOutputDevices() {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)).mapToInt(AudioDeviceInfo::getId).toArray();
} else {
return NO_DEVICES;
}
}
/**
* This method is called by SDL using JNI.
*/
public static int[] getAudioInputDevices() {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).mapToInt(AudioDeviceInfo::getId).toArray();
} else {
return NO_DEVICES;
}
}
/**
* This method is called by SDL using JNI.
*/
public static int[] audioOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
return open(false, sampleRate, audioFormat, desiredChannels, desiredFrames, deviceId);
}
/**
* This method is called by SDL using JNI.
*/
public static void audioWriteFloatBuffer(float[] buffer) {
if (mAudioTrack == null) {
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
return;
}
if (android.os.Build.VERSION.SDK_INT < 21 /* Android 5.0 (LOLLIPOP) */) {
Log.e(TAG, "Attempted to make an incompatible audio call with uninitialized audio! (floating-point output is supported since Android 5.0 Lollipop)");
return;
}
for (int i = 0; i < buffer.length;) {
int result = mAudioTrack.write(buffer, i, buffer.length - i, AudioTrack.WRITE_BLOCKING);
if (result > 0) {
i += result;
} else if (result == 0) {
try {
Thread.sleep(1);
} catch(InterruptedException e) {
// Nom nom
}
} else {
Log.w(TAG, "SDL audio: error return from write(float)");
return;
}
}
}
/**
* This method is called by SDL using JNI.
*/
public static void audioWriteShortBuffer(short[] buffer) {
if (mAudioTrack == null) {
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
return;
}
for (int i = 0; i < buffer.length;) {
int result = mAudioTrack.write(buffer, i, buffer.length - i);
if (result > 0) {
i += result;
} else if (result == 0) {
try {
Thread.sleep(1);
} catch(InterruptedException e) {
// Nom nom
}
} else {
Log.w(TAG, "SDL audio: error return from write(short)");
return;
}
}
}
/**
* This method is called by SDL using JNI.
*/
public static void audioWriteByteBuffer(byte[] buffer) {
if (mAudioTrack == null) {
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
return;
}
for (int i = 0; i < buffer.length; ) {
int result = mAudioTrack.write(buffer, i, buffer.length - i);
if (result > 0) {
i += result;
} else if (result == 0) {
try {
Thread.sleep(1);
} catch(InterruptedException e) {
// Nom nom
}
} else {
Log.w(TAG, "SDL audio: error return from write(byte)");
return;
}
}
}
/**
* This method is called by SDL using JNI.
*/
public static int[] captureOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
return open(true, sampleRate, audioFormat, desiredChannels, desiredFrames, deviceId);
}
/** This method is called by SDL using JNI. */
public static int captureReadFloatBuffer(float[] buffer, boolean blocking) {
if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) {
return 0;
} else {
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
}
}
/** This method is called by SDL using JNI. */
public static int captureReadShortBuffer(short[] buffer, boolean blocking) {
if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) {
return mAudioRecord.read(buffer, 0, buffer.length);
} else {
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
}
}
/** This method is called by SDL using JNI. */
public static int captureReadByteBuffer(byte[] buffer, boolean blocking) {
if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) {
return mAudioRecord.read(buffer, 0, buffer.length);
} else {
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
}
}
/** This method is called by SDL using JNI. */
public static void audioClose() {
if (mAudioTrack != null) {
mAudioTrack.stop();
mAudioTrack.release();
mAudioTrack = null;
}
}
/** This method is called by SDL using JNI. */
public static void captureClose() {
if (mAudioRecord != null) {
mAudioRecord.stop();
mAudioRecord.release();
mAudioRecord = null;
}
}
/** This method is called by SDL using JNI. */
public static void audioSetThreadPriority(boolean iscapture, int device_id) {
try {
/* Set thread name */
if (iscapture) {
Thread.currentThread().setName("SDLAudioC" + device_id);
} else {
Thread.currentThread().setName("SDLAudioP" + device_id);
}
/* Set thread priority */
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO);
} catch (Exception e) {
Log.v(TAG, "modify thread properties failed " + e.toString());
}
}
public static native int nativeSetupJNI();
public static native void removeAudioDevice(boolean isCapture, int deviceId);
public static native void addAudioDevice(boolean isCapture, int deviceId);
}

View File

@ -1,854 +0,0 @@
package org.libsdl.app;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import android.content.Context;
import android.os.Build;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.util.Log;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
public class SDLControllerManager
{
public static native int nativeSetupJNI();
public static native int nativeAddJoystick(int device_id, String name, String desc,
int vendor_id, int product_id,
boolean is_accelerometer, int button_mask,
int naxes, int axis_mask, int nhats, int nballs);
public static native int nativeRemoveJoystick(int device_id);
public static native int nativeAddHaptic(int device_id, String name);
public static native int nativeRemoveHaptic(int device_id);
public static native int onNativePadDown(int device_id, int keycode);
public static native int onNativePadUp(int device_id, int keycode);
public static native void onNativeJoy(int device_id, int axis,
float value);
public static native void onNativeHat(int device_id, int hat_id,
int x, int y);
protected static SDLJoystickHandler mJoystickHandler;
protected static SDLHapticHandler mHapticHandler;
private static final String TAG = "SDLControllerManager";
public static void initialize() {
if (mJoystickHandler == null) {
if (Build.VERSION.SDK_INT >= 19 /* Android 4.4 (KITKAT) */) {
mJoystickHandler = new SDLJoystickHandler_API19();
} else {
mJoystickHandler = new SDLJoystickHandler_API16();
}
}
if (mHapticHandler == null) {
if (Build.VERSION.SDK_INT >= 26 /* Android 8.0 (O) */) {
mHapticHandler = new SDLHapticHandler_API26();
} else {
mHapticHandler = new SDLHapticHandler();
}
}
}
// Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
public static boolean handleJoystickMotionEvent(MotionEvent event) {
return mJoystickHandler.handleMotionEvent(event);
}
/**
* This method is called by SDL using JNI.
*/
public static void pollInputDevices() {
mJoystickHandler.pollInputDevices();
}
/**
* This method is called by SDL using JNI.
*/
public static void pollHapticDevices() {
mHapticHandler.pollHapticDevices();
}
/**
* This method is called by SDL using JNI.
*/
public static void hapticRun(int device_id, float intensity, int length) {
mHapticHandler.run(device_id, intensity, length);
}
/**
* This method is called by SDL using JNI.
*/
public static void hapticStop(int device_id)
{
mHapticHandler.stop(device_id);
}
// Check if a given device is considered a possible SDL joystick
public static boolean isDeviceSDLJoystick(int deviceId) {
InputDevice device = InputDevice.getDevice(deviceId);
// We cannot use InputDevice.isVirtual before API 16, so let's accept
// only nonnegative device ids (VIRTUAL_KEYBOARD equals -1)
if ((device == null) || (deviceId < 0)) {
return false;
}
int sources = device.getSources();
/* This is called for every button press, so let's not spam the logs */
/*
if ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
Log.v(TAG, "Input device " + device.getName() + " has class joystick.");
}
if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) {
Log.v(TAG, "Input device " + device.getName() + " is a dpad.");
}
if ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
Log.v(TAG, "Input device " + device.getName() + " is a gamepad.");
}
*/
return ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) != 0 ||
((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) ||
((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
);
}
}
class SDLJoystickHandler {
/**
* Handles given MotionEvent.
* @param event the event to be handled.
* @return if given event was processed.
*/
public boolean handleMotionEvent(MotionEvent event) {
return false;
}
/**
* Handles adding and removing of input devices.
*/
public void pollInputDevices() {
}
}
/* Actual joystick functionality available for API >= 12 devices */
class SDLJoystickHandler_API16 extends SDLJoystickHandler {
static class SDLJoystick {
public int device_id;
public String name;
public String desc;
public ArrayList<InputDevice.MotionRange> axes;
public ArrayList<InputDevice.MotionRange> hats;
}
static class RangeComparator implements Comparator<InputDevice.MotionRange> {
@Override
public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) {
// Some controllers, like the Moga Pro 2, return AXIS_GAS (22) for right trigger and AXIS_BRAKE (23) for left trigger - swap them so they're sorted in the right order for SDL
int arg0Axis = arg0.getAxis();
int arg1Axis = arg1.getAxis();
if (arg0Axis == MotionEvent.AXIS_GAS) {
arg0Axis = MotionEvent.AXIS_BRAKE;
} else if (arg0Axis == MotionEvent.AXIS_BRAKE) {
arg0Axis = MotionEvent.AXIS_GAS;
}
if (arg1Axis == MotionEvent.AXIS_GAS) {
arg1Axis = MotionEvent.AXIS_BRAKE;
} else if (arg1Axis == MotionEvent.AXIS_BRAKE) {
arg1Axis = MotionEvent.AXIS_GAS;
}
// Make sure the AXIS_Z is sorted between AXIS_RY and AXIS_RZ.
// This is because the usual pairing are:
// - AXIS_X + AXIS_Y (left stick).
// - AXIS_RX, AXIS_RY (sometimes the right stick, sometimes triggers).
// - AXIS_Z, AXIS_RZ (sometimes the right stick, sometimes triggers).
// This sorts the axes in the above order, which tends to be correct
// for Xbox-ish game pads that have the right stick on RX/RY and the
// triggers on Z/RZ.
//
// Gamepads that don't have AXIS_Z/AXIS_RZ but use
// AXIS_LTRIGGER/AXIS_RTRIGGER are unaffected by this.
//
// References:
// - https://developer.android.com/develop/ui/views/touch-and-input/game-controllers/controller-input
// - https://www.kernel.org/doc/html/latest/input/gamepad.html
if (arg0Axis == MotionEvent.AXIS_Z) {
arg0Axis = MotionEvent.AXIS_RZ - 1;
} else if (arg0Axis > MotionEvent.AXIS_Z && arg0Axis < MotionEvent.AXIS_RZ) {
--arg0Axis;
}
if (arg1Axis == MotionEvent.AXIS_Z) {
arg1Axis = MotionEvent.AXIS_RZ - 1;
} else if (arg1Axis > MotionEvent.AXIS_Z && arg1Axis < MotionEvent.AXIS_RZ) {
--arg1Axis;
}
return arg0Axis - arg1Axis;
}
}
private final ArrayList<SDLJoystick> mJoysticks;
public SDLJoystickHandler_API16() {
mJoysticks = new ArrayList<SDLJoystick>();
}
@Override
public void pollInputDevices() {
int[] deviceIds = InputDevice.getDeviceIds();
for (int device_id : deviceIds) {
if (SDLControllerManager.isDeviceSDLJoystick(device_id)) {
SDLJoystick joystick = getJoystick(device_id);
if (joystick == null) {
InputDevice joystickDevice = InputDevice.getDevice(device_id);
joystick = new SDLJoystick();
joystick.device_id = device_id;
joystick.name = joystickDevice.getName();
joystick.desc = getJoystickDescriptor(joystickDevice);
joystick.axes = new ArrayList<InputDevice.MotionRange>();
joystick.hats = new ArrayList<InputDevice.MotionRange>();
List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
Collections.sort(ranges, new RangeComparator());
for (InputDevice.MotionRange range : ranges) {
if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) {
joystick.hats.add(range);
} else {
joystick.axes.add(range);
}
}
}
mJoysticks.add(joystick);
SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc,
getVendorId(joystickDevice), getProductId(joystickDevice), false,
getButtonMask(joystickDevice), joystick.axes.size(), getAxisMask(joystick.axes), joystick.hats.size()/2, 0);
}
}
}
/* Check removed devices */
ArrayList<Integer> removedDevices = null;
for (SDLJoystick joystick : mJoysticks) {
int device_id = joystick.device_id;
int i;
for (i = 0; i < deviceIds.length; i++) {
if (device_id == deviceIds[i]) break;
}
if (i == deviceIds.length) {
if (removedDevices == null) {
removedDevices = new ArrayList<Integer>();
}
removedDevices.add(device_id);
}
}
if (removedDevices != null) {
for (int device_id : removedDevices) {
SDLControllerManager.nativeRemoveJoystick(device_id);
for (int i = 0; i < mJoysticks.size(); i++) {
if (mJoysticks.get(i).device_id == device_id) {
mJoysticks.remove(i);
break;
}
}
}
}
}
protected SDLJoystick getJoystick(int device_id) {
for (SDLJoystick joystick : mJoysticks) {
if (joystick.device_id == device_id) {
return joystick;
}
}
return null;
}
@Override
public boolean handleMotionEvent(MotionEvent event) {
int actionPointerIndex = event.getActionIndex();
int action = event.getActionMasked();
if (action == MotionEvent.ACTION_MOVE) {
SDLJoystick joystick = getJoystick(event.getDeviceId());
if (joystick != null) {
for (int i = 0; i < joystick.axes.size(); i++) {
InputDevice.MotionRange range = joystick.axes.get(i);
/* Normalize the value to -1...1 */
float value = (event.getAxisValue(range.getAxis(), actionPointerIndex) - range.getMin()) / range.getRange() * 2.0f - 1.0f;
SDLControllerManager.onNativeJoy(joystick.device_id, i, value);
}
for (int i = 0; i < joystick.hats.size() / 2; i++) {
int hatX = Math.round(event.getAxisValue(joystick.hats.get(2 * i).getAxis(), actionPointerIndex));
int hatY = Math.round(event.getAxisValue(joystick.hats.get(2 * i + 1).getAxis(), actionPointerIndex));
SDLControllerManager.onNativeHat(joystick.device_id, i, hatX, hatY);
}
}
}
return true;
}
public String getJoystickDescriptor(InputDevice joystickDevice) {
String desc = joystickDevice.getDescriptor();
if (desc != null && !desc.isEmpty()) {
return desc;
}
return joystickDevice.getName();
}
public int getProductId(InputDevice joystickDevice) {
return 0;
}
public int getVendorId(InputDevice joystickDevice) {
return 0;
}
public int getAxisMask(List<InputDevice.MotionRange> ranges) {
return -1;
}
public int getButtonMask(InputDevice joystickDevice) {
return -1;
}
}
class SDLJoystickHandler_API19 extends SDLJoystickHandler_API16 {
@Override
public int getProductId(InputDevice joystickDevice) {
return joystickDevice.getProductId();
}
@Override
public int getVendorId(InputDevice joystickDevice) {
return joystickDevice.getVendorId();
}
@Override
public int getAxisMask(List<InputDevice.MotionRange> ranges) {
// For compatibility, keep computing the axis mask like before,
// only really distinguishing 2, 4 and 6 axes.
int axis_mask = 0;
if (ranges.size() >= 2) {
// ((1 << SDL_GAMEPAD_AXIS_LEFTX) | (1 << SDL_GAMEPAD_AXIS_LEFTY))
axis_mask |= 0x0003;
}
if (ranges.size() >= 4) {
// ((1 << SDL_GAMEPAD_AXIS_RIGHTX) | (1 << SDL_GAMEPAD_AXIS_RIGHTY))
axis_mask |= 0x000c;
}
if (ranges.size() >= 6) {
// ((1 << SDL_GAMEPAD_AXIS_LEFT_TRIGGER) | (1 << SDL_GAMEPAD_AXIS_RIGHT_TRIGGER))
axis_mask |= 0x0030;
}
// Also add an indicator bit for whether the sorting order has changed.
// This serves to disable outdated gamecontrollerdb.txt mappings.
boolean have_z = false;
boolean have_past_z_before_rz = false;
for (InputDevice.MotionRange range : ranges) {
int axis = range.getAxis();
if (axis == MotionEvent.AXIS_Z) {
have_z = true;
} else if (axis > MotionEvent.AXIS_Z && axis < MotionEvent.AXIS_RZ) {
have_past_z_before_rz = true;
}
}
if (have_z && have_past_z_before_rz) {
// If both these exist, the compare() function changed sorting order.
// Set a bit to indicate this fact.
axis_mask |= 0x8000;
}
return axis_mask;
}
@Override
public int getButtonMask(InputDevice joystickDevice) {
int button_mask = 0;
int[] keys = new int[] {
KeyEvent.KEYCODE_BUTTON_A,
KeyEvent.KEYCODE_BUTTON_B,
KeyEvent.KEYCODE_BUTTON_X,
KeyEvent.KEYCODE_BUTTON_Y,
KeyEvent.KEYCODE_BACK,
KeyEvent.KEYCODE_MENU,
KeyEvent.KEYCODE_BUTTON_MODE,
KeyEvent.KEYCODE_BUTTON_START,
KeyEvent.KEYCODE_BUTTON_THUMBL,
KeyEvent.KEYCODE_BUTTON_THUMBR,
KeyEvent.KEYCODE_BUTTON_L1,
KeyEvent.KEYCODE_BUTTON_R1,
KeyEvent.KEYCODE_DPAD_UP,
KeyEvent.KEYCODE_DPAD_DOWN,
KeyEvent.KEYCODE_DPAD_LEFT,
KeyEvent.KEYCODE_DPAD_RIGHT,
KeyEvent.KEYCODE_BUTTON_SELECT,
KeyEvent.KEYCODE_DPAD_CENTER,
// These don't map into any SDL controller buttons directly
KeyEvent.KEYCODE_BUTTON_L2,
KeyEvent.KEYCODE_BUTTON_R2,
KeyEvent.KEYCODE_BUTTON_C,
KeyEvent.KEYCODE_BUTTON_Z,
KeyEvent.KEYCODE_BUTTON_1,
KeyEvent.KEYCODE_BUTTON_2,
KeyEvent.KEYCODE_BUTTON_3,
KeyEvent.KEYCODE_BUTTON_4,
KeyEvent.KEYCODE_BUTTON_5,
KeyEvent.KEYCODE_BUTTON_6,
KeyEvent.KEYCODE_BUTTON_7,
KeyEvent.KEYCODE_BUTTON_8,
KeyEvent.KEYCODE_BUTTON_9,
KeyEvent.KEYCODE_BUTTON_10,
KeyEvent.KEYCODE_BUTTON_11,
KeyEvent.KEYCODE_BUTTON_12,
KeyEvent.KEYCODE_BUTTON_13,
KeyEvent.KEYCODE_BUTTON_14,
KeyEvent.KEYCODE_BUTTON_15,
KeyEvent.KEYCODE_BUTTON_16,
};
int[] masks = new int[] {
(1 << 0), // A -> A
(1 << 1), // B -> B
(1 << 2), // X -> X
(1 << 3), // Y -> Y
(1 << 4), // BACK -> BACK
(1 << 6), // MENU -> START
(1 << 5), // MODE -> GUIDE
(1 << 6), // START -> START
(1 << 7), // THUMBL -> LEFTSTICK
(1 << 8), // THUMBR -> RIGHTSTICK
(1 << 9), // L1 -> LEFTSHOULDER
(1 << 10), // R1 -> RIGHTSHOULDER
(1 << 11), // DPAD_UP -> DPAD_UP
(1 << 12), // DPAD_DOWN -> DPAD_DOWN
(1 << 13), // DPAD_LEFT -> DPAD_LEFT
(1 << 14), // DPAD_RIGHT -> DPAD_RIGHT
(1 << 4), // SELECT -> BACK
(1 << 0), // DPAD_CENTER -> A
(1 << 15), // L2 -> ??
(1 << 16), // R2 -> ??
(1 << 17), // C -> ??
(1 << 18), // Z -> ??
(1 << 20), // 1 -> ??
(1 << 21), // 2 -> ??
(1 << 22), // 3 -> ??
(1 << 23), // 4 -> ??
(1 << 24), // 5 -> ??
(1 << 25), // 6 -> ??
(1 << 26), // 7 -> ??
(1 << 27), // 8 -> ??
(1 << 28), // 9 -> ??
(1 << 29), // 10 -> ??
(1 << 30), // 11 -> ??
(1 << 31), // 12 -> ??
// We're out of room...
0xFFFFFFFF, // 13 -> ??
0xFFFFFFFF, // 14 -> ??
0xFFFFFFFF, // 15 -> ??
0xFFFFFFFF, // 16 -> ??
};
boolean[] has_keys = joystickDevice.hasKeys(keys);
for (int i = 0; i < keys.length; ++i) {
if (has_keys[i]) {
button_mask |= masks[i];
}
}
return button_mask;
}
}
class SDLHapticHandler_API26 extends SDLHapticHandler {
@Override
public void run(int device_id, float intensity, int length) {
SDLHaptic haptic = getHaptic(device_id);
if (haptic != null) {
Log.d("SDL", "Rtest: Vibe with intensity " + intensity + " for " + length);
if (intensity == 0.0f) {
stop(device_id);
return;
}
int vibeValue = Math.round(intensity * 255);
if (vibeValue > 255) {
vibeValue = 255;
}
if (vibeValue < 1) {
stop(device_id);
return;
}
try {
haptic.vib.vibrate(VibrationEffect.createOneShot(length, vibeValue));
}
catch (Exception e) {
// Fall back to the generic method, which uses DEFAULT_AMPLITUDE, but works even if
// something went horribly wrong with the Android 8.0 APIs.
haptic.vib.vibrate(length);
}
}
}
}
class SDLHapticHandler {
static class SDLHaptic {
public int device_id;
public String name;
public Vibrator vib;
}
private final ArrayList<SDLHaptic> mHaptics;
public SDLHapticHandler() {
mHaptics = new ArrayList<SDLHaptic>();
}
public void run(int device_id, float intensity, int length) {
SDLHaptic haptic = getHaptic(device_id);
if (haptic != null) {
haptic.vib.vibrate(length);
}
}
public void stop(int device_id) {
SDLHaptic haptic = getHaptic(device_id);
if (haptic != null) {
haptic.vib.cancel();
}
}
public void pollHapticDevices() {
final int deviceId_VIBRATOR_SERVICE = 999999;
boolean hasVibratorService = false;
int[] deviceIds = InputDevice.getDeviceIds();
// It helps processing the device ids in reverse order
// For example, in the case of the XBox 360 wireless dongle,
// so the first controller seen by SDL matches what the receiver
// considers to be the first controller
for (int i = deviceIds.length - 1; i > -1; i--) {
SDLHaptic haptic = getHaptic(deviceIds[i]);
if (haptic == null) {
InputDevice device = InputDevice.getDevice(deviceIds[i]);
Vibrator vib = device.getVibrator();
if (vib.hasVibrator()) {
haptic = new SDLHaptic();
haptic.device_id = deviceIds[i];
haptic.name = device.getName();
haptic.vib = vib;
mHaptics.add(haptic);
SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
}
}
}
/* Check VIBRATOR_SERVICE */
Vibrator vib = (Vibrator) SDL.getContext().getSystemService(Context.VIBRATOR_SERVICE);
if (vib != null) {
hasVibratorService = vib.hasVibrator();
if (hasVibratorService) {
SDLHaptic haptic = getHaptic(deviceId_VIBRATOR_SERVICE);
if (haptic == null) {
haptic = new SDLHaptic();
haptic.device_id = deviceId_VIBRATOR_SERVICE;
haptic.name = "VIBRATOR_SERVICE";
haptic.vib = vib;
mHaptics.add(haptic);
SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
}
}
}
/* Check removed devices */
ArrayList<Integer> removedDevices = null;
for (SDLHaptic haptic : mHaptics) {
int device_id = haptic.device_id;
int i;
for (i = 0; i < deviceIds.length; i++) {
if (device_id == deviceIds[i]) break;
}
if (device_id != deviceId_VIBRATOR_SERVICE || !hasVibratorService) {
if (i == deviceIds.length) {
if (removedDevices == null) {
removedDevices = new ArrayList<Integer>();
}
removedDevices.add(device_id);
}
} // else: don't remove the vibrator if it is still present
}
if (removedDevices != null) {
for (int device_id : removedDevices) {
SDLControllerManager.nativeRemoveHaptic(device_id);
for (int i = 0; i < mHaptics.size(); i++) {
if (mHaptics.get(i).device_id == device_id) {
mHaptics.remove(i);
break;
}
}
}
}
}
protected SDLHaptic getHaptic(int device_id) {
for (SDLHaptic haptic : mHaptics) {
if (haptic.device_id == device_id) {
return haptic;
}
}
return null;
}
}
class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
// Generic Motion (mouse hover, joystick...) events go here
@Override
public boolean onGenericMotion(View v, MotionEvent event) {
float x, y;
int action;
switch ( event.getSource() ) {
case InputDevice.SOURCE_JOYSTICK:
return SDLControllerManager.handleJoystickMotionEvent(event);
case InputDevice.SOURCE_MOUSE:
action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_SCROLL:
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
case MotionEvent.ACTION_HOVER_MOVE:
x = event.getX(0);
y = event.getY(0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
default:
break;
}
break;
default:
break;
}
// Event was not managed
return false;
}
public boolean supportsRelativeMouse() {
return false;
}
public boolean inRelativeMode() {
return false;
}
public boolean setRelativeMouseEnabled(boolean enabled) {
return false;
}
public void reclaimRelativeMouseModeIfNeeded()
{
}
public float getEventX(MotionEvent event) {
return event.getX(0);
}
public float getEventY(MotionEvent event) {
return event.getY(0);
}
}
class SDLGenericMotionListener_API24 extends SDLGenericMotionListener_API12 {
// Generic Motion (mouse hover, joystick...) events go here
private boolean mRelativeModeEnabled;
@Override
public boolean onGenericMotion(View v, MotionEvent event) {
// Handle relative mouse mode
if (mRelativeModeEnabled) {
if (event.getSource() == InputDevice.SOURCE_MOUSE) {
int action = event.getActionMasked();
if (action == MotionEvent.ACTION_HOVER_MOVE) {
float x = event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
float y = event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
SDLActivity.onNativeMouse(0, action, x, y, true);
return true;
}
}
}
// Event was not managed, call SDLGenericMotionListener_API12 method
return super.onGenericMotion(v, event);
}
@Override
public boolean supportsRelativeMouse() {
return true;
}
@Override
public boolean inRelativeMode() {
return mRelativeModeEnabled;
}
@Override
public boolean setRelativeMouseEnabled(boolean enabled) {
mRelativeModeEnabled = enabled;
return true;
}
@Override
public float getEventX(MotionEvent event) {
if (mRelativeModeEnabled) {
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
} else {
return event.getX(0);
}
}
@Override
public float getEventY(MotionEvent event) {
if (mRelativeModeEnabled) {
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
} else {
return event.getY(0);
}
}
}
class SDLGenericMotionListener_API26 extends SDLGenericMotionListener_API24 {
// Generic Motion (mouse hover, joystick...) events go here
private boolean mRelativeModeEnabled;
@Override
public boolean onGenericMotion(View v, MotionEvent event) {
float x, y;
int action;
switch ( event.getSource() ) {
case InputDevice.SOURCE_JOYSTICK:
return SDLControllerManager.handleJoystickMotionEvent(event);
case InputDevice.SOURCE_MOUSE:
// DeX desktop mouse cursor is a separate non-standard input type.
case InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN:
action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_SCROLL:
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
case MotionEvent.ACTION_HOVER_MOVE:
x = event.getX(0);
y = event.getY(0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
default:
break;
}
break;
case InputDevice.SOURCE_MOUSE_RELATIVE:
action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_SCROLL:
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
case MotionEvent.ACTION_HOVER_MOVE:
x = event.getX(0);
y = event.getY(0);
SDLActivity.onNativeMouse(0, action, x, y, true);
return true;
default:
break;
}
break;
default:
break;
}
// Event was not managed
return false;
}
@Override
public boolean supportsRelativeMouse() {
return (!SDLActivity.isDeXMode() || Build.VERSION.SDK_INT >= 27 /* Android 8.1 (O_MR1) */);
}
@Override
public boolean inRelativeMode() {
return mRelativeModeEnabled;
}
@Override
public boolean setRelativeMouseEnabled(boolean enabled) {
if (!SDLActivity.isDeXMode() || Build.VERSION.SDK_INT >= 27 /* Android 8.1 (O_MR1) */) {
if (enabled) {
SDLActivity.getContentView().requestPointerCapture();
} else {
SDLActivity.getContentView().releasePointerCapture();
}
mRelativeModeEnabled = enabled;
return true;
} else {
return false;
}
}
@Override
public void reclaimRelativeMouseModeIfNeeded()
{
if (mRelativeModeEnabled && !SDLActivity.isDeXMode()) {
SDLActivity.getContentView().requestPointerCapture();
}
}
@Override
public float getEventX(MotionEvent event) {
// Relative mouse in capture mode will only have relative for X/Y
return event.getX(0);
}
@Override
public float getEventY(MotionEvent event) {
// Relative mouse in capture mode will only have relative for X/Y
return event.getY(0);
}
}

View File

@ -1,405 +0,0 @@
package org.libsdl.app;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Build;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
/**
SDLSurface. This is what we draw on, so we need to know when it's created
in order to do anything useful.
Because of this, that's where we set up the SDL thread
*/
public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
View.OnKeyListener, View.OnTouchListener, SensorEventListener {
// Sensors
protected SensorManager mSensorManager;
protected Display mDisplay;
// Keep track of the surface size to normalize touch events
protected float mWidth, mHeight;
// Is SurfaceView ready for rendering
public boolean mIsSurfaceReady;
// Startup
public SDLSurface(Context context) {
super(context);
getHolder().addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
setOnKeyListener(this);
setOnTouchListener(this);
mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
setOnGenericMotionListener(SDLActivity.getMotionListener());
// Some arbitrary defaults to avoid a potential division by zero
mWidth = 1.0f;
mHeight = 1.0f;
mIsSurfaceReady = false;
}
public void handlePause() {
enableSensor(Sensor.TYPE_ACCELEROMETER, false);
}
public void handleResume() {
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
setOnKeyListener(this);
setOnTouchListener(this);
enableSensor(Sensor.TYPE_ACCELEROMETER, true);
}
public Surface getNativeSurface() {
return getHolder().getSurface();
}
// Called when we have a valid drawing surface
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.v("SDL", "surfaceCreated()");
SDLActivity.onNativeSurfaceCreated();
}
// Called when we lose the surface
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.v("SDL", "surfaceDestroyed()");
// Transition to pause, if needed
SDLActivity.mNextNativeState = SDLActivity.NativeState.PAUSED;
SDLActivity.handleNativeState();
mIsSurfaceReady = false;
SDLActivity.onNativeSurfaceDestroyed();
}
// Called when the surface is resized
@Override
public void surfaceChanged(SurfaceHolder holder,
int format, int width, int height) {
Log.v("SDL", "surfaceChanged()");
if (SDLActivity.mSingleton == null) {
return;
}
mWidth = width;
mHeight = height;
int nDeviceWidth = width;
int nDeviceHeight = height;
try
{
if (Build.VERSION.SDK_INT >= 17 /* Android 4.2 (JELLY_BEAN_MR1) */) {
DisplayMetrics realMetrics = new DisplayMetrics();
mDisplay.getRealMetrics( realMetrics );
nDeviceWidth = realMetrics.widthPixels;
nDeviceHeight = realMetrics.heightPixels;
}
} catch(Exception ignored) {
}
synchronized(SDLActivity.getContext()) {
// In case we're waiting on a size change after going fullscreen, send a notification.
SDLActivity.getContext().notifyAll();
}
Log.v("SDL", "Window size: " + width + "x" + height);
Log.v("SDL", "Device size: " + nDeviceWidth + "x" + nDeviceHeight);
SDLActivity.nativeSetScreenResolution(width, height, nDeviceWidth, nDeviceHeight, mDisplay.getRefreshRate());
SDLActivity.onNativeResize();
// Prevent a screen distortion glitch,
// for instance when the device is in Landscape and a Portrait App is resumed.
boolean skip = false;
int requestedOrientation = SDLActivity.mSingleton.getRequestedOrientation();
if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
if (mWidth > mHeight) {
skip = true;
}
} else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
if (mWidth < mHeight) {
skip = true;
}
}
// Special Patch for Square Resolution: Black Berry Passport
if (skip) {
double min = Math.min(mWidth, mHeight);
double max = Math.max(mWidth, mHeight);
if (max / min < 1.20) {
Log.v("SDL", "Don't skip on such aspect-ratio. Could be a square resolution.");
skip = false;
}
}
// Don't skip in MultiWindow.
if (skip) {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
if (SDLActivity.mSingleton.isInMultiWindowMode()) {
Log.v("SDL", "Don't skip in Multi-Window");
skip = false;
}
}
}
if (skip) {
Log.v("SDL", "Skip .. Surface is not ready.");
mIsSurfaceReady = false;
return;
}
/* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
SDLActivity.onNativeSurfaceChanged();
/* Surface is ready */
mIsSurfaceReady = true;
SDLActivity.mNextNativeState = SDLActivity.NativeState.RESUMED;
SDLActivity.handleNativeState();
}
// Key events
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
return SDLActivity.handleKeyEvent(v, keyCode, event, null);
}
// Touch events
@Override
public boolean onTouch(View v, MotionEvent event) {
/* Ref: http://developer.android.com/training/gestures/multi.html */
int touchDevId = event.getDeviceId();
final int pointerCount = event.getPointerCount();
int action = event.getActionMasked();
int pointerFingerId;
int i = -1;
float x,y,p;
/*
* Prevent id to be -1, since it's used in SDL internal for synthetic events
* Appears when using Android emulator, eg:
* adb shell input mouse tap 100 100
* adb shell input touchscreen tap 100 100
*/
if (touchDevId < 0) {
touchDevId -= 1;
}
// 12290 = Samsung DeX mode desktop mouse
// 12290 = 0x3002 = 0x2002 | 0x1002 = SOURCE_MOUSE | SOURCE_TOUCHSCREEN
// 0x2 = SOURCE_CLASS_POINTER
if (event.getSource() == InputDevice.SOURCE_MOUSE || event.getSource() == (InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN)) {
int mouseButton = 1;
try {
Object object = event.getClass().getMethod("getButtonState").invoke(event);
if (object != null) {
mouseButton = (Integer) object;
}
} catch(Exception ignored) {
}
// We need to check if we're in relative mouse mode and get the axis offset rather than the x/y values
// if we are. We'll leverage our existing mouse motion listener
SDLGenericMotionListener_API12 motionListener = SDLActivity.getMotionListener();
x = motionListener.getEventX(event);
y = motionListener.getEventY(event);
SDLActivity.onNativeMouse(mouseButton, action, x, y, motionListener.inRelativeMode());
} else {
switch(action) {
case MotionEvent.ACTION_MOVE:
for (i = 0; i < pointerCount; i++) {
pointerFingerId = event.getPointerId(i);
x = event.getX(i) / mWidth;
y = event.getY(i) / mHeight;
p = event.getPressure(i);
if (p > 1.0f) {
// may be larger than 1.0f on some devices
// see the documentation of getPressure(i)
p = 1.0f;
}
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_DOWN:
// Primary pointer up/down, the index is always zero
i = 0;
/* fallthrough */
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_POINTER_DOWN:
// Non primary pointer up/down
if (i == -1) {
i = event.getActionIndex();
}
pointerFingerId = event.getPointerId(i);
x = event.getX(i) / mWidth;
y = event.getY(i) / mHeight;
p = event.getPressure(i);
if (p > 1.0f) {
// may be larger than 1.0f on some devices
// see the documentation of getPressure(i)
p = 1.0f;
}
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
break;
case MotionEvent.ACTION_CANCEL:
for (i = 0; i < pointerCount; i++) {
pointerFingerId = event.getPointerId(i);
x = event.getX(i) / mWidth;
y = event.getY(i) / mHeight;
p = event.getPressure(i);
if (p > 1.0f) {
// may be larger than 1.0f on some devices
// see the documentation of getPressure(i)
p = 1.0f;
}
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p);
}
break;
default:
break;
}
}
return true;
}
// Sensor events
public void enableSensor(int sensortype, boolean enabled) {
// TODO: This uses getDefaultSensor - what if we have >1 accels?
if (enabled) {
mSensorManager.registerListener(this,
mSensorManager.getDefaultSensor(sensortype),
SensorManager.SENSOR_DELAY_GAME, null);
} else {
mSensorManager.unregisterListener(this,
mSensorManager.getDefaultSensor(sensortype));
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO
}
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
// Since we may have an orientation set, we won't receive onConfigurationChanged events.
// We thus should check here.
int newOrientation;
float x, y;
switch (mDisplay.getRotation()) {
case Surface.ROTATION_90:
x = -event.values[1];
y = event.values[0];
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE;
break;
case Surface.ROTATION_270:
x = event.values[1];
y = -event.values[0];
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE_FLIPPED;
break;
case Surface.ROTATION_180:
x = -event.values[0];
y = -event.values[1];
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT_FLIPPED;
break;
case Surface.ROTATION_0:
default:
x = event.values[0];
y = event.values[1];
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT;
break;
}
if (newOrientation != SDLActivity.mCurrentOrientation) {
SDLActivity.mCurrentOrientation = newOrientation;
SDLActivity.onNativeOrientationChanged(newOrientation);
}
SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH,
y / SensorManager.GRAVITY_EARTH,
event.values[2] / SensorManager.GRAVITY_EARTH);
}
}
// Captured pointer events for API 26.
public boolean onCapturedPointerEvent(MotionEvent event)
{
int action = event.getActionMasked();
float x, y;
switch (action) {
case MotionEvent.ACTION_SCROLL:
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
case MotionEvent.ACTION_HOVER_MOVE:
case MotionEvent.ACTION_MOVE:
x = event.getX(0);
y = event.getY(0);
SDLActivity.onNativeMouse(0, action, x, y, true);
return true;
case MotionEvent.ACTION_BUTTON_PRESS:
case MotionEvent.ACTION_BUTTON_RELEASE:
// Change our action value to what SDL's code expects.
if (action == MotionEvent.ACTION_BUTTON_PRESS) {
action = MotionEvent.ACTION_DOWN;
} else { /* MotionEvent.ACTION_BUTTON_RELEASE */
action = MotionEvent.ACTION_UP;
}
x = event.getX(0);
y = event.getY(0);
int button = event.getButtonState();
SDLActivity.onNativeMouse(button, action, x, y, true);
return true;
}
return false;
}
}

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_mediumAnimTime"
android:fromYDelta="100%p"
android:toYDelta="0%p" />

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_mediumAnimTime"
android:fromYDelta="-100%p"
android:toYDelta="0%p" />

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_mediumAnimTime"
android:fromYDelta="0%p"
android:toYDelta="100%p" />

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_mediumAnimTime"
android:fromYDelta="0%p"
android:toYDelta="-100%p" />

Binary file not shown.

Before

Width:  |  Height:  |  Size: 473 B

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/green_pressed" android:state_pressed="true" />
<item android:drawable="@drawable/green" />
</selector>

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/red_pressed" android:state_pressed="true" />
<item android:drawable="@drawable/red" />
</selector>

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/yellow_pressed" android:state_pressed="true" />
<item android:drawable="@drawable/yellow" />
</selector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 383 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 383 B

View File

@ -1,11 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="36dp"
android:height="36dp"
android:autoMirrored="true"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@color/green_mc"
android:pathData="M3.4,20.4l17.45,-7.48c0.81,-0.35 0.81,-1.49 0,-1.84L3.4,3.6c-0.66,-0.29 -1.39,0.2 -1.39,0.91L2,9.12c0,0.5 0.37,0.93 0.87,0.99L17,12 2.87,13.88c-0.5,0.07 -0.87,0.5 -0.87,1l0.01,4.61c0,0.71 0.73,1.2 1.39,0.91z" />
</vector>

View File

@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item
android:drawable="@drawable/loading_anim1"
android:duration="125" />
<item
android:drawable="@drawable/loading_anim2"
android:duration="125" />
<item
android:drawable="@drawable/loading_anim3"
android:duration="125" />
<item
android:drawable="@drawable/loading_anim4"
android:duration="125" />
<item
android:drawable="@drawable/loading_anim5"
android:duration="125" />
<item
android:drawable="@drawable/loading_anim6"
android:duration="125" />
<item
android:drawable="@drawable/loading_anim7"
android:duration="125" />
<item
android:drawable="@drawable/loading_anim8"
android:duration="125" />
</animation-list>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1021 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1023 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1015 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1016 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1021 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 374 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 375 B

View File

@ -1,22 +0,0 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/loading_anim"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/loading" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/preparing"
android:textColor="@color/light"
android:textSize="16sp" />
</LinearLayout>

View File

@ -1,78 +0,0 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="false"
android:gravity="center"
android:weightSum="1">
<LinearLayout
android:id="@+id/conn_root"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.7"
android:background="@drawable/bg_common"
android:orientation="vertical"
android:padding="16dp"
tools:ignore="UselessParent">
<androidx.appcompat.widget.AppCompatTextView
style="?android:attr/textAppearanceLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:drawableStart="@mipmap/ic_dialog"
android:drawablePadding="8dp"
android:gravity="center"
android:text="@string/conn_title"
android:textColor="@color/grey_900" />
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="8dp"
android:text="@string/conn_message"
android:textColor="@color/grey_900"
android:textSize="16sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/ignore"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_margin="8dp"
android:layout_weight="0.33"
android:background="@drawable/btn_yellow"
android:text="@string/ignore"
android:textAllCaps="false"
android:textColor="@android:color/white" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/mobile"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_margin="8dp"
android:layout_weight="0.33"
android:background="@drawable/btn_green"
android:text="@string/conn_mobile"
android:textAllCaps="false"
android:textColor="@android:color/white"
android:visibility="gone" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/wifi"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_margin="8dp"
android:layout_weight="0.33"
android:background="@drawable/btn_green"
android:text="@string/conn_wifi"
android:textAllCaps="false"
android:textColor="@android:color/white" />
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -1,26 +0,0 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/rl"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/input"
style="@style/TextInputLayoutStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="@drawable/bg_input"
android:hint="@string/input_text"
android:padding="4dp"
app:endIconDrawable="@drawable/ic_baseline_send"
app:endIconMode="custom"
app:endIconTint="@null"
app:hintTextColor="@color/green_mc">
<com.multicraft.game.CustomEditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="60dp" />
</com.google.android.material.textfield.TextInputLayout>
</RelativeLayout>

View File

@ -1,28 +0,0 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/multiRl"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/multiInput"
style="@style/TextInputLayoutStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="@drawable/bg_input"
android:hint="@string/input_text"
android:padding="4dp"
app:endIconDrawable="@drawable/ic_baseline_send"
app:endIconMode="custom"
app:endIconTint="@null"
app:hintTextColor="@color/green_mc">
<com.multicraft.game.CustomEditText
android:id="@+id/multiEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textMultiLine"
android:maxLines="8" />
</com.google.android.material.textfield.TextInputLayout>
</RelativeLayout>

View File

@ -1,86 +0,0 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="false"
android:gravity="center"
android:weightSum="1">
<LinearLayout
android:id="@+id/restart_root"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.7"
android:background="@drawable/bg_common"
android:orientation="vertical"
android:padding="16dp"
tools:ignore="UselessParent">
<androidx.appcompat.widget.AppCompatTextView
style="?android:attr/textAppearanceLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:drawableStart="@mipmap/ic_dialog"
android:drawablePadding="8dp"
android:gravity="center"
android:text="@string/sorry"
android:textColor="@color/grey_900" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/restart"
android:paddingStart="12dp"
android:paddingTop="4dp"
android:paddingEnd="12dp"
android:paddingBottom="4dp"
app:srcCompat="@drawable/sad" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/error_desc"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:minHeight="128dp"
android:padding="4dp"
android:text="@string/restart"
android:textColor="@color/grey_900"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/close"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_margin="8dp"
android:layout_weight="0.5"
android:background="@drawable/btn_red"
android:text="@string/close_game"
android:textAllCaps="false"
android:textColor="@android:color/white" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/restart"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_margin="8dp"
android:layout_weight="0.5"
android:background="@drawable/btn_green"
android:text="@string/restart_game"
android:textAllCaps="false"
android:textColor="@android:color/white" />
</LinearLayout>
</LinearLayout>
</LinearLayout>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="CustomDialog" parent="Theme.MaterialComponents.Light.NoActionBar">
<item name="windowActionBar">false</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowFullscreen">true</item>
<item name="android:backgroundDimEnabled">true</item>
<item name="android:windowLayoutInDisplayCutoutMode" tools:targetApi="p">shortEdges</item>
<item name="fontFamily">@font/multicraftfont</item>
</style>
</resources>

View File

@ -1,6 +0,0 @@
<resources>
<color name="light">#FAFAFA</color>
<color name="dark">#121212</color>
<color name="green_mc">#32783C</color>
<color name="grey_900">#212121</color>
</resources>

View File

@ -1,33 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<item name="windowActionBar">false</item>
<item name="android:windowBackground">@drawable/bg</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowLayoutInDisplayCutoutMode" tools:targetApi="p">shortEdges</item>
<item name="fontFamily">@font/multicraftfont</item>
</style>
<style name="CustomDialog" parent="Theme.MaterialComponents.Light.NoActionBar">
<item name="windowActionBar">false</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowFullscreen">true</item>
<item name="android:backgroundDimEnabled">true</item>
<item name="android:windowLayoutInDisplayCutoutMode" tools:targetApi="p">shortEdges</item>
<item name="fontFamily">@font/multicraftfont</item>
</style>
<style name="FullScreenDialogStyle" parent="Theme.MaterialComponents.Light.Dialog">
<item name="android:windowFullscreen">true</item>
<item name="android:windowIsFloating">false</item>
<item name="android:backgroundDimEnabled">false</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowEnterAnimation">@null</item>
</style>
<style name="TextInputLayoutStyle" parent="Widget.MaterialComponents.TextInputLayout.OutlinedBox">
<item name="boxStrokeColor">@color/green_mc</item>
</style>
</resources>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<data-extraction-rules>
<cloud-backup>
<exclude
domain="sharedpref"
path="MultiCraftSettings.xml" />
</cloud-backup>
</data-extraction-rules>

View File

@ -1,14 +0,0 @@
<#if isLowMemory>
org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
<#else>
org.gradle.jvmargs=-Xmx16G -XX:MaxMetaspaceSize=8G -XX:+HeapDumpOnOutOfMemoryError
</#if>
org.gradle.daemon=true
org.gradle.parallel=true
org.gradle.parallel.threads=8
org.gradle.configureondemand=true
android.enableJetifier=false
android.useAndroidX=true
android.defaults.buildfeatures.buildconfig=true
android.nonTransitiveRClass=false
android.nonFinalResIds=false

240
Android/gradlew vendored
View File

@ -1,240 +0,0 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

View File

@ -1,77 +0,0 @@
apply plugin: 'com.android.library'
apply plugin: 'de.undercouch.download'
android {
compileSdkVersion 34
buildToolsVersion '34.0.0'
ndkVersion '25.2.9519653'
defaultConfig {
minSdkVersion 21
targetSdkVersion 34
externalNativeBuild {
ndkBuild {
arguments '-j' + Runtime.getRuntime().availableProcessors(),
"--output-sync=none",
"versionMajor=${versionMajor}",
"versionMinor=${versionMinor}",
"versionPatch=${versionPatch}",
"versionExtra=${versionExtra}",
"developmentBuild=${developmentBuild}"
}
}
}
externalNativeBuild {
ndkBuild {
path file('jni/Android.mk')
}
}
// supported architectures
splits {
abi {
enable true
reset()
//noinspection ChromeOsAbiSupport
include 'armeabi-v7a', 'arm64-v8a', 'x86_64'
}
}
buildTypes {
release {
externalNativeBuild {
ndkBuild {
arguments 'NDEBUG=1'
}
}
}
}
namespace = "com.multicraft"
}
// get precompiled deps
def buildDirectory = layout.buildDirectory.get().asFile
tasks.register('downloadDeps', Download) {
def VERSION = "05102023"
src "https://github.com/MultiCraft/deps_android/releases/download/$VERSION/deps_android.zip"
dest new File(buildDirectory, 'deps.zip')
overwrite false
}
tasks.register('getDeps', Copy) {
dependsOn downloadDeps
def deps = file('deps')
def f = file("$buildDirectory/deps_android")
if (!f.exists()) {
from zipTree(downloadDeps.dest)
into deps
}
}
preBuild.dependsOn getDeps
android.defaultConfig.externalNativeBuild.ndkBuild {
arguments 'prebuilt=$(if $(strip $(wildcard $(prebuilt_path))),$(prebuilt_path),.)'
}

View File

@ -1,269 +0,0 @@
LOCAL_PATH := $(call my-dir)/..
#LOCAL_ADDRESS_SANITIZER:=true
include $(CLEAR_VARS)
LOCAL_MODULE := Curl
LOCAL_SRC_FILES := deps/libcurl/lib/$(APP_ABI)/libcurl.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := Gettext
LOCAL_SRC_FILES := deps/gettext/lib/$(APP_ABI)/libintl.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := Freetype
LOCAL_SRC_FILES := deps/freetype/lib/$(APP_ABI)/libfreetype.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := Irrlicht
LOCAL_SRC_FILES := deps/irrlicht/lib/$(APP_ABI)/libIrrlicht.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libpng
LOCAL_SRC_FILES := deps/libpng/lib/$(APP_ABI)/libpng.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libjpeg
LOCAL_SRC_FILES := deps/libjpeg/lib/$(APP_ABI)/libjpeg.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := SDL2
LOCAL_SRC_FILES := deps/sdl2/lib/$(APP_ABI)/libSDL2.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := LevelDB
LOCAL_SRC_FILES := deps/leveldb/lib/$(APP_ABI)/libleveldb.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := LuaJIT
LOCAL_SRC_FILES := deps/luajit/lib/$(APP_ABI)/libluajit.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := mbedTLS
LOCAL_SRC_FILES := deps/mbedtls/lib/$(APP_ABI)/libmbedtls.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := mbedx509
LOCAL_SRC_FILES := deps/mbedtls/lib/$(APP_ABI)/libmbedx509.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := mbedcrypto
LOCAL_SRC_FILES := deps/mbedtls/lib/$(APP_ABI)/libmbedcrypto.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := OpenAL
LOCAL_SRC_FILES := deps/openal/lib/$(APP_ABI)/libopenal.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := Vorbis
LOCAL_SRC_FILES := deps/vorbis/lib/$(APP_ABI)/libvorbis.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := MultiCraft
LOCAL_CFLAGS += \
-DJSONCPP_NO_LOCALE_SUPPORT \
-DHAVE_TOUCHSCREENGUI \
-DENABLE_GLES=1 \
-DUSE_CURL=1 \
-DUSE_SOUND=1 \
-DUSE_FREETYPE=1 \
-DUSE_LEVELDB=1 \
-DUSE_SQLITE=0 \
-DUSE_LUAJIT=1 \
-DUSE_GETTEXT=1 \
-DVERSION_MAJOR=${versionMajor} \
-DVERSION_MINOR=${versionMinor} \
-DVERSION_PATCH=${versionPatch} \
-DVERSION_EXTRA=${versionExtra} \
-DDEVELOPMENT_BUILD=${developmentBuild} \
$(GPROF_DEF)
ifdef NDEBUG
LOCAL_CFLAGS += -DNDEBUG=1
endif
ifdef GPROF
GPROF_DEF := -DGPROF
PROFILER_LIBS := android-ndk-profiler
LOCAL_CFLAGS += -pg
endif
LOCAL_C_INCLUDES := \
../../src \
../../src/script \
../../lib/gmp \
../../lib/jsoncpp \
deps/freetype/include \
deps/gettext/include \
deps/irrlicht/include \
deps/libpng/include \
deps/libjpeg/include \
deps/sdl2/include \
deps/leveldb/include \
deps/libcurl/include \
deps/luajit/include \
deps/openal/include \
deps/vorbis/include
LOCAL_SRC_FILES := \
$(wildcard ../../src/client/*.cpp) \
../../src/client/meshgen/collector.cpp \
../../src/client/render/core.cpp \
../../src/client/render/factory.cpp \
../../src/client/render/plain.cpp \
$(wildcard ../../src/content/*.cpp) \
../../src/database/database.cpp \
../../src/database/database-dummy.cpp \
../../src/database/database-files.cpp \
../../src/database/database-leveldb.cpp \
$(wildcard ../../src/gui/*.cpp) \
$(wildcard ../../src/irrlicht_changes/*.cpp) \
$(wildcard ../../src/mapgen/*.cpp) \
$(wildcard ../../src/network/*.cpp) \
$(wildcard ../../src/script/*.cpp) \
$(wildcard ../../src/script/common/*.cpp) \
$(wildcard ../../src/script/cpp_api/*.cpp) \
../../src/script/lua_api/l_areastore.cpp \
../../src/script/lua_api/l_auth.cpp \
../../src/script/lua_api/l_base.cpp \
../../src/script/lua_api/l_camera.cpp \
../../src/script/lua_api/l_craft.cpp \
../../src/script/lua_api/l_client.cpp \
../../src/script/lua_api/l_env.cpp \
../../src/script/lua_api/l_http.cpp \
../../src/script/lua_api/l_inventory.cpp \
../../src/script/lua_api/l_item.cpp \
../../src/script/lua_api/l_itemstackmeta.cpp \
../../src/script/lua_api/l_localplayer.cpp \
../../src/script/lua_api/l_mainmenu.cpp \
../../src/script/lua_api/l_mapgen.cpp \
../../src/script/lua_api/l_metadata.cpp \
../../src/script/lua_api/l_minimap.cpp \
../../src/script/lua_api/l_modchannels.cpp \
../../src/script/lua_api/l_nodemeta.cpp \
../../src/script/lua_api/l_nodetimer.cpp \
../../src/script/lua_api/l_noise.cpp \
../../src/script/lua_api/l_object.cpp \
../../src/script/lua_api/l_particles.cpp \
../../src/script/lua_api/l_particles_local.cpp \
../../src/script/lua_api/l_playermeta.cpp \
../../src/script/lua_api/l_server.cpp \
../../src/script/lua_api/l_settings.cpp \
../../src/script/lua_api/l_sound.cpp \
../../src/script/lua_api/l_storage.cpp \
../../src/script/lua_api/l_util.cpp \
../../src/script/lua_api/l_vmanip.cpp \
$(wildcard ../../src/server/*.cpp) \
../../src/threading/event.cpp \
../../src/threading/sdl_semaphore.cpp \
../../src/threading/sdl_thread.cpp \
$(wildcard ../../src/util/*.c) \
$(wildcard ../../src/util/*.cpp) \
../../src/ban.cpp \
../../src/chat.cpp \
../../src/clientiface.cpp \
../../src/collision.cpp \
../../src/content_mapnode.cpp \
../../src/content_nodemeta.cpp \
../../src/convert_json.cpp \
../../src/craftdef.cpp \
../../src/debug.cpp \
../../src/defaultsettings.cpp \
../../src/emerge.cpp \
../../src/environment.cpp \
../../src/face_position_cache.cpp \
../../src/filesys.cpp \
../../src/gettext.cpp \
../../src/httpfetch.cpp \
../../src/hud.cpp \
../../src/inventory.cpp \
../../src/inventorymanager.cpp \
../../src/itemdef.cpp \
../../src/itemstackmetadata.cpp \
../../src/light.cpp \
../../src/log.cpp \
../../src/main.cpp \
../../src/map.cpp \
../../src/map_settings_manager.cpp \
../../src/mapblock.cpp \
../../src/mapnode.cpp \
../../src/mapsector.cpp \
../../src/metadata.cpp \
../../src/modchannels.cpp \
../../src/nameidmapping.cpp \
../../src/nodedef.cpp \
../../src/nodemetadata.cpp \
../../src/nodetimer.cpp \
../../src/noise.cpp \
../../src/objdef.cpp \
../../src/object_properties.cpp \
../../src/particles.cpp \
../../src/pathfinder.cpp \
../../src/player.cpp \
../../src/porting.cpp \
../../src/porting_android.cpp \
../../src/profiler.cpp \
../../src/raycast.cpp \
../../src/reflowscan.cpp \
../../src/remoteplayer.cpp \
../../src/serialization.cpp \
../../src/server.cpp \
../../src/serverenvironment.cpp \
../../src/serverlist.cpp \
../../src/settings.cpp \
../../src/staticobject.cpp \
../../src/texture_override.cpp \
../../src/tileanimation.cpp \
../../src/tool.cpp \
../../src/translation.cpp \
../../src/version.cpp \
../../src/voxel.cpp \
../../src/voxelalgorithms.cpp
# GMP
LOCAL_SRC_FILES += ../../lib/gmp/mini-gmp.c
# JSONCPP
LOCAL_SRC_FILES += ../../lib/jsoncpp/jsoncpp.cpp
# Lua UTF-8 Lib
LOCAL_SRC_FILES += ../../lib/luautf8/lutf8lib.c
# Lua ChaCha Lib
LOCAL_SRC_FILES += $(wildcard ../../lib/luachacha/*.c)
LOCAL_STATIC_LIBRARIES += \
Curl mbedTLS mbedx509 mbedcrypto \
Freetype \
OpenAL \
Gettext \
Irrlicht libpng libjpeg SDL2 \
LevelDB \
Vorbis \
LuaJIT
LOCAL_STATIC_LIBRARIES += $(PROFILER_LIBS)
LOCAL_LDLIBS := -lEGL -lGLESv1_CM -lGLESv2 -landroid -lOpenSLES -lz -llog
include $(BUILD_SHARED_LIBRARY)
ifdef GPROF
$(call import-module,android-ndk-profiler)
endif

View File

@ -1,24 +0,0 @@
APP_PLATFORM := ${APP_PLATFORM}
APP_ABI := ${TARGET_ABI}
APP_STL := c++_static
APP_SHORT_COMMANDS := true
APP_MODULES := MultiCraft
ifdef NDEBUG
APP_CFLAGS := -Ofast -fvisibility=hidden -fvisibility-inlines-hidden -Wno-extra-tokens -D__FILE__=__FILE_NAME__ -Wno-builtin-macro-redefined
else
APP_CFLAGS := -g -D_DEBUG -O1 -fno-omit-frame-pointer
endif
APP_CFLAGS += -fexceptions
APP_CXXFLAGS := $(APP_CFLAGS) -frtti -std=gnu++17 #-Werror=shorten-64-to-32
# Silence Irrlicht warnings. Comment out with real debugging!
APP_CXXFLAGS += -Wno-deprecated-declarations -Wno-inconsistent-missing-override
APP_CPPFLAGS := $(APP_CXXFLAGS)
ifdef NDEBUG
APP_LDFLAGS := -Wl,--gc-sections,--icf=all
endif

View File

@ -1 +0,0 @@
<manifest />

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 817 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 653 KiB

View File

@ -1,13 +0,0 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
const char *get_secret_key(const char *key);
float get_screen_scale();
#ifdef __cplusplus
}
#endif

View File

@ -1,14 +0,0 @@
@import AppKit;
@import Foundation;
#import "wrapper.h"
const char *get_secret_key(const char *key)
{
return "dummy";
}
float get_screen_scale()
{
return [NSScreen mainScreen].backingScaleFactor;
}

View File

@ -1,36 +0,0 @@
#!/bin/bash -e
echo
echo "Starting build MultiCraft for macOS..."
echo
echo "Build libraries:"
sh scripts/SDL2.sh
sh scripts/libjpeg.sh
sh scripts/libpng.sh
sh scripts/irrlicht.sh
sh scripts/gettext.sh
sh scripts/freetype.sh
sh scripts/leveldb.sh
sh scripts/libogg.sh
sh scripts/libvorbis.sh
sh scripts/luajit.sh
sh scripts/openal.sh
echo
echo "All libraries were built!"
echo
echo "Preparing assets:"
sh scripts/assets.sh
echo
echo "Preparing locales:"
sh scripts/locale.sh
echo
echo "All done! You can continue in Xcode!"
open MultiCraft/MultiCraft.xcodeproj

View File

@ -1,41 +0,0 @@
#!/bin/bash -e
SDL2_VERSION=release-2.28.4
. scripts/sdk.sh
mkdir -p deps; cd deps
if [ ! -d SDL2-src ]; then
wget https://github.com/libsdl-org/SDL/archive/$SDL2_VERSION.tar.gz
tar -xzf $SDL2_VERSION.tar.gz
mv SDL-$SDL2_VERSION SDL2-src
rm $SDL2_VERSION.tar.gz
# Disable some features that are not needed
sed -i '' 's/#define SDL_AUDIO_DRIVER_COREAUDIO 1/#define SDL_AUDIO_DRIVER_COREAUDIO 0/g' SDL2-src/include/SDL_config_macosx.h
sed -i '' 's/#define SDL_AUDIO_DRIVER_DISK 1/#define SDL_AUDIO_DRIVER_DISK 0/g' SDL2-src/include/SDL_config_macosx.h
sed -i '' 's/#define SDL_AUDIO_DRIVER_DUMMY 1/#define SDL_AUDIO_DRIVER_DUMMY 0/g' SDL2-src/include/SDL_config_macosx.h
sed -i '' 's/#define SDL_PLATFORM_SUPPORTS_METAL 1/#define SDL_PLATFORM_SUPPORTS_METAL 0/g' SDL2-src/include/SDL_config_macosx.h
fi
rm -rf SDL2
cd SDL2-src
xcodebuild build \
ARCHS="$OSX_ARCHES" \
-project Xcode/SDL/SDL.xcodeproj \
-configuration Release \
-scheme "Static Library"
BUILD_FOLDER=$(xcodebuild \
-project Xcode/SDL/SDL.xcodeproj \
-configuration Release \
-scheme "Static Library" \
-showBuildSettings | \
grep TARGET_BUILD_DIR | sed -n -e 's/^.*TARGET_BUILD_DIR = //p')
mkdir -p ../SDL2
cp -v "${BUILD_FOLDER}/libSDL2.a" ../SDL2
cp -rv include ../SDL2
echo "SDL2 build successful"

View File

@ -1,19 +0,0 @@
#!/bin/bash -e
ALL_FONTS=false
if [ ! -d MultiCraft/MultiCraft.xcodeproj ]; then
echo "Run this from Apple folder"
exit 1
fi
DEST=$(pwd)/assets
mkdir -p $DEST/fonts
if $ALL_FONTS
then
cp ../fonts/*.ttf $DEST/fonts/
else
cp ../fonts/MultiCraftFont.ttf $DEST/fonts/
fi

View File

@ -1,38 +0,0 @@
#!/bin/bash -e
FREETYPE_VERSION=2.13.2
. scripts/sdk.sh
mkdir -p deps; cd deps
if [ ! -d freetype-src ]; then
wget http://download.savannah.gnu.org/releases/freetype/freetype-$FREETYPE_VERSION.tar.gz
tar -xzf freetype-$FREETYPE_VERSION.tar.gz
mv freetype-$FREETYPE_VERSION freetype-src
rm freetype-$FREETYPE_VERSION.tar.gz
mkdir freetype-src/build
fi
rm -rf freetype
cd freetype-src/build
cmake .. \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SHARED_LIBS=FALSE \
-DFT_DISABLE_BZIP2=TRUE \
-DFT_DISABLE_PNG=TRUE \
-DFT_DISABLE_HARFBUZZ=TRUE \
-DFT_DISABLE_BROTLI=TRUE \
-DCMAKE_C_FLAGS_RELEASE="$OSX_FLAGS $OSX_ARCH" \
-DCMAKE_OSX_ARCHITECTURES=$OSX_ARCHITECTURES
cmake --build . -j
mkdir -p ../../freetype
cp -v libfreetype.a ../../freetype
cp -rv ../include ../../freetype/include
rm -rf ../../freetype/include/dlg
echo "FreeType build successful"

View File

@ -1,31 +0,0 @@
#!/bin/bash -e
LEVELDB_VERSION=1.23
. scripts/sdk.sh
mkdir -p deps; cd deps
if [ ! -d leveldb-src ]; then
git clone -b $LEVELDB_VERSION --depth 1 https://github.com/google/leveldb leveldb-src
mkdir leveldb-src/build
fi
rm -rf leveldb
cd leveldb-src/build
cmake .. \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_C_FLAGS="$OSX_FLAGS $OSX_ARCH" \
-DCMAKE_CXX_FLAGS="$OSX_FLAGS $OSX_ARCH" \
-DLEVELDB_BUILD_TESTS=FALSE \
-DLEVELDB_BUILD_BENCHMARKS=FALSE \
-DLEVELDB_INSTALL=FALSE
cmake --build . -j
mkdir -p ../../leveldb
cp -v libleveldb.a ../../leveldb
cp -rv ../include ../../leveldb/include
echo "LevelDB build successful"

View File

@ -1,48 +0,0 @@
#!/bin/bash -e
JPEG_VERSION=3.0.1
. scripts/sdk.sh
mkdir -p deps; cd deps
if [ ! -d libjpeg-src ]; then
git clone -b $JPEG_VERSION --depth 1 https://github.com/libjpeg-turbo/libjpeg-turbo libjpeg-src
mkdir libjpeg-src/build
fi
rm -rf libjpeg
mkdir -p libjpeg/include
cd libjpeg-src
for ARCH in x86_64 arm64
do
echo "Building libjpeg for $ARCH"
mkdir -p build; cd build
cmake .. \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_C_FLAGS_RELEASE="$OSX_FLAGS -arch $ARCH" \
-DCMAKE_INSTALL_PREFIX="." \
-DCMAKE_OSX_ARCHITECTURES=$ARCH \
-DENABLE_SHARED=OFF
cmake --build . -j
make install -s
if [ $ARCH = "x86_64" ]; then
cp -rv include ../../libjpeg
cp -v lib/libjpeg.a ../../libjpeg/templib_$ARCH.a
else
cp -v lib/libjpeg.a ../../libjpeg/templib_$ARCH.a
fi
cd ..; rm -rf build
done
# repack into one .a
cd ../libjpeg
lipo -create templib_*.a -output libjpeg.a
rm templib_*.a
echo "libjpeg build successful"

View File

@ -1,29 +0,0 @@
#!/bin/bash -e
OGG_VERSION=1.3.5
. scripts/sdk.sh
mkdir -p deps; cd deps
if [ ! -d libogg-src ]; then
git clone -b v$OGG_VERSION --depth 1 https://github.com/xiph/ogg libogg-src
mkdir libogg-src/build
fi
rm -rf libogg
cd libogg-src/build
cmake .. \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_C_FLAGS_RELEASE="$OSX_FLAGS $OSX_ARCH" \
-DCMAKE_CXX_FLAGS_RELEASE="$OSX_FLAGS $OSX_ARCH" \
-DCMAKE_OSX_ARCHITECTURES=$OSX_ARCHITECTURES
cmake --build . -j
mkdir -p ../../libogg
cp -v libogg.a ../../libogg
cp -rv ../include ../../libogg/include
echo "Ogg build successful"

View File

@ -1,53 +0,0 @@
#!/bin/bash -e
PNG_VERSION=1.6.40
. scripts/sdk.sh
mkdir -p deps; cd deps
if [ ! -d libpng-src ]; then
wget https://download.sourceforge.net/libpng/libpng-$PNG_VERSION.tar.gz
tar -xzf libpng-$PNG_VERSION.tar.gz
mv libpng-$PNG_VERSION libpng-src
rm libpng-$PNG_VERSION.tar.gz
fi
rm -rf libpng
mkdir -p libpng/include
cd libpng-src
for ARCH in x86_64 arm64
do
echo "Building libpng for $ARCH"
mkdir -p build; cd build
cmake .. \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_C_FLAGS_RELEASE="$OSX_FLAGS -arch $ARCH" \
-DCMAKE_ASM_FLAGS_RELEASE="$OSX_FLAGS -arch $ARCH" \
-DCMAKE_INSTALL_PREFIX="." \
-DCMAKE_OSX_ARCHITECTURES=$ARCH \
-DPNG_SHARED=OFF \
-DPNG_TESTS=OFF \
-DPNG_EXECUTABLES=OFF
cmake --build . -j
make install -s
if [ $ARCH = "x86_64" ]; then
cp -rv include ../../libpng
cp -v lib/libpng16.a ../../libpng/templib_$ARCH.a
else
cp -v lib/libpng16.a ../../libpng/templib_$ARCH.a
fi
cd ..; rm -rf build
done
# repack into one .a
cd ../libpng
lipo -create templib_*.a -output libpng.a
rm templib_*.a
echo "libpng build successful"

View File

@ -1,32 +0,0 @@
#!/bin/bash -e
VORBIS_VERSION=1.3.7
. scripts/sdk.sh
mkdir -p deps; cd deps
if [ ! -d libvorbis-src ]; then
git clone -b v$VORBIS_VERSION --depth 1 https://github.com/xiph/vorbis libvorbis-src
mkdir libvorbis-src/build
fi
rm -rf libvorbis
cd libvorbis-src/build
cmake .. \
-DCMAKE_BUILD_TYPE=Release \
-DOGG_LIBRARY="../../libogg/libogg.a" \
-DOGG_INCLUDE_DIR="../../libogg/include" \
-DCMAKE_C_FLAGS_RELEASE="$OSX_FLAGS $OSX_ARCH" \
-DCMAKE_CXX_FLAGS_RELEASE="$OSX_FLAGS $OSX_ARCH" \
-DCMAKE_OSX_ARCHITECTURES=$OSX_ARCHITECTURES
cmake --build . -j
mkdir -p ../../libvorbis
cp -v lib/libvorbis.a ../../libvorbis
cp -v lib/libvorbisfile.a ../../libvorbis
cp -rv ../include ../../libvorbis/include
echo "Vorbis build successful"

View File

@ -1,30 +0,0 @@
#!/bin/bash -e
if [ ! -d MultiCraft/MultiCraft.xcodeproj ]; then
echo "Run this from Apple folder"
exit 1
fi
DEST=$(pwd)/assets/locale
broken_langs=(fil gd gl dv eo he hi jbo kn ko kk ky ms_Arab nn pt_BR sr_Cyrl sr_Latn zh_CN zh_TW)
pushd ../po
for lang in *; do
[ ${#lang} -ne 2 ] && continue
# Skip broken languages
if [[ " ${broken_langs[@]} " =~ " ${lang} " ]]; then
continue
fi
mopath=$DEST/$lang/LC_MESSAGES
mkdir -p $mopath
pushd $lang
for fn in *.po; do
# brew install gettext
msgfmt -o $mopath/${fn/.po/.mo} $fn
done
popd
done
popd
# Remove hidden files and directories
find $DEST -type d,f -name '.*' -print0 | xargs -0 -- rm -rf

View File

@ -1,35 +0,0 @@
#!/bin/bash -e
LUAJIT_VERSION="v2.1"
. scripts/sdk.sh
export MACOSX_DEPLOYMENT_TARGET=10.15
mkdir -p deps; cd deps
if [ ! -d luajit-src ]; then
git clone -b $LUAJIT_VERSION --depth 1 -c core.autocrlf=false https://github.com/LuaJIT/LuaJIT luajit-src
fi
rm -rf luajit
cd luajit-src
for ARCH in x86_64 arm64
do
echo "Building LuaJIT for $ARCH"
make amalg -j \
TARGET_FLAGS="$OSX_FLAGS -fno-fast-math -Wno-overriding-t-option -arch $ARCH"
cp src/libluajit.a templib_$ARCH.a
make clean
done
# repack into one .a
lipo -create templib_*.a -output libluajit.a
rm templib_*.a
mkdir -p ../luajit/include
cp -v src/*.h ../luajit/include
cp -v ../luajit/include/luajit_rolling.h ../luajit/include/luajit.h
cp -v libluajit.a ../luajit
echo "LuaJIT build successful"

View File

@ -1,34 +0,0 @@
#!/bin/bash -e
OPENAL_VERSION=1.23.1
. scripts/sdk.sh
mkdir -p deps; cd deps
if [ ! -d openal-src ]; then
git clone -b $OPENAL_VERSION --depth 1 https://github.com/kcat/openal-soft openal-src
fi
rm -rf openal
cd openal-src
cmake -S . \
-DCMAKE_BUILD_TYPE=Release \
-DLIBTYPE=STATIC \
-DALSOFT_REQUIRE_COREAUDIO=ON \
-DALSOFT_EMBED_HRTF_DATA=ON \
-DALSOFT_UTILS=OFF \
-DALSOFT_EXAMPLES=OFF \
-DALSOFT_BACKEND_WAVE=OFF \
-DCMAKE_C_FLAGS_RELEASE="$OSX_FLAGS $OSX_ARCH" \
-DCMAKE_CXX_FLAGS_RELEASE="$OSX_FLAGS $OSX_ARCH" \
-DCMAKE_OSX_ARCHITECTURES=$OSX_ARCHITECTURES
cmake --build . -j
mkdir -p ../openal
cp -v libopenal.a ../openal
cp -rv include ../openal/include
echo "OpenAL-Soft build successful"

View File

@ -1,12 +0,0 @@
#!/bin/bash -e
# This file sets the appropriate compiler and flags for compiling for macOS
export OSX_OSVER=10.11
export OSX_ARCHES="x86_64 arm64"
export OSX_ARCHITECTURES="x86_64;arm64"
export OSX_ARCH="-arch x86_64 -arch arm64"
export OSX_CC=$(xcrun --sdk macosx --find clang)
export OSX_CXX=$(xcrun --sdk macosx --find clang++)
export OSX_FLAGS="-isysroot $(xcrun --sdk macosx --show-sdk-path) -mmacosx-version-min=$OSX_OSVER -fdata-sections -ffunction-sections -Ofast"

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 2.6)
cmake_minimum_required(VERSION 3.5)
cmake_policy(SET CMP0025 OLD)
@ -6,60 +6,23 @@ cmake_policy(SET CMP0025 OLD)
project(multicraft)
set(PROJECT_NAME_CAPITALIZED "MultiCraft")
# check compatible compileer must be after project definition and set flags, assume if C++ is installed also CC is installed
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.7)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++0x")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
endif()
if (CMAKE_C_COMPILER_VERSION VERSION_LESS 4.6)
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99")
elseif (CMAKE_C_COMPILER_VERSION VERSION_EQUAL 4.6)
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu1x")
else()
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu11")
endif()
message(STATUS "using gnu compiler")
elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.4)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++1y")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
endif()
if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.5)
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu11")
endif()
message(STATUS "using clang compiler")
else()
if (CMAKE_VERSION VERSION_GREATER 3.0)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 11)
else()
if(APPLE)
# Fix behavior of CMAKE_CXX_STANDARD when targeting macOS.
if (POLICY CMP0025)
cmake_policy(SET CMP0025 NEW)
endif ()
endif ()
endif()
message(STATUS "using default installed compiler")
endif()
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
set(GCC_MINIMUM_VERSION "5.1")
set(CLANG_MINIMUM_VERSION "3.5")
# Also remember to set PROTOCOL_VERSION in network/networkprotocol.h when releasing
set(VERSION_MAJOR 2)
set(VERSION_MINOR 0)
set(VERSION_PATCH 6)
set(VERSION_PATCH 1)
set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string")
# Change to false for releases
set(DEVELOPMENT_BUILD FALSE)
set(ENABLE_UPDATE_CHECKER (NOT ${DEVELOPMENT_BUILD}) CACHE BOOL
"Whether to enable update checks by default")
set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
if(VERSION_EXTRA)
set(VERSION_STRING "${VERSION_STRING}-${VERSION_EXTRA}")
set(VERSION_STRING ${VERSION_STRING}-${VERSION_EXTRA})
elseif(DEVELOPMENT_BUILD)
set(VERSION_STRING "${VERSION_STRING}-dev")
endif()
@ -223,11 +186,10 @@ install(FILES "doc/world_format.txt" DESTINATION "${DOCDIR}" COMPONENT "Docs")
install(FILES "multicraft.conf.example" DESTINATION "${EXAMPLE_CONF_DIR}")
if(UNIX AND NOT APPLE)
install(FILES "doc/minetest.6" DESTINATION "${MANDIR}/man6" RENAME "${PROJECT_NAME}.6")
install(FILES "doc/minetestserver.6" DESTINATION "${MANDIR}/man6" RENAME "${PROJECT_NAME}server.6")
install(FILES "misc/net.minetest.minetest.desktop" DESTINATION "${XDG_APPS_DIR}" RENAME "net.minetest.${PROJECT_NAME}.desktop")
install(FILES "misc/net.minetest.minetest.appdata.xml" DESTINATION "${APPDATADIR}" RENAME "net.minetest.${PROJECT_NAME}.appdata.xml")
install(FILES "misc/minetest.svg" DESTINATION "${ICONDIR}/hicolor/scalable/apps" RENAME "multicraft.svg")
install(FILES "doc/minetest.6" "doc/minetestserver.6" DESTINATION "${MANDIR}/man6")
install(FILES "misc/net.minetest.minetest.desktop" DESTINATION "${XDG_APPS_DIR}")
install(FILES "misc/net.minetest.minetest.appdata.xml" DESTINATION "${APPDATADIR}")
install(FILES "misc/minetest.svg" DESTINATION "${ICONDIR}/hicolor/scalable/apps")
install(FILES "misc/multicraft-xorg-icon-128.png"
DESTINATION "${ICONDIR}/hicolor/128x128/apps"
RENAME "multicraft.png")

View File

@ -1,28 +1,68 @@
FROM alpine:3.18
FROM alpine:3.14
ENV MINETEST_GAME_VERSION master
COPY .git /usr/src/multicraft/.git
COPY CMakeLists.txt /usr/src/multicraft/CMakeLists.txt
COPY README.md /usr/src/multicraft/README.md
COPY multicraft.conf.example /usr/src/multicraft/multicraft.conf.example
COPY builtin /usr/src/multicraft/builtin
COPY cmake /usr/src/multicraft/cmake
COPY doc /usr/src/multicraft/doc
COPY fonts /usr/src/multicraft/fonts
COPY lib /usr/src/multicraft/lib
COPY misc /usr/src/multicraft/misc
COPY po /usr/src/multicraft/po
COPY src /usr/src/multicraft/src
COPY textures /usr/src/multicraft/textures
WORKDIR /usr/src/multicraft
RUN apk add --no-cache git build-base irrlicht-dev cmake bzip2-dev libpng-dev \
jpeg-dev libxxf86vm-dev mesa-dev sqlite-dev libogg-dev \
libvorbis-dev openal-soft-dev curl-dev freetype-dev zlib-dev \
gmp-dev jsoncpp-dev postgresql-dev luajit-dev ca-certificates && \
git clone --depth=1 -b ${MINETEST_GAME_VERSION} https://github.com/minetest/minetest_game.git ./games/minetest_game && \
rm -fr ./games/minetest_game/.git
WORKDIR /usr/src/
RUN git clone --recursive https://github.com/jupp0r/prometheus-cpp/ && \
mkdir prometheus-cpp/build && \
cd prometheus-cpp/build && \
cmake .. \
-DCMAKE_INSTALL_PREFIX=/usr/local \
-DCMAKE_BUILD_TYPE=Release \
-DENABLE_TESTING=0 && \
make -j2 && \
make install
RUN apk add --no-cache git build-base cmake pkgconf gettext-dev bzip2-dev curl-dev libnl3-dev rtmpdump-dev libidn-dev ncurses-dev freetype-dev mesa-dev gmp-dev irrlicht-dev libjpeg-turbo-dev jsoncpp-dev leveldb-dev luajit-dev lua5.1-dev libogg-dev openal-soft-dev libpng-dev postgresql-dev hiredis-dev sqlite-dev libvorbis-dev libxi-dev zlib-dev doxygen libxrandr-dev libx11-dev zstd-dev openssl-dev samurai
WORKDIR /usr/src
RUN cd /usr/src && \
git clone --recursive --depth=1 -b minenux https://gitlab.com/minenux/minetest-engine-multicraft2 multicraft && \
cd multicraft && \
rm -fr ./games/minetest/.git
WORKDIR /usr/src/multicraft
RUN mkdir build && \
cd build && \
cmake .. \
-DCMAKE_INSTALL_PREFIX=/usr/ -DBUILD_SERVER=ON -DBUILD_CLIENT=OFF -DRUN_IN_PLACE=OFF -DENABLE_CURL=ON -DENABLE_SOUND=ON -DENABLE_LUAJIT=ON -DENABLE_GETTEXT=ON -DENABLE_FREETYPE=ON -DENABLE_SYSTEM_GMP=ON -DENABLE_SYSTEM_JSONCPP=ON -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_POSTGRESQL=ON \
-DRUN_IN_PLACE=OFF -DCUSTOM_BINDIR=/usr/games -DCUSTOM_LOCALEDIR=/usr/share/locale -DCUSTOM_SHAREDIR=/usr/share/games/multicraft -DCUSTOM_EXAMPLE_CONF_DIR=/etc/multicraft .. \
make && \
-DCMAKE_INSTALL_PREFIX=/usr/local \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SERVER=TRUE \
-DENABLE_PROMETHEUS=TRUE \
-DBUILD_UNITTESTS=FALSE \
-DBUILD_CLIENT=FALSE && \
make -j2 && \
make install
FROM alpine:3.18
FROM alpine:3.14
RUN apk add --no-cache sqlite-libs curl gmp libstdc++ libgcc libpq luajit && \
adduser -D multicraft --uid 30000 -h /var/lib/multicraft && \
chown -R multicraft:multicraft /var/lib/multicraft
WORKDIR /var/lib/multicraft
COPY --from=0 /usr/local/share/multicraft /usr/local/share/multicraft
COPY --from=0 /usr/local/bin/multicraftserver /usr/local/bin/multicraftserver
COPY --from=0 /usr/local/share/doc/multicraft/multicraft.conf.example /etc/multicraft/multicraft.conf
RUN apk add --no-cache libstdc++ libgcc bzip2 curl libnl3 librtmp libidn libintl libncursesw freetype mesa-gl gmp irrlicht libjpeg-turbo jsoncpp leveldb luajit lua5.1 libogg openal-soft-libs libpng libpq hiredis sqlite-libs libvorbis libxi zlib-dev libxrandr libx11 zstd && \
mkdir -p /var/games/multicraft && adduser -D multicraft --uid 30000 -h /var/games/multicraft && chown -R multicraft:multicraft /var/games/multicraft
COPY --from=0 /usr/share/games/multicraft /usr/share/games/multicraft
COPY --from=0 /usr/games/multicraftserver /usr/games/multicraftserver
COPY --from=0 /etc/multicraft/multicraft.conf /etc/multicraft/multicraft.conf
USER multicraft:multicraft
EXPOSE 40000/udp 40000/tcp
WORKDIR /var/games/multicraft
CMD ["/usr/games/multicraftserver", "--port", "40000", "--config", "/etc/multicraft/multicraft.conf", "--gameid", "minetest", "--world", "minetest"]
EXPOSE 30000/udp 30000/tcp
CMD ["/usr/local/bin/multicraftserver", "--config", "/etc/multicraft/multicraft.conf"]

View File

@ -1,13 +1,16 @@
License of MultiCraft textures and sounds
License of Minetest textures and sounds
---------------------------------------
This applies to textures and sounds contained in the main MultiCraft
This applies to textures and sounds contained in the main Minetest
distribution.
Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
http://creativecommons.org/licenses/by-sa/3.0/
Textures by Zughy & MultiCraft Development Team are under CC BY-SA 4.0
textures/base/pack/refresh.png is under the Apache 2 license
https://www.apache.org/licenses/LICENSE-2.0.html
Textures by Zughy are under CC BY-SA 4.0
https://creativecommons.org/licenses/by-sa/4.0/
Authors of media files
@ -15,55 +18,15 @@ Authors of media files
Everything not listed in here:
Copyright (C) 2010-2012 celeron55, Perttu Ahola <celeron55@gmail.com>
MultiCraft Development Team:
textures/base/pack/gui/*.png
textures/base/pack/attention.png
textures/base/pack/bg_common.png
textures/base/pack/btn_play.png
textures/base/pack/btn_play_hover.png
textures/base/pack/camera_btn.png
textures/base/pack/chat_btn.png
textures/base/pack/crack_anylength_touch.png
textures/base/pack/creative_bg.png
textures/base/pack/cursor.png
textures/base/pack/error_screenshot.png
textures/base/pack/desc_bg.png
textures/base/pack/down_btn.png
textures/base/pack/drop_btn.png
textures/base/pack/escape_btn.png
textures/base/pack/hunger_*.png
textures/base/pack/inventory_btn.png
textures/base/pack/joystick_*.png
textures/base/pack/jump_btn.png
textures/base/pack/loading_screenshot.png
textures/base/pack/logo.png
textures/base/pack/minimap_btn.png
textures/base/pack/minimap_mask_*.png
textures/base/pack/minimap_overlay_*.png
textures/base/pack/player_marker.png
textures/base/pack/progress_bar.png
textures/base/pack/progress_bar_*.png
textures/base/pack/rangeview_btn.png
textures/base/pack/refresh.png
textures/base/pack/server_flags_creative.png
textures/base/pack/server_flags_damage.png
textures/base/pack/server_flags_favorite.png
textures/base/pack/server_flags_mc.png
textures/base/pack/server_flags_mt.png
textures/base/pack/server_flags_pvp.png
textures/base/pack/server_ping_*.png
textures/base/pack/switch_local_*.png
textures/base/pack/trash.png
textures/base/pack/trash_pressed.png
textures/base/pack/worldlist_bg.png
ShadowNinja:
textures/base/pack/smoke_puff.png
paramat:
textures/base/pack/menu_header.png
textures/base/pack/next_icon.png
textures/base/pack/prev_icon.png
textures/base/pack/clear.png
textures/base/pack/search.png
rubenwardy, paramat:
textures/base/pack/start_icon.png
@ -73,10 +36,18 @@ erlehmann:
misc/minetest-icon-24x24.png
misc/minetest-icon.ico
misc/minetest.svg
textures/base/pack/logo.png
JRottm
textures/base/pack/player_marker.png
srifqi
textures/base/pack/chat_hide_btn.png
textures/base/pack/chat_show_btn.png
textures/base/pack/joystick_bg.png
textures/base/pack/joystick_center.png
textures/base/pack/joystick_off.png
textures/base/pack/minimap_btn.png
Zughy:
textures/base/pack/cdb_add.png
@ -86,10 +57,6 @@ Zughy:
textures/base/pack/cdb_update.png
textures/base/pack/cdb_viewonline.png
KevDoy, An0n3m0us:
textures/base/pack/heart.png
textures/base/pack/heart_gone.png
License of Minetest source code
-------------------------------
@ -265,10 +232,8 @@ DroidSansFallBackFull:
See the License for the specific language governing permissions and
limitations under the License.
MultiCraft Font:
Retron2000:
Copyright (C) 2021-2022 MultiCraft Development Team
1001Fonts Free For Commercial Use License (FFC)
Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0)
https://creativecommons.org/licenses/by-nc-nd/4.0/
Full copy of license you can find in source code: fonts/1001fonts-retron2000-eula.txt

103
Portfile
View File

@ -1,103 +0,0 @@
# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:ft=tcl:et:sw=4:ts=4:sts=4
# por file for MAC ports.. currently only tested in older versions of MACOS(X)
PortSystem 1.0
PortGroup github 1.0
PortGroup cmake 1.1
compiler.cxx_standard 2011
compiler.thread_local_storage yes
github.setup multicraft multicraft 2.0.6
revision 1
# the game version could be different from the multicraft version
set game_version 2.0.6
# to add on more files to a github portgroup download
# have to cache the preset distfiles from the github PG
set main_distfile ${distfiles}
# then add another distfile, with a direct URL as can't use the github PG again
# set game_distfile ${game_version}${extract.suffix}
# set game_mastersite https://codeberg.org/minenux/minetest-game-minetest/archive/
distfiles ${main_distfile}:main
master_sites ${github.master_sites}:main
checksums ${main_distfile} \
rmd160 550fd4c6767cce4bb5dd79f9d907b233e21b9b2c \
sha256 8a320b3355381ba7ed4b6a41bfb0f39cb2858eb7bae0f6fc5935b329a53bde56 \
size 10116355
# rename directory - from github portgroup
post-extract {
if {[file exists [glob -nocomplain ${workpath}/${github.author}-${github.project}-*]] && \
[file isdirectory [glob -nocomplain ${workpath}/${github.author}-${github.project}-*]]} {
move [glob ${workpath}/${github.author}-${github.project}-*] ${workpath}/${distname}
}
}
license LGPL-2.1+
categories games
platforms darwin
maintainers @mckaygerhard openmaintainer
description Survival Craft Build Sandbox Game STOLEN from MINETEST
long_description ${description} - the multipprotocol version enhanched for play in older or newer servers
homepage https://www.minetest.org
depends_build-append path:bin/doxygen:doxygen \
port:mesa
depends_lib-append port:irrlichtmt \
path:include/turbojpeg.h:libjpeg-turbo \
port:libogg \
port:libvorbis \
port:freetype \
port:gettext \
port:leveldb \
port:sqlite3 \
port:zstd \
path:lib/libluajit-5.1.2.dylib:luajit \
port:gmp \
port:curl \
port:jsoncpp \
port:spatialindex \
port:xorg-libX11 \
port:xorg-libXxf86vm
# see https://trac.macports.org/ticket/58315 - possibly fixable?
universal_variant no
supported_archs x86_64 x86
# 001. the original build calls fixup_bundle to move all the deps into the app bundle.
# this doesn't work correctly with macports destrooting, and isn't necessary for a macports install so deleted it
# patchfiles-append 001-patch-src-CMakeLists-disable-bundlefixup.diff
configure.args-append -DCMAKE_BUILD_TYPE=Release \
-DBUILD_SERVER=ON -DBUILD_CLIENT=ON \
-DENABLE_CURL=ON \
-DENABLE_SOUND=ON \
-DENABLE_LUAJIT=ON \
-DENABLE_GETTEXT=ON \
-DENABLE_FREETYPE=ON \
-DENABLE_SYSTEM_GMP=ON \
-DENABLE_SYSTEM_JSONCPP=ON \
-DCMAKE_VERBOSE_MAKEFILE=ON \
-DCMAKE_BUILD_TYPE=Release \
-DENABLE_POSTGRESQL=ON \
-DENABLE_REDIS=OFF -DCURSES_INCLUDE_PATH:FILEPATH=${prefix}/include \
-DCMAKE_INSTALL_PREFIX:PATH=${applications_dir} \
-DBUILD_UNITTESTS=OFF \
-DENABLE_UPDATE_CHECKER=OFF \
-DENABLE_SPATIAL=ON \
-DINSTALL_DEVTEST=ON \
-DLUA_INCLUDE_DIR:PATH=${prefix}/include/luajit-2.1 \
-DLUA_LIBRARY:FILEPATH=${prefix}/lib/libluajit-5.1.dylib \
-DVERSION_EXTRA=MacPorts-rev${revision}
post-destroot {
move ${workpath}/multicraft_game-${game_version} ${destroot}${applications_dir}/multicraft.app/Contents/Resources/games/multicraft_game
}

369
README.md
View File

@ -1,31 +1,362 @@
MultiCraft
==========
MultiCraft Open Source
======================
[![pipeline status](https://gitlab.com/minenux/minetest-engine-multicraft2/badges/master/pipeline.svg)](https://gitlab.com/minenux/minetest-engine-multicraft2/-/commits/master)
![Build Status](https://github.com/MultiCraft/MultiCraft2/workflows/build/badge.svg)
[![License](https://img.shields.io/badge/license-LGPLv3.0%2B-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0.en.html)
[![License: CC BY-SA 4.0](https://img.shields.io/badge/license-CC_BY--SA_4.0-orange.svg)](https://creativecommons.org/licenses/by-sa/4.0/)
MultiCraft Open Source is a Survival Craft Build Sandbox Game STOLEN from MINETEST where a niche of guys make money from the work of others!
MultiCraft Open Source is a free open-source voxel game engine with easy modding and game creation.
..cos is based on the Minetest project, which is developed by a [number of contributors](https://github.com/minetest/minetest/graphs/contributors) but doe snot show in the Apple Store or Gplay Store!
MultiCraft is based on the Minetest project, which is developed by a [number of contributors](https://github.com/minetest/minetest/graphs/contributors).
## Information
--------------
Copyright © 2014-2022 Maksim Gamarnik [MoNTE48] <Maksym48@pm.me> & MultiCraft Development Team.
IF you wish downloable artifacts to direct usage go to:
Table of Contents
------------------
* Linux: https://software.opensuse.org//download.html?project=home%3Avenenux%3Aminenux&package=minetest [![build and download multicraft](https://build.opensuse.org/projects/home:venenux:minenux/packages/minetest/badge.svg?type=percent)](https://build.opensuse.org/package/show/home:venenux:minenux/minetest)
* Windos: we do not support it but you can download modified versions from : https://t.me/latam_minecraft_chat/766185 access maybe need some rules!
* MAc: we do not support it but you can download modified versions from : https://t.me/latam_minecraft_chat/766185 access maybe need some rules!
* Androit: we do not support it but you can download modified versions from : https://t.me/latam_minecraft_chat/766185 access maybe need some rules!
1. [Further Documentation](#further-documentation)
2. [Default Controls](#default-controls)
3. [Paths](#paths)
4. [Configuration File](#configuration-file)
5. [Command-line Options](#command-line-options)
6. [Compiling](#compiling)
7. [Docker](#docker)
8. [Version Scheme](#version-scheme)
This is the repository for sources of multicraft, The truth is that the sources only compile well in Linux, for other cases there are strong dependencies on the configurations made by the team that replaces the work of minetest.
This means that it will only compile the last commit and it will be **impossible to reproduce the compilation in previous commits or versions even if you have the dependencies, since the configurations and the reason for them are obfuscated!**
Further documentation
----------------------
- Minetest Website: https://minetest.net/
- Minetest Wiki: https://wiki.minetest.net/
- Minetest Developer wiki: https://dev.minetest.net/
- Minetest Forum: https://forum.minetest.net/
- Minetest GitHub: https://github.com/minetest/minetest/
- [doc/](doc/) directory of source distribution
## Technical info
-----------------
Default controls
----------------
All controls are re-bindable using settings.
Some can be changed in the key config dialog in the settings tab.
This source does not remove backguar compatibility.. you can connect or serve older or newer minetest protocols!
| Button | Action |
|-------------------------------|----------------------------------------------------------------|
| Move mouse | Look around |
| W, A, S, D | Move |
| Space | Jump/move up |
| Shift | Sneak/move down |
| Q | Drop itemstack |
| Shift + Q | Drop single item |
| Left mouse button | Dig/punch/take item |
| Right mouse button | Place/use |
| Shift + right mouse button | Build (without using) |
| I | Inventory menu |
| Mouse wheel | Select item |
| 0-9 | Select item |
| Z | Zoom (needs zoom privilege) |
| T | Chat |
| / | Command |
| Esc | Pause menu/abort/exit (pauses only singleplayer game) |
| R | Enable/disable full range view |
| + | Increase view range |
| - | Decrease view range |
| K | Enable/disable fly mode (needs fly privilege) |
| P | Enable/disable pitch move mode |
| J | Enable/disable fast mode (needs fast privilege) |
| H | Enable/disable noclip mode (needs noclip privilege) |
| E | Move fast in fast mode |
| C | Cycle through camera modes |
| V | Cycle through minimap modes |
| Shift + V | Change minimap orientation |
| F1 | Hide/show HUD |
| F2 | Hide/show chat |
| F3 | Disable/enable fog |
| F4 | Disable/enable camera update (Mapblocks are not updated anymore when disabled, disabled in release builds) |
| F5 | Cycle through debug information screens |
| F6 | Cycle through profiler info screens |
| F10 | Show/hide console |
| F12 | Take screenshot |
Visit wiki.minetest.org
Paths
-----
Locations:
* `bin` - Compiled binaries
* `share` - Distributed read-only data
* `user` - User-created modifiable data
Where each location is on each platform:
* Windows .zip / RUN_IN_PLACE source:
* `bin` = `bin`
* `share` = `.`
* `user` = `.`
* Windows installed:
* `bin` = `C:\Program Files\MultiCraft\bin (Depends on the install location)`
* `share` = `C:\Program Files\MultiCraft (Depends on the install location)`
* `user` = `%APPDATA%\MultiCraft`
* Linux installed:
* `bin` = `/usr/bin`
* `share` = `/usr/share/multicraft`
* `user` = `~/.multicraft`
* macOS:
* `bin` = `Contents/MacOS`
* `share` = `Contents/Resources`
* `user` = `Contents/User OR ~/Library/Application Support/multicraft`
Worlds can be found as separate folders in: `user/worlds/`
Configuration file
------------------
- Default location:
`user/multicraft.conf`
- This file is created by closing MultiCraft for the first time.
- A specific file can be specified on the command line:
`--config <path-to-file>`
- A run-in-place build will look for the configuration file in
`location_of_exe/../multicraft.conf` and also `location_of_exe/../../multicraft.conf`
Command-line options
--------------------
- Use `--help`
Compiling
---------
### Compiling on GNU/Linux
#### Dependencies
| Dependency | Version | Commentary |
|------------|---------|------------|
| GCC | 4.9+ | Can be replaced with Clang 3.4+ |
| CMake | 2.6+ | |
| Irrlicht | 1.7.3+ | |
| SQLite3 | 3.0+ | |
| LuaJIT | 2.0+ | Bundled Lua 5.1 is used if not present |
| GMP | 5.0.0+ | Bundled mini-GMP is used if not present |
| JsonCPP | 1.0.0+ | Bundled JsonCPP is used if not present |
For Debian/Ubuntu users:
sudo apt install g++ make libc6-dev libirrlicht-dev cmake libbz2-dev libpng-dev libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev
For Fedora users:
sudo dnf install make automake gcc gcc-c++ kernel-devel cmake libcurl-devel openal-soft-devel libvorbis-devel libXxf86vm-devel libogg-devel freetype-devel mesa-libGL-devel zlib-devel jsoncpp-devel irrlicht-devel bzip2-libs gmp-devel sqlite-devel luajit-devel leveldb-devel ncurses-devel doxygen spatialindex-devel bzip2-devel
For Arch users:
sudo pacman -S base-devel libcurl-gnutls cmake libxxf86vm irrlicht libpng sqlite libogg libvorbis openal freetype2 jsoncpp gmp luajit leveldb ncurses
For Alpine users:
sudo apk add build-base irrlicht-dev cmake bzip2-dev libpng-dev jpeg-dev libxxf86vm-dev mesa-dev sqlite-dev libogg-dev libvorbis-dev openal-soft-dev curl-dev freetype-dev zlib-dev gmp-dev jsoncpp-dev luajit-dev
#### Download
You can install Git for easily keeping your copy up to date.
If you dont want Git, read below on how to get the source without Git.
This is an example for installing Git on Debian/Ubuntu:
sudo apt install git
For Fedora users:
sudo dnf install git
Download source (this is the URL to the latest of source repository, which might not work at all times) using Git:
git clone --depth 1 https://github.com/MultiCraft/MultiCraft.git
cd MultiCraft
Download source, without using Git:
wget https://github.com/MultiCraft/MultiCraft/archive/master.tar.gz
tar xf master.tar.gz
cd MultiCraft-master
#### Build
Build a version that runs directly from the source directory:
cmake . -DRUN_IN_PLACE=TRUE
make -j$(nproc)
Run it:
./bin/multicraft
- Use `cmake . -LH` to see all CMake options and their current state.
- If you want to install it system-wide (or are making a distribution package),
you will want to use `-DRUN_IN_PLACE=FALSE`.
- You can build a bare server by specifying `-DBUILD_SERVER=TRUE`.
- You can disable the client build by specifying `-DBUILD_CLIENT=FALSE`.
- You can select between Release and Debug build by `-DCMAKE_BUILD_TYPE=<Debug or Release>`.
- Debug build is slower, but gives much more useful output in a debugger.
- If you build a bare server you don't need to have Irrlicht installed.
- In that case use `-DIRRLICHT_SOURCE_DIR=/the/irrlicht/source`.
### CMake options
General options and their default values:
BUILD_CLIENT=TRUE - Build MultiCraft client
BUILD_SERVER=FALSE - Build MultiCraft server
BUILD_UNITTESTS=TRUE - Build unittest sources
CMAKE_BUILD_TYPE=Release - Type of build (Release vs. Debug)
Release - Release build
Debug - Debug build
SemiDebug - Partially optimized debug build
RelWithDebInfo - Release build with debug information
MinSizeRel - Release build with -Os passed to compiler to make executable as small as possible
ENABLE_CURL=ON - Build with cURL; Enables use of online mod repo, public serverlist and remote media fetching via http
ENABLE_CURSES=ON - Build with (n)curses; Enables a server side terminal (command line option: --terminal)
ENABLE_FREETYPE=ON - Build with FreeType2; Allows using TTF fonts
ENABLE_GETTEXT=ON - Build with Gettext; Allows using translations
ENABLE_GLES=OFF - Build for OpenGL ES instead of OpenGL (requires support by Irrlicht)
ENABLE_LEVELDB=ON - Build with LevelDB; Enables use of LevelDB map backend
ENABLE_POSTGRESQL=ON - Build with libpq; Enables use of PostgreSQL map backend (PostgreSQL 9.5 or greater recommended)
ENABLE_REDIS=ON - Build with libhiredis; Enables use of Redis map backend
ENABLE_SPATIAL=ON - Build with LibSpatial; Speeds up AreaStores
ENABLE_SOUND=ON - Build with OpenAL, libogg & libvorbis; in-game sounds
ENABLE_LUAJIT=ON - Build with LuaJIT (much faster than non-JIT Lua)
ENABLE_PROMETHEUS=OFF - Build with Prometheus metrics exporter (listens on tcp/30000 by default)
ENABLE_SYSTEM_GMP=ON - Use GMP from system (much faster than bundled mini-gmp)
ENABLE_SYSTEM_JSONCPP=OFF - Use JsonCPP from system
OPENGL_GL_PREFERENCE=LEGACY - Linux client build only; See CMake Policy CMP0072 for reference
RUN_IN_PLACE=FALSE - Create a portable install (worlds, settings etc. in current directory)
USE_GPROF=FALSE - Enable profiling using GProf
VERSION_EXTRA= - Text to append to version (e.g. VERSION_EXTRA=foobar -> MultiCraft 0.4.9-foobar)
ENABLE_TOUCH=FALSE - Enable Touchscreen support (requires support by IrrlichtMt)
Library specific options:
BZIP2_INCLUDE_DIR - Linux only; directory where bzlib.h is located
BZIP2_LIBRARY - Linux only; path to libbz2.a/libbz2.so
CURL_DLL - Only if building with cURL on Windows; path to libcurl.dll
CURL_INCLUDE_DIR - Only if building with cURL; directory where curl.h is located
CURL_LIBRARY - Only if building with cURL; path to libcurl.a/libcurl.so/libcurl.lib
EGL_INCLUDE_DIR - Only if building with GLES; directory that contains egl.h
EGL_LIBRARY - Only if building with GLES; path to libEGL.a/libEGL.so
FREETYPE_INCLUDE_DIR_freetype2 - Only if building with FreeType 2; directory that contains an freetype directory with files such as ftimage.h in it
FREETYPE_INCLUDE_DIR_ft2build - Only if building with FreeType 2; directory that contains ft2build.h
FREETYPE_LIBRARY - Only if building with FreeType 2; path to libfreetype.a/libfreetype.so/freetype.lib
FREETYPE_DLL - Only if building with FreeType 2 on Windows; path to libfreetype.dll
GETTEXT_DLL - Only when building with gettext on Windows; path to libintl3.dll
GETTEXT_ICONV_DLL - Only when building with gettext on Windows; path to libiconv2.dll
GETTEXT_INCLUDE_DIR - Only when building with gettext; directory that contains iconv.h
GETTEXT_LIBRARY - Only when building with gettext on Windows; path to libintl.dll.a
GETTEXT_MSGFMT - Only when building with gettext; path to msgfmt/msgfmt.exe
IRRLICHT_DLL - Only on Windows; path to Irrlicht.dll
IRRLICHT_INCLUDE_DIR - Directory that contains IrrCompileConfig.h
IRRLICHT_LIBRARY - Path to libIrrlicht.a/libIrrlicht.so/libIrrlicht.dll.a/Irrlicht.lib
LEVELDB_INCLUDE_DIR - Only when building with LevelDB; directory that contains db.h
LEVELDB_LIBRARY - Only when building with LevelDB; path to libleveldb.a/libleveldb.so/libleveldb.dll.a
LEVELDB_DLL - Only when building with LevelDB on Windows; path to libleveldb.dll
PostgreSQL_INCLUDE_DIR - Only when building with PostgreSQL; directory that contains libpq-fe.h
PostgreSQL_LIBRARY - Only when building with PostgreSQL; path to libpq.a/libpq.so/libpq.lib
REDIS_INCLUDE_DIR - Only when building with Redis; directory that contains hiredis.h
REDIS_LIBRARY - Only when building with Redis; path to libhiredis.a/libhiredis.so
SPATIAL_INCLUDE_DIR - Only when building with LibSpatial; directory that contains spatialindex/SpatialIndex.h
SPATIAL_LIBRARY - Only when building with LibSpatial; path to libspatialindex_c.so/spatialindex-32.lib
LUA_INCLUDE_DIR - Only if you want to use LuaJIT; directory where luajit.h is located
LUA_LIBRARY - Only if you want to use LuaJIT; path to libluajit.a/libluajit.so
MINGWM10_DLL - Only if compiling with MinGW; path to mingwm10.dll
OGG_DLL - Only if building with sound on Windows; path to libogg.dll
OGG_INCLUDE_DIR - Only if building with sound; directory that contains an ogg directory which contains ogg.h
OGG_LIBRARY - Only if building with sound; path to libogg.a/libogg.so/libogg.dll.a
OPENAL_DLL - Only if building with sound on Windows; path to OpenAL32.dll
OPENAL_INCLUDE_DIR - Only if building with sound; directory where al.h is located
OPENAL_LIBRARY - Only if building with sound; path to libopenal.a/libopenal.so/OpenAL32.lib
OPENGLES2_INCLUDE_DIR - Only if building with GLES; directory that contains gl2.h
OPENGLES2_LIBRARY - Only if building with GLES; path to libGLESv2.a/libGLESv2.so
SQLITE3_INCLUDE_DIR - Directory that contains sqlite3.h
SQLITE3_LIBRARY - Path to libsqlite3.a/libsqlite3.so/sqlite3.lib
VORBISFILE_DLL - Only if building with sound on Windows; path to libvorbisfile-3.dll
VORBISFILE_LIBRARY - Only if building with sound; path to libvorbisfile.a/libvorbisfile.so/libvorbisfile.dll.a
VORBIS_DLL - Only if building with sound on Windows; path to libvorbis-0.dll
VORBIS_INCLUDE_DIR - Only if building with sound; directory that contains a directory vorbis with vorbisenc.h inside
VORBIS_LIBRARY - Only if building with sound; path to libvorbis.a/libvorbis.so/libvorbis.dll.a
XXF86VM_LIBRARY - Only on Linux; path to libXXf86vm.a/libXXf86vm.so
ZLIB_DLL - Only on Windows; path to zlib1.dll
ZLIB_INCLUDE_DIR - Directory that contains zlib.h
ZLIB_LIBRARY - Path to libz.a/libz.so/zlib.lib
### Compiling on Windows
### Requirements
- [Visual Studio 2015 or newer](https://visualstudio.microsoft.com)
- [CMake](https://cmake.org/download/)
- [vcpkg](https://github.com/Microsoft/vcpkg)
- [Git](https://git-scm.com/downloads)
### Compiling and installing the dependencies
It is highly recommended to use vcpkg as package manager.
#### a) Using vcpkg to install dependencies
After you successfully built vcpkg you can easily install the required libraries:
```powershell
vcpkg install irrlicht zlib curl[winssl] openal-soft libvorbis libogg sqlite3 freetype luajit gmp jsoncpp --triplet x64-windows
```
- `curl` is optional, but required to read the serverlist, `curl[winssl]` is required to use the content store.
- `openal-soft`, `libvorbis` and `libogg` are optional, but required to use sound.
- `freetype` is optional, it allows true-type font rendering.
- `luajit` is optional, it replaces the integrated Lua interpreter with a faster just-in-time interpreter.
- `gmp` and `jsoncpp` are optional, otherwise the bundled versions will be compiled
There are other optional libraries, but they are not tested if they can build and link correctly.
Use `--triplet` to specify the target triplet, e.g. `x64-windows` or `x86-windows`.
#### b) Compile the dependencies on your own
This is outdated and not recommended. Follow the instructions on https://dev.minetest.net/Build_Win32_Minetest_including_all_required_libraries#VS2012_Build
### Compile MultiCraft
#### a) Using the vcpkg toolchain and CMake GUI
1. Start up the CMake GUI
2. Select **Browse Source...** and select DIR/multicraft
3. Select **Browse Build...** and select DIR/multicraft-build
4. Select **Configure**
5. Choose the right visual Studio version and target platform. It has to match the version of the installed dependencies
6. Choose **Specify toolchain file for cross-compiling**
7. Click **Next**
8. Select the vcpkg toolchain file e.g. `D:/vcpkg/scripts/buildsystems/vcpkg.cmake`
9. Click Finish
10. Wait until cmake have generated the cash file
11. If there are any errors, solve them and hit **Configure**
12. Click **Generate**
13. Click **Open Project**
14. Compile MultiCraft inside Visual studio.
#### b) Using the vcpkg toolchain and the commandline
Run the following script in PowerShell:
```powershell
cmake . -G"Visual Studio 15 2017 Win64" -DCMAKE_TOOLCHAIN_FILE=D:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_GETTEXT=OFF -DENABLE_CURSES=OFF -DENABLE_SYSTEM_JSONCPP=ON
cmake --build . --config Release
```
Make sure that the right compiler is selected and the path to the vcpkg toolchain is correct.
#### c) Using your own compiled libraries
**This is outdated and not recommended**
Follow the instructions on https://dev.minetest.net/Build_Win32_Minetest_including_all_required_libraries#VS2012_Build
### Windows Installer using WiX Toolset
Requirements:
* [Visual Studio 2017](https://visualstudio.microsoft.com/)
* [WiX Toolset](https://wixtoolset.org/)
In the Visual Studio 2017 Installer select **Optional Features -> WiX Toolset**.
Build the binaries as described above, but make sure you unselect `RUN_IN_PLACE`.
Open the generated project file with Visual Studio. Right-click **Package** and choose **Generate**.
It may take some minutes to generate the installer.

9
Windows/.gitignore vendored
View File

@ -1,9 +0,0 @@
CMakeFiles
deps/*
!deps/*.sh
lib
locale
src
*.ninja*
*.cmake
CMakeCache.txt

View File

@ -1,39 +0,0 @@
The SHIITTT of Guindows
-----------------------
This is reduced set for stupid windosers:
1. Download MSYS2 from https://www.msys2.org/
https://github.com/msys2/msys2-installer/releases/download/2023-03-18/msys2-x86_64-20230318.exe
2. After installation open "MSYS2 MINGW64" from Menu Start
3. Install required packages with
pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-cmake mingw-w64-x86_64-autotools git
4. Browse to MultiCraft directory, for example
cd /c/Users/username/Documents/MultiCraft
5. Build inside build/windows directory with
./Start
cmake --build . -j
6. Check libraries linked with multicraft.exe using command below. It should
show only system libraries.
objdump -x ../../bin/multicraft.exe | grep dll
--------------
The "make -j" and even "make -j`nproc`" eats a lot of memory (when building libcurl).
If it's a problem, then change NPROC to 1 or so.
Also, don't install any other packages that are not for mingw because then some
libraries search headers in /usr/include rather than /mingw64/include and fail
to compile.

View File

@ -1,73 +0,0 @@
#!/bin/bash -e
echo
echo "Starting build MultiCraft for Windows..."
echo
echo "Build Libraries:"
cd deps
sh libraries.sh
cd ..
export DEPS_ROOT=$(pwd)/deps
cmake ../ \
-DCMAKE_BUILD_TYPE=Release \
-DENABLE_SQLITE=1 \
-DENABLE_POSTGRESQL=0 \
-DENABLE_LEVELDB=0 \
-DENABLE_REDIS=0 \
-DENABLE_SPATIAL=0 \
-DENABLE_PROMETHEUS=0 \
-DENABLE_CURSES=0 \
-DENABLE_SYSTEM_GMP=0 \
-DUSE_SDL=1 \
-DUSE_STATIC_BUILD=1 \
-DCMAKE_C_FLAGS="-static \
-DNO_IRR_COMPILE_WITH_SDL_TEXTINPUT_ \
-DNO_IRR_COMPILE_WITH_OGLES2_ \
-DNO_IRR_COMPILE_WITH_DIRECT3D_9_ \
-DNO_IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_ \
-D_IRR_STATIC_LIB_ \
-DAL_LIBTYPE_STATIC \
-DCURL_STATICLIB" \
-DCMAKE_CXX_FLAGS="-static \
-DNO_IRR_COMPILE_WITH_SDL_TEXTINPUT_ \
-DNO_IRR_COMPILE_WITH_OGLES2_ \
-DNO_IRR_COMPILE_WITH_DIRECT3D_9_ \
-DNO_IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_ \
-D_IRR_STATIC_LIB_ \
-DAL_LIBTYPE_STATIC \
-DCURL_STATICLIB" \
-DIRRLICHT_LIBRARY="$DEPS_ROOT/irrlicht/lib/libIrrlicht.a" \
-DIRRLICHT_INCLUDE_DIR="$DEPS_ROOT/irrlicht/include" \
-DSDL2_LIBRARIES="$DEPS_ROOT/sdl2/lib/libSDL2.a" \
-DSDL2_INCLUDE_DIR="$DEPS_ROOT/sdl2/include" \
-DCURL_LIBRARY="$DEPS_ROOT/libcurl/lib/libcurl.a" \
-DCURL_INCLUDE_DIR="$DEPS_ROOT/libcurl/include" \
-DLUA_LIBRARY="$DEPS_ROOT/luajit/lib/libluajit.a" \
-DLUA_INCLUDE_DIR="$DEPS_ROOT/luajit/include" \
-DZLIB_LIBRARIES="$DEPS_ROOT/zlib/lib/libzlibstatic.a" \
-DZLIB_INCLUDE_DIR="$DEPS_ROOT/zlib/include" \
-DPNG_LIBRARIES="$DEPS_ROOT/libpng/lib/libpng16.a" \
-DPNG_INCLUDE_DIR="$DEPS_ROOT/libpng/include" \
-DJPEG_LIBRARIES="$DEPS_ROOT/libjpeg/lib/libjpeg.a" \
-DJPEG_INCLUDE_DIR="$DEPS_ROOT/libjpeg/include" \
-DFREETYPE_LIBRARY="$DEPS_ROOT/freetype/lib/libfreetype.a" \
-DFREETYPE_INCLUDE_DIRS="$DEPS_ROOT/freetype/include" \
-DSQLITE3_LIBRARY="$DEPS_ROOT/sqlite/lib/libsqlite3.a" \
-DSQLITE3_INCLUDE_DIR="$DEPS_ROOT/sqlite/include" \
-DOGG_LIBRARY="$DEPS_ROOT/libogg/lib/libogg.a" \
-DOGG_INCLUDE_DIR="$DEPS_ROOT/libogg/include" \
-DVORBIS_LIBRARY="$DEPS_ROOT/libvorbis/lib/libvorbis.a" \
-DVORBISFILE_LIBRARY="$DEPS_ROOT/libvorbis/lib/libvorbisfile.a" \
-DVORBIS_INCLUDE_DIR="$DEPS_ROOT/libvorbis/include" \
-DGETTEXT_LIBRARY="$DEPS_ROOT/gettext/lib/libintl.a" \
-DGETTEXT_ICONV_LIBRARY="/mingw64/lib/libiconv.a" \
-DGETTEXT_INCLUDE_DIR="$DEPS_ROOT/gettext/include" \
-DOPENAL_LIBRARY="$DEPS_ROOT/openal/lib/libOpenAL32.a" \
-DOPENAL_INCLUDE_DIR="$DEPS_ROOT/openal/include/AL"
echo
echo "Build with 'cmake --build . -j'"

Some files were not shown because too many files have changed in this diff Show More