Compare commits
256 Commits
master
...
5.8.1-andr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d445638265 | ||
|
|
f61f6626b7 | ||
|
|
857ba25115 | ||
|
|
00774bc28b | ||
|
|
f572266f19 | ||
|
|
c3893064a3 | ||
|
|
4183443f02 | ||
|
|
5715434d5e | ||
|
|
ab2419db24 | ||
|
|
35a83c3514 | ||
|
|
de09cac1e9 | ||
|
|
b64f4bdb3a | ||
|
|
008310aad9 | ||
|
|
4d024d737c | ||
|
|
50edb30a18 | ||
|
|
00f6bd0f08 | ||
|
|
81e40a1172 | ||
|
|
fe4f89f3fa | ||
|
|
0d372250c1 | ||
|
|
367720020c | ||
|
|
0dab82085d | ||
|
|
cb211d63cd | ||
|
|
2e497cc471 | ||
|
|
587f6656a4 | ||
|
|
4377c03168 | ||
|
|
1e0520074c | ||
|
|
b9f6832347 | ||
|
|
f8bb0cd3d1 | ||
|
|
129aef758e | ||
|
|
94f55cf406 | ||
|
|
a20b758e19 | ||
|
|
128842becf | ||
|
|
d51331f51f | ||
|
|
4ef221a645 | ||
|
|
79010e972e | ||
|
|
0ca530e251 | ||
|
|
57b4d46dbc | ||
|
|
50df5e2f59 | ||
|
|
b6db2c7262 | ||
|
|
9441b69ad2 | ||
|
|
08e3d16a58 | ||
|
|
3f3049fdba | ||
|
|
41fb7a8a7e | ||
|
|
5b9828e094 | ||
|
|
96e35585b0 | ||
|
|
f035fe9336 | ||
|
|
44c2e33c78 | ||
|
|
2785dcbbbf | ||
|
|
9b03bd3243 | ||
|
|
8f30456ee3 | ||
|
|
38557ff635 | ||
|
|
7bc2cde4dd | ||
|
|
f065d3a06b | ||
|
|
21f7e3a987 | ||
|
|
9f688bc433 | ||
|
|
6e9d31d4fb | ||
|
|
e81c48526b | ||
|
|
b405985b80 | ||
|
|
8b010c5a9f | ||
|
|
1e7b5d6fdb | ||
|
|
a55982e7f0 | ||
|
|
1ac378063e | ||
|
|
d497c92684 | ||
|
|
677dc2c155 | ||
|
|
beea8deeb5 | ||
|
|
0d0f1a2fb2 | ||
|
|
439701ed7a | ||
|
|
d945d0129c | ||
|
|
cc91477308 | ||
|
|
ac139ec03d | ||
|
|
b4f0e834bf | ||
|
|
6e6cdc834f | ||
|
|
4b81ae1b35 | ||
|
|
d569dc45a8 | ||
|
|
23d49fda29 | ||
|
|
62ad2c3bc1 | ||
|
|
25373ad294 | ||
|
|
26d0c0fd8d | ||
|
|
3afffcd36b | ||
|
|
5440de1785 | ||
|
|
25a56f11c8 | ||
|
|
8271c6481f | ||
|
|
83e26f839d | ||
|
|
c3f7905d82 | ||
|
|
05b54a8d18 | ||
|
|
e5cfdd369e | ||
|
|
c61d8cfb85 | ||
|
|
27f4195471 | ||
|
|
b2596eda32 | ||
|
|
9379440fcb | ||
|
|
b7c502c8d1 | ||
|
|
3c16d2d9b4 | ||
|
|
6cbc03a418 | ||
|
|
15ecc0fa65 | ||
|
|
7f2f8cdad1 | ||
|
|
a64646cb7b | ||
|
|
05f531c538 | ||
|
|
7ca335446b | ||
|
|
99e96a6581 | ||
|
|
7200d8f90e | ||
|
|
7038837aca | ||
|
|
541dcc0e5a | ||
|
|
1060b5aabf | ||
|
|
6ea73c4982 | ||
|
|
cff847273a | ||
|
|
e669f8db9b | ||
|
|
461cc30842 | ||
|
|
13d7cb957c | ||
|
|
47a439a905 | ||
|
|
053fb96694 | ||
|
|
b72c1f7367 | ||
|
|
e90738575f | ||
|
|
35718eec9c | ||
|
|
8285a53152 | ||
|
|
116fe7815b | ||
|
|
4c2efd7da3 | ||
|
|
1a433e3185 | ||
|
|
fa98a00916 | ||
|
|
f4dd46ad60 | ||
|
|
41825ccfbb | ||
|
|
4d1a5f12c0 | ||
|
|
cc58d56c6d | ||
|
|
683ef07312 | ||
|
|
c77c78cef5 | ||
|
|
88517030a4 | ||
|
|
45471e8e63 | ||
|
|
4e620beb75 | ||
|
|
e7ab875b47 | ||
|
|
be74ed9ab6 | ||
|
|
52b2eacd37 | ||
|
|
6b4210a2ea | ||
|
|
740d0da5ee | ||
|
|
34883d356e | ||
|
|
aeafcce314 | ||
|
|
ae1d82c325 | ||
|
|
1c89a07226 | ||
|
|
43e262f13e | ||
|
|
e5f802ab5c | ||
|
|
847860fc5c | ||
|
|
77e936445f | ||
|
|
41beb74ef7 | ||
|
|
67be50b706 | ||
|
|
cd840b7c9d | ||
|
|
07903949ec | ||
|
|
d2156ddaad | ||
|
|
a84ff4b3ff | ||
|
|
c02f13d33f | ||
|
|
f6490859fd | ||
|
|
4b7816dbf4 | ||
|
|
86ffbc3ec5 | ||
|
|
67257f44a5 | ||
|
|
3086f5567a | ||
|
|
bb8acb095d | ||
|
|
24be3cbb5f | ||
|
|
9773191103 | ||
|
|
ddc703c3ec | ||
|
|
09ec204e4b | ||
|
|
8b6cafa0e0 | ||
|
|
1ee8be9d43 | ||
|
|
91bc190d21 | ||
|
|
4dc833b642 | ||
|
|
895e9f8d5c | ||
|
|
12906ff631 | ||
|
|
9c9bcff107 | ||
|
|
443ca5c63b | ||
|
|
1102b1fc4c | ||
|
|
0d089eab21 | ||
|
|
b44fada29e | ||
|
|
859ea160e6 | ||
|
|
1c49e2ffc0 | ||
|
|
140245c58d | ||
|
|
47adcced60 | ||
|
|
a2054deb12 | ||
|
|
30fdfd9ded | ||
|
|
a87a86eb92 | ||
|
|
49dfbcfbc8 | ||
|
|
c558a4f4f6 | ||
|
|
4f49a2248f | ||
|
|
4e57da42c1 | ||
|
|
4f6c9e206c | ||
|
|
699e1d4b77 | ||
|
|
fa2978b3b9 | ||
|
|
ffcdf741fc | ||
|
|
429d7f33d4 | ||
|
|
b6a229775e | ||
|
|
0a4580368a | ||
|
|
e5b191e1f1 | ||
|
|
a07acc067b | ||
|
|
01e763f879 | ||
|
|
a95e75261a | ||
|
|
dcaa7ac609 | ||
|
|
7d2ae10849 | ||
|
|
642cac3759 | ||
|
|
662af2edba | ||
|
|
6f97e15a8f | ||
|
|
ab2fa2ca9d | ||
|
|
4666c99b27 | ||
|
|
e38415c56d | ||
|
|
3f3fce4664 | ||
|
|
1407a7bc8a | ||
|
|
1d268f4b83 | ||
|
|
92aab79d27 | ||
|
|
0823bb1276 | ||
|
|
18e42efa01 | ||
|
|
0ea16328df | ||
|
|
b037efc5c6 | ||
|
|
839bb71389 | ||
|
|
2ef7df5cfe | ||
|
|
f5d2d79f75 | ||
|
|
8e5fa96ffc | ||
|
|
6dbbe07d50 | ||
|
|
3c23410753 | ||
|
|
8e35ab78b4 | ||
|
|
7f2911b7f0 | ||
|
|
d877e90301 | ||
|
|
93db4be726 | ||
|
|
1f419ad19f | ||
|
|
3c7f6508ad | ||
|
|
39449d4b63 | ||
|
|
28e05295fa | ||
|
|
60c18d3550 | ||
|
|
482ab186a2 | ||
|
|
4814195dc4 | ||
|
|
d215b7a10e | ||
|
|
0e52b78590 | ||
|
|
b3a9d607c4 | ||
|
|
c00081b62c | ||
|
|
5e4739b460 | ||
|
|
0d8f598df2 | ||
|
|
2ef04cc308 | ||
|
|
7a0884e2cd | ||
|
|
fa858530cc | ||
|
|
1c61fe5ed9 | ||
|
|
57409ef382 | ||
|
|
06ba826803 | ||
|
|
6e6ef9489f | ||
|
|
45bbe39d1f | ||
|
|
6c9fc13083 | ||
|
|
4c8a642388 | ||
|
|
6208c9d64f | ||
|
|
76325d0ba9 | ||
|
|
cf1802a6de | ||
|
|
538a7b12bd | ||
|
|
57e0f52aaa | ||
|
|
1ae0335b62 | ||
|
|
ca1bff6b66 | ||
|
|
10cc62d2ca | ||
|
|
dd451a8a00 | ||
|
|
444ec1e412 | ||
|
|
82739f4d7d | ||
|
|
19825d853e | ||
|
|
d8ece2e3e9 | ||
|
|
bf4deb0ce6 | ||
|
|
da4739a26c | ||
|
|
fc24bf0915 | ||
|
|
9329b99cba |
11
.github/workflows/android.yml
vendored
11
.github/workflows/android.yml
vendored
@@ -28,8 +28,17 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends gettext openjdk-11-jdk-headless
|
||||
- name: Build with Gradle
|
||||
- name: Build AAB with Gradle
|
||||
# We build an AAB as well for uploading to the the Play Store.
|
||||
run: cd android; ./gradlew bundlerelease
|
||||
- name: Build APKs with Gradle
|
||||
# "assemblerelease" is very fast after "bundlerelease".
|
||||
run: cd android; ./gradlew assemblerelease
|
||||
- name: Save AAB artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Minetest-release.aab
|
||||
path: android/app/build/outputs/bundle/release/app-release.aab
|
||||
- name: Save armeabi artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
|
||||
7
.github/workflows/build.yml
vendored
7
.github/workflows/build.yml
vendored
@@ -83,7 +83,7 @@ jobs:
|
||||
- name: Install deps
|
||||
run: |
|
||||
source ./util/ci/common.sh
|
||||
install_linux_deps clang-7 valgrind
|
||||
install_linux_deps clang-7 llvm
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
@@ -91,15 +91,12 @@ jobs:
|
||||
env:
|
||||
CC: clang-7
|
||||
CXX: clang++-7
|
||||
CMAKE_FLAGS: '-DCMAKE_C_FLAGS="-fsanitize=address" -DCMAKE_CXX_FLAGS="-fsanitize=address"'
|
||||
|
||||
- name: Unittest
|
||||
run: |
|
||||
./bin/minetest --run-unittests
|
||||
|
||||
- name: Valgrind
|
||||
run: |
|
||||
valgrind --leak-check=full --leak-check-heuristics=all --undef-value-errors=no --error-exitcode=9 ./bin/minetest --run-unittests
|
||||
|
||||
# Current clang version
|
||||
clang_14:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# Set policies up to 3.9 since we want to enable the IPO option
|
||||
if(${CMAKE_VERSION} VERSION_LESS 3.9)
|
||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||
else()
|
||||
cmake_policy(VERSION 3.9)
|
||||
endif()
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
|
||||
# This can be read from ${PROJECT_NAME} after project() is called
|
||||
project(minetest)
|
||||
@@ -19,7 +12,7 @@ set(CLANG_MINIMUM_VERSION "7.0.1")
|
||||
# You should not need to edit these manually, use util/bump_version.sh
|
||||
set(VERSION_MAJOR 5)
|
||||
set(VERSION_MINOR 8)
|
||||
set(VERSION_PATCH 0)
|
||||
set(VERSION_PATCH 1)
|
||||
set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string")
|
||||
|
||||
# Change to false for releases
|
||||
@@ -44,6 +37,13 @@ set(BUILD_UNITTESTS TRUE CACHE BOOL "Build unittests")
|
||||
set(BUILD_BENCHMARKS FALSE CACHE BOOL "Build benchmarks")
|
||||
set(BUILD_DOCUMENTATION TRUE CACHE BOOL "Build documentation")
|
||||
|
||||
set(DEFAULT_ENABLE_LTO TRUE)
|
||||
# by default don't enable on Debug builds to get faster builds
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(DEFAULT_ENABLE_LTO FALSE)
|
||||
endif()
|
||||
set(ENABLE_LTO ${DEFAULT_ENABLE_LTO} CACHE BOOL "Use Link Time Optimization")
|
||||
|
||||
set(DEFAULT_RUN_IN_PLACE FALSE)
|
||||
if(WIN32)
|
||||
set(DEFAULT_RUN_IN_PLACE TRUE)
|
||||
@@ -66,6 +66,20 @@ if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type: Debug or Release" FORCE)
|
||||
endif()
|
||||
|
||||
# FIXME: Windows build fails in multiple places to link, needs to be investigated.
|
||||
if (ENABLE_LTO AND NOT WIN32)
|
||||
include(CheckIPOSupported)
|
||||
check_ipo_supported(RESULT lto_supported OUTPUT lto_output)
|
||||
if(lto_supported)
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||
message(STATUS "LTO/IPO is enabled")
|
||||
else()
|
||||
message(STATUS "LTO/IPO requested but it is not supported by the compiler: ${lto_output}")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "LTO/IPO is not enabled")
|
||||
endif()
|
||||
|
||||
set(ENABLE_UPDATE_CHECKER (NOT ${DEVELOPMENT_BUILD}) CACHE BOOL
|
||||
"Whether to enable update checks by default")
|
||||
|
||||
|
||||
@@ -80,6 +80,17 @@ public class GameActivity extends NativeActivity {
|
||||
makeFullScreen();
|
||||
}
|
||||
|
||||
private native void saveSettings();
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
// Avoid losing setting changes in case the app is onDestroy()ed later.
|
||||
// Saving stuff in onStop() is recommended in the Android activity
|
||||
// lifecycle documentation.
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
// Ignore the back press so Minetest can handle it
|
||||
|
||||
@@ -20,23 +20,29 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
||||
package net.minetest.minetest;
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import static net.minetest.minetest.UnzipService.*;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
public static final String NOTIFICATION_CHANNEL_ID = "Minetest channel";
|
||||
|
||||
private final static int versionCode = BuildConfig.VERSION_CODE;
|
||||
private static final String SETTINGS = "MinetestSettings";
|
||||
private static final String TAG_VERSION_CODE = "versionCode";
|
||||
@@ -81,12 +87,18 @@ public class MainActivity extends AppCompatActivity {
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
IntentFilter filter = new IntentFilter(ACTION_UPDATE);
|
||||
registerReceiver(myReceiver, filter);
|
||||
|
||||
mProgressBar = findViewById(R.id.progressBar);
|
||||
mTextView = findViewById(R.id.textView);
|
||||
sharedPreferences = getSharedPreferences(SETTINGS, Context.MODE_PRIVATE);
|
||||
|
||||
checkAppVersion();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
createNotificationChannel();
|
||||
}
|
||||
|
||||
private void checkAppVersion() {
|
||||
@@ -114,6 +126,28 @@ public class MainActivity extends AppCompatActivity {
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
private void createNotificationChannel() {
|
||||
NotificationManager notifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (notifyManager == null)
|
||||
return;
|
||||
|
||||
NotificationChannel notifyChannel = new NotificationChannel(
|
||||
NOTIFICATION_CHANNEL_ID,
|
||||
getString(R.string.notification_channel_name),
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
);
|
||||
notifyChannel.setDescription(getString(R.string.notification_channel_description));
|
||||
// Configure the notification channel without sound set
|
||||
notifyChannel.setSound(null, null);
|
||||
notifyChannel.enableLights(false);
|
||||
notifyChannel.enableVibration(false);
|
||||
|
||||
// It is fine to always create the notification channel because creating a channel
|
||||
// with the same ID is the same as overriding it (only its name and description).
|
||||
notifyManager.createNotificationChannel(notifyChannel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
// Prevent abrupt interruption when copy game files from assets
|
||||
|
||||
@@ -22,7 +22,6 @@ package net.minetest.minetest;
|
||||
|
||||
import android.app.IntentService;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
@@ -58,9 +57,11 @@ public class UnzipService extends IntentService {
|
||||
private String failureMessage;
|
||||
|
||||
private static boolean isRunning = false;
|
||||
|
||||
public static synchronized boolean getIsRunning() {
|
||||
return isRunning;
|
||||
}
|
||||
|
||||
private static synchronized void setIsRunning(boolean v) {
|
||||
isRunning = v;
|
||||
}
|
||||
@@ -99,28 +100,13 @@ public class UnzipService extends IntentService {
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Notification.Builder createNotification() {
|
||||
String name = "net.minetest.minetest";
|
||||
String channelId = "Minetest channel";
|
||||
String description = "notifications from Minetest";
|
||||
Notification.Builder builder;
|
||||
if (mNotifyManager == null)
|
||||
mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
int importance = NotificationManager.IMPORTANCE_LOW;
|
||||
NotificationChannel mChannel = null;
|
||||
if (mNotifyManager != null)
|
||||
mChannel = mNotifyManager.getNotificationChannel(channelId);
|
||||
if (mChannel == null) {
|
||||
mChannel = new NotificationChannel(channelId, name, importance);
|
||||
mChannel.setDescription(description);
|
||||
// Configure the notification channel, NO SOUND
|
||||
mChannel.setSound(null, null);
|
||||
mChannel.enableLights(false);
|
||||
mChannel.enableVibration(false);
|
||||
mNotifyManager.createNotificationChannel(mChannel);
|
||||
}
|
||||
builder = new Notification.Builder(this, channelId);
|
||||
builder = new Notification.Builder(this, MainActivity.NOTIFICATION_CHANNEL_ID);
|
||||
} else {
|
||||
builder = new Notification.Builder(this);
|
||||
}
|
||||
@@ -135,9 +121,9 @@ public class UnzipService extends IntentService {
|
||||
PendingIntent intent = PendingIntent.getActivity(this, 0,
|
||||
notificationIntent, pendingIntentFlag);
|
||||
|
||||
builder.setContentTitle(getString(R.string.notification_title))
|
||||
builder.setContentTitle(getString(R.string.unzip_notification_title))
|
||||
.setSmallIcon(R.mipmap.ic_launcher)
|
||||
.setContentText(getString(R.string.notification_description))
|
||||
.setContentText(getString(R.string.unzip_notification_description))
|
||||
.setContentIntent(intent)
|
||||
.setOngoing(true)
|
||||
.setProgress(0, 0, true);
|
||||
@@ -198,7 +184,7 @@ public class UnzipService extends IntentService {
|
||||
}
|
||||
}
|
||||
|
||||
private void publishProgress(@Nullable Notification.Builder notificationBuilder, @StringRes int message, int progress) {
|
||||
private void publishProgress(@Nullable Notification.Builder notificationBuilder, @StringRes int message, int progress) {
|
||||
Intent intentUpdate = new Intent(ACTION_UPDATE);
|
||||
intentUpdate.putExtra(ACTION_PROGRESS, progress);
|
||||
intentUpdate.putExtra(ACTION_PROGRESS_MESSAGE, message);
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
<resources>
|
||||
<string name="label">Minetest</string>
|
||||
<string name="loading">Loading…</string>
|
||||
<string name="notification_title">Loading Minetest</string>
|
||||
<string name="notification_description">Less than 1 minute…</string>
|
||||
<string name="notification_channel_name">General notification</string>
|
||||
<string name="notification_channel_description">Notifications from Minetest</string>
|
||||
<string name="unzip_notification_title">Loading Minetest</string>
|
||||
<string name="unzip_notification_description">Less than 1 minute…</string>
|
||||
<string name="ime_dialog_done">Done</string>
|
||||
</resources>
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
project.ext.set("versionMajor", 5) // Version Major
|
||||
project.ext.set("versionMinor", 8) // Version Minor
|
||||
project.ext.set("versionPatch", 0) // Version Patch
|
||||
project.ext.set("versionPatch", 1) // Version Patch
|
||||
// ^ keep in sync with cmake
|
||||
project.ext.set("versionCode", 46) // Android Version Code
|
||||
project.ext.set("versionCode", 48) // Android Version Code
|
||||
// NOTE: +2 after each release!
|
||||
// +1 for ARM and +1 for ARM64 APK's, because
|
||||
// each APK must have a larger `versionCode` than the previous
|
||||
|
||||
@@ -43,19 +43,25 @@ android {
|
||||
}
|
||||
|
||||
// get precompiled deps
|
||||
task downloadDeps(type: Download) {
|
||||
def depsDir = new File(buildDir.parent, 'deps')
|
||||
def depsZip = new File(buildDir, 'deps.zip')
|
||||
def depsDir = new File(buildDir.parent, 'deps')
|
||||
if (new File(depsDir, 'armeabi-v7a').exists()) {
|
||||
task getDeps {
|
||||
doLast { logger.lifecycle('Using existing deps from {}', depsDir) }
|
||||
}
|
||||
} else {
|
||||
task downloadDeps(type: Download) {
|
||||
def depsZip = new File(buildDir, 'deps.zip')
|
||||
|
||||
src 'https://github.com/minetest/minetest_android_deps/releases/download/latest/deps.zip'
|
||||
dest depsZip
|
||||
overwrite false
|
||||
src 'https://github.com/minetest/minetest_android_deps/releases/download/5.8.0/deps.zip'
|
||||
dest depsZip
|
||||
overwrite false
|
||||
|
||||
task getDeps(dependsOn: downloadDeps, type: Copy) {
|
||||
depsDir.mkdir()
|
||||
from zipTree(depsZip)
|
||||
into depsDir
|
||||
doFirst { logger.lifecycle('Extracting to {}', depsDir) }
|
||||
task getDeps(dependsOn: downloadDeps, type: Copy) {
|
||||
depsDir.mkdir()
|
||||
from zipTree(depsZip)
|
||||
into depsDir
|
||||
doFirst { logger.lifecycle('Extracting to {}', depsDir) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -608,6 +608,16 @@ local function get_formspec(dialogdata)
|
||||
end
|
||||
|
||||
|
||||
-- On Android, closing the app via the "Recents screen" won't result in a clean
|
||||
-- exit, discarding any setting changes made by the user.
|
||||
-- To avoid that, we write the settings file in more cases on Android.
|
||||
function write_settings_early()
|
||||
if PLATFORM == "Android" then
|
||||
core.settings:write()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function buttonhandler(this, fields)
|
||||
local dialogdata = this.data
|
||||
dialogdata.leftscroll = core.explode_scrollbar_event(fields.leftscroll).value or dialogdata.leftscroll
|
||||
@@ -622,12 +632,15 @@ local function buttonhandler(this, fields)
|
||||
if fields.show_technical_names ~= nil then
|
||||
local value = core.is_yes(fields.show_technical_names)
|
||||
core.settings:set_bool("show_technical_names", value)
|
||||
write_settings_early()
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
if fields.show_advanced ~= nil then
|
||||
local value = core.is_yes(fields.show_advanced)
|
||||
core.settings:set_bool("show_advanced", value)
|
||||
write_settings_early()
|
||||
|
||||
local suggested_page_id = update_filtered_pages(dialogdata.query)
|
||||
|
||||
@@ -672,12 +685,15 @@ local function buttonhandler(this, fields)
|
||||
|
||||
for i, comp in ipairs(dialogdata.components) do
|
||||
if comp.on_submit and comp:on_submit(fields, this) then
|
||||
write_settings_early()
|
||||
|
||||
-- Clear components so they regenerate
|
||||
dialogdata.components = nil
|
||||
return true
|
||||
end
|
||||
if comp.setting and fields["reset_" .. i] then
|
||||
core.settings:remove(comp.setting.name)
|
||||
write_settings_early()
|
||||
|
||||
-- Clear components so they regenerate
|
||||
dialogdata.components = nil
|
||||
|
||||
@@ -401,7 +401,8 @@ anisotropic_filter (Anisotropic filtering) bool false
|
||||
#
|
||||
# * None - No antialiasing (default)
|
||||
#
|
||||
# * FSAA - Hardware-provided full-screen antialiasing (incompatible with shaders)
|
||||
# * FSAA - Hardware-provided full-screen antialiasing
|
||||
# (incompatible with Post Processing and Undersampling)
|
||||
# A.K.A multi-sample antialiasing (MSAA)
|
||||
# Smoothens out block edges but does not affect the insides of textures.
|
||||
# A restart is required to change this option.
|
||||
@@ -557,12 +558,17 @@ shadow_sky_body_orbit_tilt (Sky Body Orbit Tilt) float 0.0 -60.0 60.0
|
||||
|
||||
[**Post Processing]
|
||||
|
||||
# Enables the post processing pipeline.
|
||||
#
|
||||
# Requires: shaders
|
||||
enable_post_processing (Enable Post Processing) bool true
|
||||
|
||||
# Enables Hable's 'Uncharted 2' filmic tone mapping.
|
||||
# Simulates the tone curve of photographic film and how this approximates the
|
||||
# appearance of high dynamic range images. Mid-range contrast is slightly
|
||||
# enhanced, highlights and shadows are gradually compressed.
|
||||
#
|
||||
# Requires: shaders
|
||||
# Requires: shaders, enable_post_processing
|
||||
tone_mapping (Filmic tone mapping) bool false
|
||||
|
||||
# Enable automatic exposure correction
|
||||
@@ -570,14 +576,14 @@ tone_mapping (Filmic tone mapping) bool false
|
||||
# automatically adjust to the brightness of the scene,
|
||||
# simulating the behavior of human eye.
|
||||
#
|
||||
# Requires: shaders
|
||||
# Requires: shaders, enable_post_processing
|
||||
enable_auto_exposure (Enable Automatic Exposure) bool false
|
||||
|
||||
# Set the exposure compensation in EV units.
|
||||
# Value of 0.0 (default) means no exposure compensation.
|
||||
# Range: from -1 to 1.0
|
||||
#
|
||||
# Requires: shaders, enable_auto_exposure
|
||||
# Requires: shaders, enable_post_processing, enable_auto_exposure
|
||||
exposure_compensation (Exposure compensation) float 0.0 -1.0 1.0
|
||||
|
||||
[**Bloom]
|
||||
@@ -585,7 +591,7 @@ exposure_compensation (Exposure compensation) float 0.0 -1.0 1.0
|
||||
# Set to true to enable bloom effect.
|
||||
# Bright colors will bleed over the neighboring objects.
|
||||
#
|
||||
# Requires: shaders
|
||||
# Requires: shaders, enable_post_processing
|
||||
enable_bloom (Enable Bloom) bool false
|
||||
|
||||
# Set to true to render debugging breakdown of the bloom effect.
|
||||
@@ -593,27 +599,27 @@ enable_bloom (Enable Bloom) bool false
|
||||
# top-left - processed base image, top-right - final image
|
||||
# bottom-left - raw base image, bottom-right - bloom texture.
|
||||
#
|
||||
# Requires: shaders, enable_bloom
|
||||
# Requires: shaders, enable_post_processing, enable_bloom
|
||||
enable_bloom_debug (Enable Bloom Debug) bool false
|
||||
|
||||
# Defines how much bloom is applied to the rendered image
|
||||
# Smaller values make bloom more subtle
|
||||
# Range: from 0.01 to 1.0, default: 0.05
|
||||
#
|
||||
# Requires: shaders, enable_bloom
|
||||
# Requires: shaders, enable_post_processing, enable_bloom
|
||||
bloom_intensity (Bloom Intensity) float 0.05 0.01 1.0
|
||||
|
||||
# Defines the magnitude of bloom overexposure.
|
||||
# Range: from 0.1 to 10.0, default: 1.0
|
||||
#
|
||||
# Requires: shaders, enable_bloom
|
||||
# Requires: shaders, enable_post_processing, enable_bloom
|
||||
bloom_strength_factor (Bloom Strength Factor) float 1.0 0.1 10.0
|
||||
|
||||
# Logical value that controls how far the bloom effect spreads
|
||||
# from the bright objects.
|
||||
# Range: from 0.1 to 8, default: 1
|
||||
#
|
||||
# Requires: shaders, enable_bloom
|
||||
# Requires: shaders, enable_post_processing, enable_bloom
|
||||
bloom_radius (Bloom Radius) float 1 0.1 8
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Minetest Lua Client Modding API Reference 5.8.0
|
||||
Minetest Lua Client Modding API Reference 5.8.1
|
||||
================================================
|
||||
* More information at <http://www.minetest.net/>
|
||||
* Developer Wiki: <http://dev.minetest.net/>
|
||||
|
||||
@@ -28,6 +28,7 @@ General options and their default values:
|
||||
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_LTO=ON - Build with IPO/LTO optimizations (smaller and more efficient than regular build)
|
||||
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)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Minetest Lua Mainmenu API Reference 5.8.0
|
||||
Minetest Lua Mainmenu API Reference 5.8.1
|
||||
=========================================
|
||||
|
||||
Introduction
|
||||
|
||||
@@ -7,6 +7,10 @@ unittests.register("test_get_version", function()
|
||||
assert(type(version.proto_min) == "number")
|
||||
assert(type(version.proto_max) == "number")
|
||||
assert(version.proto_max >= version.proto_min)
|
||||
assert(type(version.hash) == "string")
|
||||
assert(type(version.is_dev) == "boolean")
|
||||
if version.is_dev then
|
||||
assert(type(version.hash) == "string")
|
||||
else
|
||||
assert(version.hash == nil)
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -82,6 +82,6 @@
|
||||
<translation type="gettext">minetest</translation>
|
||||
<update_contact>sfan5@live.de</update_contact>
|
||||
<releases>
|
||||
<release date="2023-12-04" version="5.8.0"/>
|
||||
<release date="2024-04-21" version="5.8.1"/>
|
||||
</releases>
|
||||
</component>
|
||||
|
||||
@@ -1777,6 +1777,11 @@ float Client::mediaReceiveProgress()
|
||||
return 1.0; // downloader only exists when not yet done
|
||||
}
|
||||
|
||||
void Client::drawLoadScreen(const std::wstring &text, float dtime, int percent) {
|
||||
m_rendering_engine->run();
|
||||
m_rendering_engine->draw_load_screen(text, guienv, m_tsrc, dtime, percent);
|
||||
}
|
||||
|
||||
struct TextureUpdateArgs {
|
||||
gui::IGUIEnvironment *guienv;
|
||||
u64 last_time_ms;
|
||||
|
||||
@@ -357,6 +357,7 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
|
||||
|
||||
float mediaReceiveProgress();
|
||||
|
||||
void drawLoadScreen(const std::wstring &text, float dtime, int percent);
|
||||
void afterContentReceived();
|
||||
void showUpdateProgressTexture(void *args, u32 progress, u32 max_progress);
|
||||
|
||||
|
||||
@@ -215,8 +215,9 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args)
|
||||
L" " + utf8_to_wide(g_version_hash) +
|
||||
L" [" + wstrgettext("Main Menu") + L"]").c_str());
|
||||
|
||||
try { // This is used for catching disconnects
|
||||
|
||||
#ifdef NDEBUG
|
||||
try {
|
||||
#endif
|
||||
m_rendering_engine->get_gui_env()->clear();
|
||||
|
||||
/*
|
||||
@@ -247,11 +248,8 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args)
|
||||
}
|
||||
|
||||
// Break out of menu-game loop to shut down cleanly
|
||||
if (!m_rendering_engine->run() || *kill) {
|
||||
if (!g_settings_path.empty())
|
||||
g_settings->updateConfigFile(g_settings_path.c_str());
|
||||
if (!m_rendering_engine->run() || *kill)
|
||||
break;
|
||||
}
|
||||
|
||||
m_rendering_engine->get_video_driver()->setTextureCreationFlag(
|
||||
video::ETCF_CREATE_MIP_MAPS, g_settings->getBool("mip_map"));
|
||||
@@ -270,14 +268,8 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args)
|
||||
chat_backend,
|
||||
&reconnect_requested
|
||||
);
|
||||
} //try
|
||||
catch (con::PeerNotFoundException &e) {
|
||||
error_message = gettext("Connection error (timed out?)");
|
||||
errorstream << error_message << std::endl;
|
||||
}
|
||||
|
||||
#ifdef NDEBUG
|
||||
catch (std::exception &e) {
|
||||
} catch (std::exception &e) {
|
||||
error_message = "Some exception: ";
|
||||
error_message.append(debug_describe_exc(e));
|
||||
errorstream << error_message << std::endl;
|
||||
@@ -292,6 +284,16 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args)
|
||||
receiver->m_touchscreengui = NULL;
|
||||
#endif
|
||||
|
||||
/* Save the settings when leaving the game.
|
||||
* This makes sure that setting changes made in-game are persisted even
|
||||
* in case of a later unclean exit from the mainmenu.
|
||||
* This is especially useful on Android because closing the app from the
|
||||
* "Recents screen" results in an unclean exit.
|
||||
* Caveat: This means that the settings are saved twice when exiting Minetest.
|
||||
*/
|
||||
if (!g_settings_path.empty())
|
||||
g_settings->updateConfigFile(g_settings_path.c_str());
|
||||
|
||||
// If no main menu, show error and exit
|
||||
if (skip_main_menu) {
|
||||
if (!error_message.empty()) {
|
||||
@@ -555,6 +557,16 @@ void ClientLauncher::main_menu(MainMenuData *menudata)
|
||||
|
||||
/* leave scene manager in a clean state */
|
||||
m_rendering_engine->get_scene_manager()->clear();
|
||||
|
||||
/* Save the settings when leaving the mainmenu.
|
||||
* This makes sure that setting changes made in the mainmenu are persisted
|
||||
* even in case of a later unclean exit from the game.
|
||||
* This is especially useful on Android because closing the app from the
|
||||
* "Recents screen" results in an unclean exit.
|
||||
* Caveat: This means that the settings are saved twice when exiting Minetest.
|
||||
*/
|
||||
if (!g_settings_path.empty())
|
||||
g_settings->updateConfigFile(g_settings_path.c_str());
|
||||
}
|
||||
|
||||
void ClientLauncher::speed_tests()
|
||||
|
||||
@@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
*/
|
||||
|
||||
#include "clientmedia.h"
|
||||
#include "gettext.h"
|
||||
#include "httpfetch.h"
|
||||
#include "client.h"
|
||||
#include "filecache.h"
|
||||
@@ -29,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "util/serialize.h"
|
||||
#include "util/sha1.h"
|
||||
#include "util/string.h"
|
||||
#include <sstream>
|
||||
|
||||
static std::string getMediaCacheDir()
|
||||
{
|
||||
@@ -41,7 +43,16 @@ bool clientMediaUpdateCache(const std::string &raw_hash, const std::string &file
|
||||
std::string sha1_hex = hex_encode(raw_hash);
|
||||
if (!media_cache.exists(sha1_hex))
|
||||
return media_cache.update(sha1_hex, filedata);
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool clientMediaUpdateCacheCopy(const std::string &raw_hash, const std::string &path)
|
||||
{
|
||||
FileCache media_cache(getMediaCacheDir());
|
||||
std::string sha1_hex = hex_encode(raw_hash);
|
||||
if (!media_cache.exists(sha1_hex))
|
||||
return media_cache.updateCopyFile(sha1_hex, path);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -174,6 +185,11 @@ void ClientMediaDownloader::step(Client *client)
|
||||
|
||||
void ClientMediaDownloader::initialStep(Client *client)
|
||||
{
|
||||
std::wstring loading_text = wstrgettext("Media...");
|
||||
// Tradeoff between responsiveness during media loading and media loading speed
|
||||
const u64 chunk_time_ms = 33;
|
||||
u64 last_time = porting::getTimeMs();
|
||||
|
||||
// Check media cache
|
||||
m_uncached_count = m_files.size();
|
||||
for (auto &file_it : m_files) {
|
||||
@@ -185,14 +201,17 @@ void ClientMediaDownloader::initialStep(Client *client)
|
||||
filestatus->received = true;
|
||||
m_uncached_count--;
|
||||
}
|
||||
|
||||
u64 cur_time = porting::getTimeMs();
|
||||
u64 dtime = porting::getDeltaMs(last_time, cur_time);
|
||||
if (dtime >= chunk_time_ms) {
|
||||
client->drawLoadScreen(loading_text, dtime / 1000.0f, 30);
|
||||
last_time = cur_time;
|
||||
}
|
||||
}
|
||||
|
||||
assert(m_uncached_received_count == 0);
|
||||
|
||||
// Create the media cache dir if we are likely to write to it
|
||||
if (m_uncached_count != 0)
|
||||
createCacheDirs();
|
||||
|
||||
// If we found all files in the cache, report this fact to the server.
|
||||
// If the server reported no remote servers, immediately start
|
||||
// conventional transfers. Note: if cURL support is not compiled in,
|
||||
@@ -511,18 +530,6 @@ IClientMediaDownloader::IClientMediaDownloader():
|
||||
{
|
||||
}
|
||||
|
||||
void IClientMediaDownloader::createCacheDirs()
|
||||
{
|
||||
if (!m_write_to_cache)
|
||||
return;
|
||||
|
||||
std::string path = getMediaCacheDir();
|
||||
if (!fs::CreateAllDirs(path)) {
|
||||
errorstream << "Client: Could not create media cache directory: "
|
||||
<< path << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
bool IClientMediaDownloader::tryLoadFromCache(const std::string &name,
|
||||
const std::string &sha1, Client *client)
|
||||
{
|
||||
@@ -726,8 +733,6 @@ void SingleMediaDownloader::initialStep(Client *client)
|
||||
if (isDone())
|
||||
return;
|
||||
|
||||
createCacheDirs();
|
||||
|
||||
// If the server reported no remote servers, immediately fall back to
|
||||
// conventional transfer.
|
||||
if (!USE_CURL || m_remotes.empty()) {
|
||||
|
||||
@@ -22,7 +22,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "irrlichttypes.h"
|
||||
#include "filecache.h"
|
||||
#include "util/basic_macros.h"
|
||||
#include <ostream>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
@@ -35,10 +34,15 @@ struct HTTPFetchResult;
|
||||
#define MTHASHSET_FILE_NAME "index.mth"
|
||||
|
||||
// Store file into media cache (unless it exists already)
|
||||
// Validating the hash is responsibility of the caller
|
||||
// Caller should check the hash.
|
||||
// return true if something was updated
|
||||
bool clientMediaUpdateCache(const std::string &raw_hash,
|
||||
const std::string &filedata);
|
||||
|
||||
// Copy file on disk(!) into media cache (unless it exists already)
|
||||
bool clientMediaUpdateCacheCopy(const std::string &raw_hash,
|
||||
const std::string &path);
|
||||
|
||||
// more of a base class than an interface but this name was most convenient...
|
||||
class IClientMediaDownloader
|
||||
{
|
||||
@@ -81,8 +85,6 @@ class IClientMediaDownloader
|
||||
virtual bool loadMedia(Client *client, const std::string &data,
|
||||
const std::string &name) = 0;
|
||||
|
||||
void createCacheDirs();
|
||||
|
||||
bool tryLoadFromCache(const std::string &name, const std::string &sha1,
|
||||
Client *client);
|
||||
|
||||
|
||||
@@ -28,6 +28,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include <fstream>
|
||||
#include <cstdlib>
|
||||
|
||||
void FileCache::createDir()
|
||||
{
|
||||
if (!fs::CreateAllDirs(m_dir)) {
|
||||
errorstream << "Could not create cache directory: "
|
||||
<< m_dir << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
bool FileCache::loadByPath(const std::string &path, std::ostream &os)
|
||||
{
|
||||
std::ifstream fis(path.c_str(), std::ios_base::binary);
|
||||
@@ -40,8 +48,8 @@ bool FileCache::loadByPath(const std::string &path, std::ostream &os)
|
||||
|
||||
bool bad = false;
|
||||
for(;;){
|
||||
char buf[1024];
|
||||
fis.read(buf, 1024);
|
||||
char buf[4096];
|
||||
fis.read(buf, sizeof(buf));
|
||||
std::streamsize len = fis.gcount();
|
||||
os.write(buf, len);
|
||||
if(fis.eof())
|
||||
@@ -61,6 +69,7 @@ bool FileCache::loadByPath(const std::string &path, std::ostream &os)
|
||||
|
||||
bool FileCache::updateByPath(const std::string &path, const std::string &data)
|
||||
{
|
||||
createDir();
|
||||
std::ofstream file(path.c_str(), std::ios_base::binary |
|
||||
std::ios_base::trunc);
|
||||
|
||||
@@ -95,3 +104,11 @@ bool FileCache::exists(const std::string &name)
|
||||
std::ifstream fis(path.c_str(), std::ios_base::binary);
|
||||
return fis.good();
|
||||
}
|
||||
|
||||
bool FileCache::updateCopyFile(const std::string &name, const std::string &src_path)
|
||||
{
|
||||
std::string path = m_dir + DIR_DELIM + name;
|
||||
|
||||
createDir();
|
||||
return fs::CopyFileContents(src_path, path);
|
||||
}
|
||||
|
||||
@@ -35,9 +35,13 @@ class FileCache
|
||||
bool load(const std::string &name, std::ostream &os);
|
||||
bool exists(const std::string &name);
|
||||
|
||||
// Copy another file on disk into the cache
|
||||
bool updateCopyFile(const std::string &name, const std::string &src_path);
|
||||
|
||||
private:
|
||||
std::string m_dir;
|
||||
|
||||
void createDir();
|
||||
bool loadByPath(const std::string &path, std::ostream &os);
|
||||
bool updateByPath(const std::string &path, const std::string &data);
|
||||
};
|
||||
|
||||
@@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "client/mapblock_mesh.h"
|
||||
#include "client/sound.h"
|
||||
#include "clientmap.h"
|
||||
#include "clientmedia.h" // For clientMediaUpdateCacheCopy
|
||||
#include "clouds.h"
|
||||
#include "config.h"
|
||||
#include "content_cao.h"
|
||||
@@ -63,6 +64,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "settings.h"
|
||||
#include "shader.h"
|
||||
#include "sky.h"
|
||||
#include "threading/lambda.h"
|
||||
#include "translation.h"
|
||||
#include "util/basic_macros.h"
|
||||
#include "util/directiontables.h"
|
||||
@@ -737,6 +739,7 @@ class Game {
|
||||
bool initSound();
|
||||
bool createSingleplayerServer(const std::string &map_dir,
|
||||
const SubgameSpec &gamespec, u16 port);
|
||||
void copyServerClientCache();
|
||||
|
||||
// Client creation
|
||||
bool createClient(const GameStartData &start_data);
|
||||
@@ -1058,7 +1061,7 @@ Game::~Game()
|
||||
delete soundmaker;
|
||||
sound_manager.reset();
|
||||
|
||||
delete server; // deleted first to stop all server threads
|
||||
delete server;
|
||||
|
||||
delete hud;
|
||||
delete camera;
|
||||
@@ -1272,6 +1275,9 @@ void Game::shutdown()
|
||||
if (formspec)
|
||||
formspec->quitMenu();
|
||||
|
||||
// Clear text when exiting.
|
||||
m_game_ui->clearText();
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
g_touchscreengui->hide();
|
||||
#endif
|
||||
@@ -1309,6 +1315,31 @@ void Game::shutdown()
|
||||
sleep_ms(100);
|
||||
}
|
||||
}
|
||||
|
||||
delete client;
|
||||
client = nullptr;
|
||||
delete soundmaker;
|
||||
soundmaker = nullptr;
|
||||
sound_manager.reset();
|
||||
|
||||
auto stop_thread = runInThread([=] {
|
||||
delete server;
|
||||
server = nullptr;
|
||||
}, "ServerStop");
|
||||
|
||||
FpsControl fps_control;
|
||||
fps_control.reset();
|
||||
|
||||
while (stop_thread->isRunning()) {
|
||||
m_rendering_engine->run();
|
||||
f32 dtime;
|
||||
fps_control.limit(device, &dtime);
|
||||
showOverlayMessage(N_("Shutting down..."), dtime, 0, false);
|
||||
}
|
||||
|
||||
stop_thread->rethrow();
|
||||
|
||||
// to be continued in Game::~Game
|
||||
}
|
||||
|
||||
|
||||
@@ -1414,9 +1445,53 @@ bool Game::createSingleplayerServer(const std::string &map_dir,
|
||||
|
||||
server = new Server(map_dir, gamespec, simple_singleplayer_mode, bind_addr,
|
||||
false, nullptr, error_message);
|
||||
server->start();
|
||||
|
||||
return true;
|
||||
auto start_thread = runInThread([=] {
|
||||
server->start();
|
||||
copyServerClientCache();
|
||||
}, "ServerStart");
|
||||
|
||||
input->clear();
|
||||
bool success = true;
|
||||
|
||||
FpsControl fps_control;
|
||||
fps_control.reset();
|
||||
|
||||
while (start_thread->isRunning()) {
|
||||
if (!m_rendering_engine->run() || input->cancelPressed())
|
||||
success = false;
|
||||
f32 dtime;
|
||||
fps_control.limit(device, &dtime);
|
||||
|
||||
if (success)
|
||||
showOverlayMessage(N_("Creating server..."), dtime, 5);
|
||||
else
|
||||
showOverlayMessage(N_("Shutting down..."), dtime, 0, false);
|
||||
}
|
||||
|
||||
start_thread->rethrow();
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void Game::copyServerClientCache()
|
||||
{
|
||||
// It would be possible to let the client directly read the media files
|
||||
// from where the server knows they are. But aside from being more complicated
|
||||
// it would also *not* fill the media cache and cause slower joining of
|
||||
// remote servers.
|
||||
// (Imagine that you launch a game once locally and then connect to a server.)
|
||||
|
||||
assert(server);
|
||||
auto map = server->getMediaList();
|
||||
u32 n = 0;
|
||||
for (auto &it : map) {
|
||||
assert(it.first.size() == 20); // SHA1
|
||||
if (clientMediaUpdateCacheCopy(it.first, it.second))
|
||||
n++;
|
||||
}
|
||||
infostream << "Copied " << n << " files directly from server to client cache"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
bool Game::createClient(const GameStartData &start_data)
|
||||
@@ -1428,12 +1503,6 @@ bool Game::createClient(const GameStartData &start_data)
|
||||
return false;
|
||||
|
||||
bool could_connect, connect_aborted;
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (g_touchscreengui) {
|
||||
g_touchscreengui->init(texture_src);
|
||||
g_touchscreengui->hide();
|
||||
}
|
||||
#endif
|
||||
if (!connectToServer(start_data, &could_connect, &connect_aborted))
|
||||
return false;
|
||||
|
||||
@@ -1542,10 +1611,8 @@ bool Game::initGui()
|
||||
-1, chat_backend, client, &g_menumgr);
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
|
||||
if (g_touchscreengui)
|
||||
g_touchscreengui->show();
|
||||
|
||||
g_touchscreengui->init(texture_src);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
@@ -2616,7 +2683,7 @@ void Game::updateCameraOrientation(CameraOrientation *cam, float dtime)
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (g_touchscreengui) {
|
||||
cam->camera_yaw += g_touchscreengui->getYawChange();
|
||||
cam->camera_pitch = g_touchscreengui->getPitch();
|
||||
cam->camera_pitch += g_touchscreengui->getPitchChange();
|
||||
} else {
|
||||
#endif
|
||||
v2s32 center(driver->getScreenSize().Width / 2, driver->getScreenSize().Height / 2);
|
||||
@@ -4553,6 +4620,10 @@ void the_game(bool *kill,
|
||||
error_message = std::string("ModError: ") + e.what() +
|
||||
strgettext("\nCheck debug.txt for details.");
|
||||
errorstream << error_message << std::endl;
|
||||
} catch (con::PeerNotFoundException &e) {
|
||||
error_message = gettext("Connection error (timed out?)");
|
||||
errorstream << error_message << std::endl;
|
||||
}
|
||||
|
||||
game.shutdown();
|
||||
}
|
||||
|
||||
@@ -334,3 +334,36 @@ void GameUI::deleteFormspec()
|
||||
|
||||
m_formname.clear();
|
||||
}
|
||||
|
||||
void GameUI::clearText()
|
||||
{
|
||||
if (m_guitext_chat) {
|
||||
m_guitext_chat->remove();
|
||||
m_guitext_chat = nullptr;
|
||||
}
|
||||
|
||||
if (m_guitext) {
|
||||
m_guitext->remove();
|
||||
m_guitext = nullptr;
|
||||
}
|
||||
|
||||
if (m_guitext2) {
|
||||
m_guitext2->remove();
|
||||
m_guitext2 = nullptr;
|
||||
}
|
||||
|
||||
if (m_guitext_info) {
|
||||
m_guitext_info->remove();
|
||||
m_guitext_info = nullptr;
|
||||
}
|
||||
|
||||
if (m_guitext_status) {
|
||||
m_guitext_status->remove();
|
||||
m_guitext_status = nullptr;
|
||||
}
|
||||
|
||||
if (m_guitext_profiler) {
|
||||
m_guitext_profiler->remove();
|
||||
m_guitext_profiler = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +106,7 @@ class GameUI
|
||||
const std::string &getFormspecName() { return m_formname; }
|
||||
GUIFormSpecMenu *&getFormspecGUI() { return m_formspec; }
|
||||
void deleteFormspec();
|
||||
void clearText();
|
||||
|
||||
private:
|
||||
Flags m_flags;
|
||||
|
||||
@@ -213,10 +213,10 @@ void Particle::step(float dtime)
|
||||
|
||||
if (m_p.animation.type != TAT_NONE) {
|
||||
m_animation_time += dtime;
|
||||
int frame_length_i, frame_count;
|
||||
int frame_length_i = 0;
|
||||
m_p.animation.determineParams(
|
||||
m_material.getTexture(0)->getSize(),
|
||||
&frame_count, &frame_length_i, NULL);
|
||||
NULL, &frame_length_i, NULL);
|
||||
float frame_length = frame_length_i / 1000.0;
|
||||
while (m_animation_time > frame_length) {
|
||||
m_animation_frame++;
|
||||
|
||||
@@ -106,7 +106,7 @@ void UpscaleStep::run(PipelineContext &context)
|
||||
std::unique_ptr<RenderStep> create3DStage(Client *client, v2f scale)
|
||||
{
|
||||
RenderStep *step = new Draw3D();
|
||||
if (g_settings->getBool("enable_shaders")) {
|
||||
if (g_settings->getBool("enable_shaders") && g_settings->getBool("enable_post_processing")) {
|
||||
RenderPipeline *pipeline = new RenderPipeline();
|
||||
pipeline->addStep(pipeline->own(std::unique_ptr<RenderStep>(step)));
|
||||
|
||||
@@ -131,7 +131,7 @@ RenderStep* addUpscaling(RenderPipeline *pipeline, RenderStep *previousStep, v2f
|
||||
return previousStep;
|
||||
|
||||
// When shaders are enabled, post-processing pipeline takes care of rescaling
|
||||
if (g_settings->getBool("enable_shaders"))
|
||||
if (g_settings->getBool("enable_shaders") && g_settings->getBool("enable_post_processing"))
|
||||
return previousStep;
|
||||
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <exception>
|
||||
#include <cassert>
|
||||
#include "gettime.h"
|
||||
|
||||
@@ -248,6 +248,7 @@ void set_default_settings()
|
||||
settings->setDefault("minimap_double_scan_height", "true");
|
||||
|
||||
// Effects
|
||||
settings->setDefault("enable_post_processing", "true");
|
||||
settings->setDefault("directional_colored_fog", "true");
|
||||
settings->setDefault("inventory_items_animations", "false");
|
||||
settings->setDefault("mip_map", "false");
|
||||
@@ -497,6 +498,7 @@ void set_default_settings()
|
||||
settings->setDefault("active_block_range", "2");
|
||||
settings->setDefault("viewing_range", "50");
|
||||
settings->setDefault("leaves_style", "simple");
|
||||
settings->setDefault("enable_post_processing", "false");
|
||||
settings->setDefault("curl_verify_cert", "false");
|
||||
|
||||
// Apply settings according to screen size
|
||||
|
||||
181
src/filesys.cpp
181
src/filesys.cpp
@@ -31,6 +31,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef SERVER
|
||||
#include "irr_ptr.h"
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#ifndef FICLONE
|
||||
#define FICLONE _IOW(0x94, 9, int)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace fs
|
||||
{
|
||||
@@ -214,6 +221,31 @@ std::string CreateTempFile()
|
||||
return path;
|
||||
}
|
||||
|
||||
bool CopyFileContents(const std::string &source, const std::string &target)
|
||||
{
|
||||
BOOL ok = CopyFileEx(source.c_str(), target.c_str(), nullptr, nullptr,
|
||||
nullptr, COPY_FILE_ALLOW_DECRYPTED_DESTINATION);
|
||||
if (!ok) {
|
||||
errorstream << "copying " << source << " to " << target
|
||||
<< " failed: " << GetLastError() << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// docs: "File attributes for the existing file are copied to the new file."
|
||||
// This is not our intention so get rid of unwanted attributes:
|
||||
DWORD attr = GetFileAttributes(target.c_str());
|
||||
if (attr == INVALID_FILE_ATTRIBUTES) {
|
||||
errorstream << target << ": file disappeared after copy" << std::endl;
|
||||
return false;
|
||||
}
|
||||
attr &= ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN);
|
||||
SetFileAttributes(target.c_str(), attr);
|
||||
|
||||
tracestream << "copied " << source << " to " << target
|
||||
<< " using CopyFileEx" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/*********
|
||||
@@ -412,6 +444,101 @@ std::string CreateTempFile()
|
||||
return path;
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct FileDeleter {
|
||||
void operator()(FILE *stream) {
|
||||
fclose(stream);
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::unique_ptr<FILE, FileDeleter> FileUniquePtr;
|
||||
}
|
||||
|
||||
bool CopyFileContents(const std::string &source, const std::string &target)
|
||||
{
|
||||
FileUniquePtr sourcefile, targetfile;
|
||||
|
||||
#ifdef __linux__
|
||||
// Try to clone using Copy-on-Write (CoW). This is instant but supported
|
||||
// only by some filesystems.
|
||||
|
||||
int srcfd, tgtfd;
|
||||
srcfd = open(source.c_str(), O_RDONLY);
|
||||
if (srcfd == -1) {
|
||||
errorstream << source << ": can't open for reading: "
|
||||
<< strerror(errno) << std::endl;
|
||||
return false;
|
||||
}
|
||||
tgtfd = open(target.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
if (tgtfd == -1) {
|
||||
errorstream << target << ": can't open for writing: "
|
||||
<< strerror(errno) << std::endl;
|
||||
close(srcfd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ioctl(tgtfd, FICLONE, srcfd) == 0) {
|
||||
tracestream << "copied " << source << " to " << target
|
||||
<< " using FICLONE" << std::endl;
|
||||
close(srcfd);
|
||||
close(tgtfd);
|
||||
return true;
|
||||
}
|
||||
|
||||
// fallback to normal copy, but no need to reopen the files
|
||||
sourcefile.reset(fdopen(srcfd, "rb"));
|
||||
targetfile.reset(fdopen(tgtfd, "wb"));
|
||||
goto fallback;
|
||||
|
||||
#endif
|
||||
|
||||
sourcefile.reset(fopen(source.c_str(), "rb"));
|
||||
targetfile.reset(fopen(target.c_str(), "wb"));
|
||||
|
||||
fallback:
|
||||
|
||||
if (!sourcefile) {
|
||||
errorstream << source << ": can't open for reading: "
|
||||
<< strerror(errno) << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (!targetfile) {
|
||||
errorstream << target << ": can't open for writing: "
|
||||
<< strerror(errno) << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t total = 0;
|
||||
bool done = false;
|
||||
char readbuffer[BUFSIZ];
|
||||
while (!done) {
|
||||
size_t readbytes = fread(readbuffer, 1,
|
||||
sizeof(readbuffer), sourcefile.get());
|
||||
total += readbytes;
|
||||
if (ferror(sourcefile.get())) {
|
||||
errorstream << source << ": IO error: "
|
||||
<< strerror(errno) << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (readbytes > 0)
|
||||
fwrite(readbuffer, 1, readbytes, targetfile.get());
|
||||
if (feof(sourcefile.get())) {
|
||||
// flush destination file to catch write errors (e.g. disk full)
|
||||
fflush(targetfile.get());
|
||||
done = true;
|
||||
}
|
||||
if (ferror(targetfile.get())) {
|
||||
errorstream << target << ": IO error: "
|
||||
<< strerror(errno) << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
tracestream << "copied " << total << " bytes from "
|
||||
<< source << " to " << target << std::endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/****************************
|
||||
@@ -486,60 +613,6 @@ bool CreateAllDirs(const std::string &path)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CopyFileContents(const std::string &source, const std::string &target)
|
||||
{
|
||||
FILE *sourcefile = fopen(source.c_str(), "rb");
|
||||
if(sourcefile == NULL){
|
||||
errorstream<<source<<": can't open for reading: "
|
||||
<<strerror(errno)<<std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
FILE *targetfile = fopen(target.c_str(), "wb");
|
||||
if(targetfile == NULL){
|
||||
errorstream<<target<<": can't open for writing: "
|
||||
<<strerror(errno)<<std::endl;
|
||||
fclose(sourcefile);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t total = 0;
|
||||
bool retval = true;
|
||||
bool done = false;
|
||||
char readbuffer[BUFSIZ];
|
||||
while(!done){
|
||||
size_t readbytes = fread(readbuffer, 1,
|
||||
sizeof(readbuffer), sourcefile);
|
||||
total += readbytes;
|
||||
if(ferror(sourcefile)){
|
||||
errorstream<<source<<": IO error: "
|
||||
<<strerror(errno)<<std::endl;
|
||||
retval = false;
|
||||
done = true;
|
||||
}
|
||||
if(readbytes > 0){
|
||||
fwrite(readbuffer, 1, readbytes, targetfile);
|
||||
}
|
||||
if(feof(sourcefile) || ferror(sourcefile)){
|
||||
// flush destination file to catch write errors
|
||||
// (e.g. disk full)
|
||||
fflush(targetfile);
|
||||
done = true;
|
||||
}
|
||||
if(ferror(targetfile)){
|
||||
errorstream<<target<<": IO error: "
|
||||
<<strerror(errno)<<std::endl;
|
||||
retval = false;
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
infostream<<"copied "<<total<<" bytes from "
|
||||
<<source<<" to "<<target<<std::endl;
|
||||
fclose(sourcefile);
|
||||
fclose(targetfile);
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool CopyDir(const std::string &source, const std::string &target)
|
||||
{
|
||||
if(PathExists(source)){
|
||||
|
||||
@@ -608,13 +608,15 @@ bool GUIEngine::downloadFile(const std::string &url, const std::string &target)
|
||||
fetch_request.caller = HTTPFETCH_SYNC;
|
||||
fetch_request.timeout = std::max(MIN_HTTPFETCH_TIMEOUT,
|
||||
(long)g_settings->getS32("curl_file_download_timeout"));
|
||||
httpfetch_sync(fetch_request, fetch_result);
|
||||
bool completed = httpfetch_sync_interruptible(fetch_request, fetch_result);
|
||||
|
||||
if (!fetch_result.succeeded) {
|
||||
if (!completed || !fetch_result.succeeded) {
|
||||
target_file.close();
|
||||
fs::DeleteSingleFileOrEmptyDirectory(target);
|
||||
return false;
|
||||
}
|
||||
// TODO: directly stream the response data into the file instead of first
|
||||
// storing the complete response in memory
|
||||
target_file << fetch_result.data;
|
||||
|
||||
return true;
|
||||
|
||||
@@ -222,7 +222,7 @@ void GUITable::setTable(const TableOptions &options,
|
||||
s32 colcount = columns.size();
|
||||
assert(colcount >= 1);
|
||||
// rowcount = ceil(cellcount / colcount) but use integer arithmetic
|
||||
s32 rowcount = (content.size() + colcount - 1) / colcount;
|
||||
s32 rowcount = std::min(((u32)content.size() + colcount - 1) / colcount, (u32)S32_MAX);
|
||||
assert(rowcount >= 0);
|
||||
// Append empty strings to content if there is an incomplete row
|
||||
s32 cellcount = rowcount * colcount;
|
||||
|
||||
@@ -278,7 +278,7 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event)
|
||||
irr_ptr<GUIModalMenu> holder;
|
||||
holder.grab(this); // keep this alive until return (it might be dropped downstream [?])
|
||||
|
||||
if (event.TouchInput.ID == 0) {
|
||||
if (event.TouchInput.touchedCount == 1) {
|
||||
if (event.TouchInput.Event == ETIE_PRESSED_DOWN || event.TouchInput.Event == ETIE_MOVED)
|
||||
m_pointer = v2s32(event.TouchInput.X, event.TouchInput.Y);
|
||||
gui::IGUIElement *hovered = Environment->getRootGUIElement()->getElementFromPoint(core::position2d<s32>(m_pointer));
|
||||
@@ -295,7 +295,7 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event)
|
||||
if (event.TouchInput.Event == ETIE_LEFT_UP)
|
||||
leave();
|
||||
return ret;
|
||||
} else if (event.TouchInput.ID == 1) {
|
||||
} else if (event.TouchInput.touchedCount == 2) {
|
||||
if (event.TouchInput.Event != ETIE_LEFT_UP)
|
||||
return true; // ignore
|
||||
auto focused = Environment->getFocus();
|
||||
|
||||
@@ -832,7 +832,7 @@ void TouchScreenGUI::translateEvent(const SEvent &event)
|
||||
const double d = g_settings->getFloat("touchscreen_sensitivity", 0.001f, 10.0f) * 3.0f;
|
||||
|
||||
m_camera_yaw_change -= dir_free.X * d;
|
||||
m_camera_pitch = MYMIN(MYMAX(m_camera_pitch + (dir_free.Y * d), -180.0f), 180.0f);
|
||||
m_camera_pitch_change += dir_free.Y * d;
|
||||
|
||||
// update shootline
|
||||
// no need to update (X, Y) when using crosshair since the shootline is not used
|
||||
|
||||
@@ -171,7 +171,11 @@ class TouchScreenGUI
|
||||
return res;
|
||||
}
|
||||
|
||||
double getPitch() { return m_camera_pitch; }
|
||||
double getPitchChange() {
|
||||
double res = m_camera_pitch_change;
|
||||
m_camera_pitch_change = 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a line which describes what the player is pointing at.
|
||||
@@ -213,7 +217,7 @@ class TouchScreenGUI
|
||||
|
||||
// value in degree
|
||||
double m_camera_yaw_change = 0.0;
|
||||
double m_camera_pitch = 0.0;
|
||||
double m_camera_pitch_change = 0.0;
|
||||
|
||||
/**
|
||||
* A line starting at the camera and pointing towards the selected object.
|
||||
|
||||
@@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "exceptions.h"
|
||||
#include "debug.h"
|
||||
#include "log.h"
|
||||
#include "porting.h"
|
||||
#include "util/container.h"
|
||||
#include "util/thread.h"
|
||||
#include "version.h"
|
||||
@@ -783,7 +784,7 @@ static void httpfetch_request_clear(u64 caller)
|
||||
}
|
||||
}
|
||||
|
||||
void httpfetch_sync(const HTTPFetchRequest &fetch_request,
|
||||
static void httpfetch_sync(const HTTPFetchRequest &fetch_request,
|
||||
HTTPFetchResult &fetch_result)
|
||||
{
|
||||
// Create ongoing fetch data and make a cURL handle
|
||||
@@ -796,6 +797,28 @@ void httpfetch_sync(const HTTPFetchRequest &fetch_request,
|
||||
fetch_result = *ongoing.complete(res);
|
||||
}
|
||||
|
||||
bool httpfetch_sync_interruptible(const HTTPFetchRequest &fetch_request,
|
||||
HTTPFetchResult &fetch_result, long interval)
|
||||
{
|
||||
if (Thread *thread = Thread::getCurrentThread()) {
|
||||
HTTPFetchRequest req = fetch_request;
|
||||
req.caller = httpfetch_caller_alloc_secure();
|
||||
httpfetch_async(req);
|
||||
do {
|
||||
if (thread->stopRequested()) {
|
||||
httpfetch_caller_free(req.caller);
|
||||
fetch_result = HTTPFetchResult(fetch_request);
|
||||
return false;
|
||||
}
|
||||
sleep_ms(interval);
|
||||
} while (!httpfetch_async_get(req.caller, fetch_result));
|
||||
httpfetch_caller_free(req.caller);
|
||||
} else {
|
||||
httpfetch_sync(fetch_request, fetch_result);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#else // USE_CURL
|
||||
|
||||
/*
|
||||
@@ -825,13 +848,14 @@ static void httpfetch_request_clear(u64 caller)
|
||||
{
|
||||
}
|
||||
|
||||
void httpfetch_sync(const HTTPFetchRequest &fetch_request,
|
||||
HTTPFetchResult &fetch_result)
|
||||
bool httpfetch_sync_interruptible(const HTTPFetchRequest &fetch_request,
|
||||
HTTPFetchResult &fetch_result, long interval)
|
||||
{
|
||||
errorstream << "httpfetch_sync: unable to fetch " << fetch_request.url
|
||||
errorstream << "httpfetch_sync_interruptible: unable to fetch " << fetch_request.url
|
||||
<< " because USE_CURL=0" << std::endl;
|
||||
|
||||
fetch_result = HTTPFetchResult(fetch_request); // sets succeeded = false etc.
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // USE_CURL
|
||||
|
||||
@@ -135,6 +135,9 @@ u64 httpfetch_caller_alloc_secure();
|
||||
// to stop any ongoing fetches for the given caller.
|
||||
void httpfetch_caller_free(u64 caller);
|
||||
|
||||
// Performs a synchronous HTTP request. This blocks and therefore should
|
||||
// only be used from background threads.
|
||||
void httpfetch_sync(const HTTPFetchRequest &fetch_request, HTTPFetchResult &fetch_result);
|
||||
// Performs a synchronous HTTP request that is interruptible if the current
|
||||
// thread is a Thread object. interval is the completion check interval in ms.
|
||||
// This blocks and therefore should only be used from background threads.
|
||||
// Returned is whether the request completed without interruption.
|
||||
bool httpfetch_sync_interruptible(const HTTPFetchRequest &fetch_request,
|
||||
HTTPFetchResult &fetch_result, long interval = 100);
|
||||
|
||||
@@ -716,7 +716,7 @@ static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
|
||||
int frame_count = 1;
|
||||
if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
|
||||
assert(layer->texture);
|
||||
int frame_length_ms;
|
||||
int frame_length_ms = 0;
|
||||
tiledef.animation.determineParams(layer->texture->getOriginalSize(),
|
||||
&frame_count, &frame_length_ms, NULL);
|
||||
layer->animation_frame_count = frame_count;
|
||||
|
||||
@@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "config.h"
|
||||
#include "filesys.h"
|
||||
#include "log.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <exception>
|
||||
@@ -39,6 +40,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
||||
extern int main(int argc, char *argv[]);
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_net_minetest_minetest_GameActivity_saveSettings(JNIEnv* env, jobject /* this */) {
|
||||
if (!g_settings_path.empty())
|
||||
g_settings->updateConfigFile(g_settings_path.c_str());
|
||||
}
|
||||
|
||||
void android_main(android_app *app)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
@@ -114,9 +114,9 @@ int ModApiHttp::l_http_fetch_sync(lua_State *L)
|
||||
infostream << "Mod performs HTTP request with URL " << req.url << std::endl;
|
||||
|
||||
HTTPFetchResult res;
|
||||
httpfetch_sync(req, res);
|
||||
bool completed = httpfetch_sync_interruptible(req, res);
|
||||
|
||||
push_http_fetch_result(L, res, true);
|
||||
push_http_fetch_result(L, res, completed);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -167,26 +167,6 @@ int ModApiServer::l_get_player_information(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
Be careful not to introduce a depdendency on the connection to
|
||||
the peer here. This function is >>REQUIRED<< to still be able to return
|
||||
values even when the peer unexpectedly disappears.
|
||||
Hence all the ConInfo values here are optional.
|
||||
*/
|
||||
|
||||
auto getConInfo = [&] (con::rtt_stat_type type, float *value) -> bool {
|
||||
return server->getClientConInfo(player->getPeerId(), type, value);
|
||||
};
|
||||
|
||||
float min_rtt, max_rtt, avg_rtt, min_jitter, max_jitter, avg_jitter;
|
||||
bool have_con_info =
|
||||
getConInfo(con::MIN_RTT, &min_rtt) &&
|
||||
getConInfo(con::MAX_RTT, &max_rtt) &&
|
||||
getConInfo(con::AVG_RTT, &avg_rtt) &&
|
||||
getConInfo(con::MIN_JITTER, &min_jitter) &&
|
||||
getConInfo(con::MAX_JITTER, &max_jitter) &&
|
||||
getConInfo(con::AVG_JITTER, &avg_jitter);
|
||||
|
||||
ClientInfo info;
|
||||
if (!server->getClientInfo(player->getPeerId(), info)) {
|
||||
warningstream << FUNCTION_NAME << ": no client info?!" << std::endl;
|
||||
@@ -211,6 +191,26 @@ int ModApiServer::l_get_player_information(lua_State *L)
|
||||
}
|
||||
lua_settable(L, table);
|
||||
|
||||
/*
|
||||
Be careful not to introduce a depdendency on the connection to
|
||||
the peer here. This function is >>REQUIRED<< to still be able to return
|
||||
values even when the peer unexpectedly disappears.
|
||||
Hence all the ConInfo values here are optional.
|
||||
*/
|
||||
|
||||
auto getConInfo = [&] (con::rtt_stat_type type, float *value) -> bool {
|
||||
return server->getClientConInfo(player->getPeerId(), type, value);
|
||||
};
|
||||
|
||||
float min_rtt, max_rtt, avg_rtt, min_jitter, max_jitter, avg_jitter;
|
||||
bool have_con_info =
|
||||
getConInfo(con::MIN_RTT, &min_rtt) &&
|
||||
getConInfo(con::MAX_RTT, &max_rtt) &&
|
||||
getConInfo(con::AVG_RTT, &avg_rtt) &&
|
||||
getConInfo(con::MIN_JITTER, &min_jitter) &&
|
||||
getConInfo(con::MAX_JITTER, &max_jitter) &&
|
||||
getConInfo(con::AVG_JITTER, &avg_jitter);
|
||||
|
||||
if (have_con_info) { // may be missing
|
||||
lua_pushstring(L, "min_rtt");
|
||||
lua_pushnumber(L, min_rtt);
|
||||
|
||||
@@ -4081,6 +4081,19 @@ Translations *Server::getTranslationLanguage(const std::string &lang_code)
|
||||
return translations;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::string> Server::getMediaList()
|
||||
{
|
||||
MutexAutoLock env_lock(m_env_mutex);
|
||||
|
||||
std::unordered_map<std::string, std::string> ret;
|
||||
for (auto &it : m_media) {
|
||||
if (it.second.no_announce)
|
||||
continue;
|
||||
ret.emplace(base64_decode(it.second.sha1_digest), it.second.path);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ModStorageDatabase *Server::openModStorageDatabase(const std::string &world_path)
|
||||
{
|
||||
std::string world_mt_path = world_path + DIR_DELIM + "world.mt";
|
||||
|
||||
@@ -375,6 +375,10 @@ class Server : public con::PeerHandler, public MapEventReceiver,
|
||||
// Get or load translations for a language
|
||||
Translations *getTranslationLanguage(const std::string &lang_code);
|
||||
|
||||
// Returns all media files the server knows about
|
||||
// map key = binary sha1, map value = file path
|
||||
std::unordered_map<std::string, std::string> getMediaList();
|
||||
|
||||
static ModStorageDatabase *openModStorageDatabase(const std::string &world_path);
|
||||
|
||||
static ModStorageDatabase *openModStorageDatabase(const std::string &backend,
|
||||
|
||||
64
src/threading/lambda.h
Normal file
64
src/threading/lambda.h
Normal file
@@ -0,0 +1,64 @@
|
||||
// Minetest
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include "debug.h"
|
||||
#include "threading/thread.h"
|
||||
|
||||
/**
|
||||
* Class returned by `runInThread`.
|
||||
*
|
||||
* Provides the usual thread methods along with `rethrow()`.
|
||||
*/
|
||||
class LambdaThread : public Thread
|
||||
{
|
||||
friend std::unique_ptr<LambdaThread> runInThread(
|
||||
const std::function<void()> &, const std::string &);
|
||||
public:
|
||||
/// Re-throw a caught exception, if any. Can only be called after thread exit.
|
||||
void rethrow()
|
||||
{
|
||||
sanity_check(!isRunning());
|
||||
if (m_exptr)
|
||||
std::rethrow_exception(m_exptr);
|
||||
}
|
||||
|
||||
private:
|
||||
// hide methods
|
||||
LambdaThread(const std::string &name="") : Thread(name) {}
|
||||
using Thread::start;
|
||||
|
||||
std::function<void()> m_fn;
|
||||
std::exception_ptr m_exptr;
|
||||
|
||||
void *run()
|
||||
{
|
||||
try {
|
||||
m_fn();
|
||||
} catch(...) {
|
||||
m_exptr = std::current_exception();
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Run a lambda in a separate thread.
|
||||
*
|
||||
* Exceptions will be caught.
|
||||
* @param fn function to run
|
||||
* @param thread_name name for thread
|
||||
* @return thread object of type `LambdaThread`
|
||||
*/
|
||||
std::unique_ptr<LambdaThread> runInThread(const std::function<void()> &fn,
|
||||
const std::string &thread_name = "")
|
||||
{
|
||||
std::unique_ptr<LambdaThread> t(new LambdaThread(thread_name));
|
||||
t->m_fn = fn;
|
||||
t->start();
|
||||
return t;
|
||||
}
|
||||
@@ -60,6 +60,9 @@ DEALINGS IN THE SOFTWARE.
|
||||
#endif
|
||||
|
||||
|
||||
thread_local Thread *current_thread = nullptr;
|
||||
|
||||
|
||||
Thread::Thread(const std::string &name) :
|
||||
m_name(name),
|
||||
m_request_stop(false),
|
||||
@@ -176,6 +179,8 @@ void Thread::threadProc(Thread *thr)
|
||||
thr->m_kernel_thread_id = thread_self();
|
||||
#endif
|
||||
|
||||
current_thread = thr;
|
||||
|
||||
thr->setName(thr->m_name);
|
||||
|
||||
g_logger.registerThread(thr->m_name);
|
||||
@@ -196,6 +201,12 @@ void Thread::threadProc(Thread *thr)
|
||||
}
|
||||
|
||||
|
||||
Thread *Thread::getCurrentThread()
|
||||
{
|
||||
return current_thread;
|
||||
}
|
||||
|
||||
|
||||
void Thread::setName(const std::string &name)
|
||||
{
|
||||
#if defined(__linux__)
|
||||
|
||||
@@ -59,6 +59,7 @@ class Thread {
|
||||
Thread(const std::string &name="");
|
||||
virtual ~Thread();
|
||||
DISABLE_CLASS_COPY(Thread)
|
||||
// Note: class cannot be moved since other references exist
|
||||
|
||||
/*
|
||||
* Begins execution of a new thread at the pure virtual method Thread::run().
|
||||
@@ -119,6 +120,11 @@ class Thread {
|
||||
*/
|
||||
bool setPriority(int prio);
|
||||
|
||||
/*
|
||||
* Returns the thread object of the current thread if it exists.
|
||||
*/
|
||||
static Thread *getCurrentThread();
|
||||
|
||||
/*
|
||||
* Sets the currently executing thread's name to where supported; useful
|
||||
* for debugging.
|
||||
|
||||
@@ -38,6 +38,8 @@ class TestFilePath : public TestBase {
|
||||
void testRemoveLastPathComponent();
|
||||
void testRemoveLastPathComponentWithTrailingDelimiter();
|
||||
void testRemoveRelativePathComponent();
|
||||
void testSafeWriteToFile();
|
||||
void testCopyFileContents();
|
||||
};
|
||||
|
||||
static TestFilePath g_test_instance;
|
||||
@@ -49,6 +51,8 @@ void TestFilePath::runTests(IGameDef *gamedef)
|
||||
TEST(testRemoveLastPathComponent);
|
||||
TEST(testRemoveLastPathComponentWithTrailingDelimiter);
|
||||
TEST(testRemoveRelativePathComponent);
|
||||
TEST(testSafeWriteToFile);
|
||||
TEST(testCopyFileContents);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -56,7 +60,7 @@ void TestFilePath::runTests(IGameDef *gamedef)
|
||||
// adjusts a POSIX path to system-specific conventions
|
||||
// -> changes '/' to DIR_DELIM
|
||||
// -> absolute paths start with "C:\\" on windows
|
||||
std::string p(std::string path)
|
||||
static std::string p(std::string path)
|
||||
{
|
||||
for (size_t i = 0; i < path.size(); ++i) {
|
||||
if (path[i] == '/') {
|
||||
@@ -262,3 +266,47 @@ void TestFilePath::testRemoveRelativePathComponent()
|
||||
result = fs::RemoveRelativePathComponents(path);
|
||||
UASSERT(result == p("/a/e"));
|
||||
}
|
||||
|
||||
|
||||
void TestFilePath::testSafeWriteToFile()
|
||||
{
|
||||
const std::string dest_path = getTestTempFile();
|
||||
const std::string test_data("hello\0world", 11);
|
||||
fs::safeWriteToFile(dest_path, test_data);
|
||||
UASSERT(fs::PathExists(dest_path));
|
||||
std::string contents_actual;
|
||||
UASSERT(fs::ReadFile(dest_path, contents_actual));
|
||||
UASSERTEQ(auto, contents_actual, test_data);
|
||||
}
|
||||
|
||||
void TestFilePath::testCopyFileContents()
|
||||
{
|
||||
const auto dir_path = getTestTempDirectory();
|
||||
const auto file1 = dir_path + DIR_DELIM "src", file2 = dir_path + DIR_DELIM "dst";
|
||||
const std::string test_data("hello\0world", 11);
|
||||
|
||||
// error case
|
||||
UASSERT(!fs::CopyFileContents(file1, "somewhere"));
|
||||
|
||||
{
|
||||
std::ofstream ofs(file1);
|
||||
ofs << test_data;
|
||||
}
|
||||
|
||||
// normal case
|
||||
UASSERT(fs::CopyFileContents(file1, file2));
|
||||
std::string contents_actual;
|
||||
UASSERT(fs::ReadFile(file2, contents_actual));
|
||||
UASSERTEQ(auto, contents_actual, test_data);
|
||||
|
||||
// should overwrite and truncate
|
||||
{
|
||||
std::ofstream ofs(file2);
|
||||
for (int i = 0; i < 10; i++)
|
||||
ofs << "OH MY GAH";
|
||||
}
|
||||
UASSERT(fs::CopyFileContents(file1, file2));
|
||||
contents_actual.clear();
|
||||
UASSERT(fs::ReadFile(file2, contents_actual));
|
||||
UASSERTEQ(auto, contents_actual, test_data);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#! /bin/bash -e
|
||||
#!/bin/bash -e
|
||||
|
||||
cmake -B build \
|
||||
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE:-Debug} \
|
||||
-DENABLE_LTO=FALSE \
|
||||
-DRUN_IN_PLACE=TRUE \
|
||||
-DENABLE_GETTEXT=${CMAKE_ENABLE_GETTEXT:-TRUE} \
|
||||
-DBUILD_SERVER=${CMAKE_BUILD_SERVER:-TRUE} \
|
||||
|
||||
Reference in New Issue
Block a user