diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index b234fb283..b01a89509 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -43,6 +43,12 @@ Contributions are welcome! Here's how you can help: 4. The code's interfaces are well designed, regardless of other aspects that might need more work in the future. 5. It uses protocols and formats which include the required compatibility. +### Important note about automated GitHub checks + +When you submit a pull request, GitHub automatically runs checks on the Minetest Engine combined with your changes. One of these checks is called 'cpp lint / clang format', which checks code formatting. Because formatting for readability requires human judgement this check often fails and often makes unsuitable formatting requests which make code readability worse. + +If this check fails, look at the details to check for any clear mistakes and correct those. However, you should not apply everything ClangFormat requests. Ignore requests that make code readability worse and any other clearly unsuitable requests. Discuss in the pull request with a core developer about how to progress. + ## Issues If you experience an issue, we would like to know the details - especially when a stable release is on the way. diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml new file mode 100644 index 000000000..0fcfe2390 --- /dev/null +++ b/.github/workflows/android.yml @@ -0,0 +1,42 @@ +name: android + +# build on c/cpp changes or workflow changes +on: + push: + paths: + - 'lib/**.[ch]' + - 'lib/**.cpp' + - 'src/**.[ch]' + - 'src/**.cpp' + - 'build/android/**' + - '.github/workflows/android.yml' + pull_request: + paths: + - 'lib/**.[ch]' + - 'lib/**.cpp' + - 'src/**.[ch]' + - 'src/**.cpp' + - 'build/android/**' + - '.github/workflows/android.yml' + +jobs: + build: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + - name: Build with Gradle + run: cd build/android; ./gradlew assemblerelease + - name: Save armeabi artifact + uses: actions/upload-artifact@v2 + with: + name: Minetest-armeabi-v7a.apk + path: build/android/app/build/outputs/apk/release/app-armeabi-v7a-release-unsigned.apk + - name: Save arm64 artifact + uses: actions/upload-artifact@v2 + with: + name: Minetest-arm64-v8a.apk + path: build/android/app/build/outputs/apk/release/app-arm64-v8a-release-unsigned.apk diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ae359f5d8..71fdf3652 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -221,8 +221,8 @@ jobs: name: VS 2019 ${{ matrix.config.arch }}-${{ matrix.type }} runs-on: windows-2019 env: - VCPKG_VERSION: c7ab9d3110813979a873b2dbac630a9ab79850dc -# 2020.04 + VCPKG_VERSION: 0bf3923f9fab4001c00f0f429682a0853b5749e0 +# 2020.11 vcpkg_packages: irrlicht zlib curl[winssl] openal-soft libvorbis libogg sqlite3 freetype luajit strategy: fail-fast: false @@ -248,7 +248,7 @@ jobs: uses: actions/checkout@v2 - name: Restore from cache and run vcpkg - uses: lukka/run-vcpkg@v2 + uses: lukka/run-vcpkg@v5 with: vcpkgArguments: ${{env.vcpkg_packages}} vcpkgDirectory: '${{ github.workspace }}\vcpkg' diff --git a/.gitignore b/.gitignore index 78b047f32..b09d37dae 100644 --- a/.gitignore +++ b/.gitignore @@ -53,6 +53,7 @@ build/.cmake/ /worlds /world/ /client/mod_storage/ +!/clientmods/mods_here.txt ## Configuration/log files minetest.conf diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d03b7b601..c3e120375 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -36,7 +36,7 @@ variables: - cp misc/debpkg-control build/deb/minetest/DEBIAN/control - cp -Rp artifact/minetest/usr build/deb/minetest/ script: - - git clone $MINETEST_GAME_REPO build/deb/minetest/usr/share/minetest/games/minetest + - 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 @@ -142,36 +142,6 @@ deploy:debian-10: ## Ubuntu ## -# Trusty - -build:ubuntu-14.04: - extends: .build_template - image: ubuntu:trusty - before_script: - - echo "deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu trusty main" > /etc/apt/sources.list.d/uptodate-toolchain.list - - apt-key adv --keyserver keyserver.ubuntu.com --recv BA9EF27F - - apt-get update -y - - apt-get -y install build-essential gcc-6 g++-6 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 - variables: - CC: gcc-6 - CXX: g++-6 - -package:ubuntu-14.04: - extends: .debpkg_template - image: ubuntu:trusty - dependencies: - - build:ubuntu-14.04 - variables: - LEVELDB_PKG: libleveldb1 - -deploy:ubuntu-14.04: - extends: .debpkg_install - image: ubuntu:trusty - dependencies: - - package:ubuntu-14.04 - variables: - LEVELDB_PKG: libleveldb1 - # Xenial build:ubuntu-16.04: @@ -197,6 +167,31 @@ deploy:ubuntu-16.04: variables: LEVELDB_PKG: libleveldb1v5 +# Bionic + +build:ubuntu-18.04: + extends: .build_template + image: ubuntu:bionic + before_script: + - 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 + dependencies: + - build:ubuntu-18.04 + variables: + LEVELDB_PKG: libleveldb1v5 + +deploy:ubuntu-18.04: + extends: .debpkg_install + image: ubuntu:bionic + dependencies: + - package:ubuntu-18.04 + variables: + LEVELDB_PKG: libleveldb1v5 + ## ## Fedora ## @@ -308,3 +303,28 @@ pages: only: - master +package:appimage-client: + stage: package + image: appimagecrafters/appimage-builder + dependencies: + - 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 -R 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: + when: on_success + expire_in: 90 day + paths: + - ./*.AppImage diff --git a/.luacheckrc b/.luacheckrc index 3ab6e10c8..e010ab95c 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -72,3 +72,11 @@ files["builtin/mainmenu"] = { "PLATFORM", }, } + +files["builtin/common/tests"] = { + read_globals = { + "describe", + "it", + "assert", + }, +} diff --git a/AppImageBuilder.yml b/AppImageBuilder.yml new file mode 100644 index 000000000..9ecad5d8e --- /dev/null +++ b/AppImageBuilder.yml @@ -0,0 +1,51 @@ +version: 1 + +AppDir: + path: ./AppDir + + app_info: + id: minetest + name: Minetest + icon: minetest + version: !ENV ${VERSION} + exec: usr/bin/minetest + exec_args: $@ + runtime: + env: + APPDIR_LIBRARY_PATH: $APPDIR/usr/lib/x86_64-linux-gnu + + apt: + arch: amd64 + sources: + - sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic main universe + key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3b4fe6acc0b21f32' + - sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-updates main universe + - sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-backports main universe + - sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-security main universe + + include: + - libirrlicht1.8 + - libxxf86vm1 + - libgl1-mesa-glx + - libsqlite3-0 + - libogg0 + - libvorbis0a + - libopenal1 + - libcurl3-gnutls + - libfreetype6 + - zlib1g + - libgmp10 + - libjsoncpp1 + + files: + exclude: + - usr/share/man + - usr/share/doc/*/README.* + - usr/share/doc/*/changelog.* + - usr/share/doc/*/NEWS.* + - usr/share/doc/*/TODO.* + +AppImage: + update-information: None + sign-key: None + arch: x86_64 diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ebc6451f..0dcab660e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -249,15 +249,15 @@ cpack_add_component(Docs cpack_add_component(SUBGAME_MINETEST_GAME DISPLAY_NAME "Minetest Game" - DESCRIPTION "The official subgame for the Minetest engine, that can easily extended by mods." - GROUP "Subgames" + DESCRIPTION "The default game bundled in the Minetest engine. Mainly used as a modding base." + GROUP "Games" ) cpack_add_component(SUBGAME_MINIMAL DISPLAY_NAME "Development Test" - DESCRIPTION "A minimal test game helping to develop the engine." + DESCRIPTION "A basic testing environment used for engine development and sometimes for testing mods." DISABLED #DISABLED does not mean it is disabled, and is just not selected by default. - GROUP "Subgames" + GROUP "Games" ) cpack_add_component_group(Subgames diff --git a/Dockerfile b/Dockerfile index 72343ab9c..871ca9825 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,7 +21,7 @@ WORKDIR /usr/src/minetest 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 ca-certificates && \ + 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 @@ -51,7 +51,7 @@ RUN mkdir build && \ FROM alpine:3.11 -RUN apk add --no-cache sqlite-libs curl gmp libstdc++ libgcc libpq && \ +RUN apk add --no-cache sqlite-libs curl gmp libstdc++ libgcc libpq luajit && \ adduser -D minetest --uid 30000 -h /var/lib/minetest && \ chown -R minetest:minetest /var/lib/minetest diff --git a/README.md b/README.md index f88481ca2..d7613601e 100644 --- a/README.md +++ b/README.md @@ -102,11 +102,11 @@ Where each location is on each platform: * `bin` = `bin` * `share` = `.` * `user` = `.` -* Windows installed: +* Windows installation: * `bin` = `C:\Program Files\Minetest\bin (Depends on the install location)` * `share` = `C:\Program Files\Minetest (Depends on the install location)` * `user` = `%APPDATA%\Minetest` -* Linux installed: +* Linux installation: * `bin` = `/usr/bin` * `share` = `/usr/share/minetest` * `user` = `~/.minetest` @@ -321,13 +321,14 @@ It is highly recommended to use vcpkg as package manager. 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 --triplet x64-windows +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. @@ -360,7 +361,7 @@ This is outdated and not recommended. Follow the instructions on https://dev.min 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=0 -DENABLE_CURSES=0 +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. diff --git a/build/android/app/build.gradle b/build/android/app/build.gradle index e3619af17..fccb7b3b4 100644 --- a/build/android/app/build.gradle +++ b/build/android/app/build.gradle @@ -1,8 +1,8 @@ apply plugin: 'com.android.application' android { compileSdkVersion 29 - buildToolsVersion '29.0.3' - ndkVersion '21.1.6352462' + buildToolsVersion '30.0.2' + ndkVersion '21.3.6528147' defaultConfig { applicationId 'net.minetest.minetest' minSdkVersion 16 @@ -11,8 +11,11 @@ android { versionCode project.versionCode } + // load properties Properties props = new Properties() - props.load(new FileInputStream(file('../local.properties'))) + def propfile = file('../local.properties') + if (propfile.exists()) + props.load(new FileInputStream(propfile)) if (props.getProperty('keystore') != null) { signingConfigs { @@ -61,10 +64,9 @@ task prepareAssets() { copy { from "${projRoot}/builtin" into "${assetsFolder}/builtin" } - /*copy { - // ToDo: fix Minetest shaders that currently don't work with OpenGL ES + copy { from "${projRoot}/client/shaders" into "${assetsFolder}/client/shaders" - }*/ + } copy { from "../native/deps/Android/Irrlicht/shaders" into "${assetsFolder}/client/shaders/Irrlicht" } @@ -107,5 +109,5 @@ android.applicationVariants.all { variant -> dependencies { implementation project(':native') - implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.appcompat:appcompat:1.2.0' } diff --git a/build/android/app/src/main/AndroidManifest.xml b/build/android/app/src/main/AndroidManifest.xml index 0a7c8d95a..fa93e7069 100644 --- a/build/android/app/src/main/AndroidManifest.xml +++ b/build/android/app/src/main/AndroidManifest.xml @@ -17,8 +17,8 @@ android:allowBackup="false" android:icon="@mipmap/ic_launcher" android:label="@string/label" - android:resizeableActivity="false" android:requestLegacyExternalStorage="true" + android:resizeableActivity="false" tools:ignore="UnusedAttribute"> - - +Copyright (C) 2014-2020 ubulem, Bektur Mambetov + +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 2.1 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 net.minetest.minetest; + +import android.content.Context; +import android.view.KeyEvent; +import android.view.inputmethod.InputMethodManager; + +import androidx.appcompat.widget.AppCompatEditText; + +import java.util.Objects; + +public class CustomEditText extends AppCompatEditText { + public CustomEditText(Context context) { + super(context); + } + + @Override + public boolean onKeyPreIme(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + InputMethodManager mgr = (InputMethodManager) + getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + Objects.requireNonNull(mgr).hideSoftInputFromWindow(this.getWindowToken(), 0); + } + return false; + } +} diff --git a/build/android/app/src/main/java/net/minetest/minetest/GameActivity.java b/build/android/app/src/main/java/net/minetest/minetest/GameActivity.java index 635512569..db126a2b9 100644 --- a/build/android/app/src/main/java/net/minetest/minetest/GameActivity.java +++ b/build/android/app/src/main/java/net/minetest/minetest/GameActivity.java @@ -25,8 +25,16 @@ import android.content.Intent; import android.net.Uri; import android.os.Build; import android.os.Bundle; +import android.text.InputType; +import android.view.KeyEvent; import android.view.View; import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; + +import androidx.appcompat.app.AlertDialog; + +import java.util.Objects; public class GameActivity extends NativeActivity { static { @@ -34,8 +42,8 @@ public class GameActivity extends NativeActivity { System.loadLibrary("Minetest"); } - private int messageReturnCode; - private String messageReturnValue; + private int messageReturnCode = -1; + private String messageReturnValue = ""; public static native void putMessageBoxResult(String text); @@ -43,8 +51,6 @@ public class GameActivity extends NativeActivity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - messageReturnCode = -1; - messageReturnValue = ""; } private void makeFullScreen() { @@ -73,29 +79,46 @@ public class GameActivity extends NativeActivity { // Ignore the back press so Minetest can handle it } - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == 101) { - if (resultCode == RESULT_OK) { - String text = data.getStringExtra("text"); - messageReturnCode = 0; - messageReturnValue = text; - } else - messageReturnCode = 1; - } + public void showDialog(String acceptButton, String hint, String current, int editType) { + runOnUiThread(() -> showDialogUI(hint, current, editType)); } - public void showDialog(String acceptButton, String hint, String current, int editType) { - Intent intent = new Intent(this, InputDialogActivity.class); - Bundle params = new Bundle(); - params.putString("acceptButton", acceptButton); - params.putString("hint", hint); - params.putString("current", current); - params.putInt("editType", editType); - intent.putExtras(params); - startActivityForResult(intent, 101); - messageReturnValue = ""; - messageReturnCode = -1; + private void showDialogUI(String hint, String current, int editType) { + final AlertDialog.Builder builder = new AlertDialog.Builder(this); + EditText editText = new CustomEditText(this); + builder.setView(editText); + AlertDialog alertDialog = builder.create(); + editText.requestFocus(); + editText.setHint(hint); + editText.setText(current); + final InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE); + Objects.requireNonNull(imm).toggleSoftInput(InputMethodManager.SHOW_FORCED, + InputMethodManager.HIDE_IMPLICIT_ONLY); + if (editType == 1) + editText.setInputType(InputType.TYPE_CLASS_TEXT | + InputType.TYPE_TEXT_FLAG_MULTI_LINE); + else if (editType == 3) + editText.setInputType(InputType.TYPE_CLASS_TEXT | + InputType.TYPE_TEXT_VARIATION_PASSWORD); + else + editText.setInputType(InputType.TYPE_CLASS_TEXT); + editText.setSelection(editText.getText().length()); + editText.setOnKeyListener((view, KeyCode, event) -> { + if (KeyCode == KeyEvent.KEYCODE_ENTER) { + imm.hideSoftInputFromWindow(editText.getWindowToken(), 0); + messageReturnCode = 0; + messageReturnValue = editText.getText().toString(); + alertDialog.dismiss(); + return true; + } + return false; + }); + alertDialog.show(); + alertDialog.setOnCancelListener(dialog -> { + getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); + messageReturnValue = current; + messageReturnCode = -1; + }); } public int getDialogState() { diff --git a/build/android/app/src/main/java/net/minetest/minetest/InputDialogActivity.java b/build/android/app/src/main/java/net/minetest/minetest/InputDialogActivity.java deleted file mode 100644 index 7c6aa111d..000000000 --- a/build/android/app/src/main/java/net/minetest/minetest/InputDialogActivity.java +++ /dev/null @@ -1,98 +0,0 @@ -/* -Minetest -Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik -Copyright (C) 2014-2020 ubulem, Bektur Mambetov - -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 2.1 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 net.minetest.minetest; - -import android.app.Activity; -import android.content.Intent; -import android.os.Build; -import android.os.Bundle; -import android.text.InputType; -import android.view.KeyEvent; -import android.view.View; -import android.view.inputmethod.InputMethodManager; -import android.widget.EditText; - -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; - -import java.util.Objects; - -public class InputDialogActivity extends AppCompatActivity { - private AlertDialog alertDialog; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - Bundle b = getIntent().getExtras(); - int editType = Objects.requireNonNull(b).getInt("editType"); - String hint = b.getString("hint"); - String current = b.getString("current"); - final AlertDialog.Builder builder = new AlertDialog.Builder(this); - EditText editText = new EditText(this); - builder.setView(editText); - editText.requestFocus(); - editText.setHint(hint); - editText.setText(current); - final InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE); - Objects.requireNonNull(imm).toggleSoftInput(InputMethodManager.SHOW_FORCED, - InputMethodManager.HIDE_IMPLICIT_ONLY); - if (editType == 3) - editText.setInputType(InputType.TYPE_CLASS_TEXT | - InputType.TYPE_TEXT_VARIATION_PASSWORD); - else - editText.setInputType(InputType.TYPE_CLASS_TEXT); - editText.setOnKeyListener((view, KeyCode, event) -> { - if (KeyCode == KeyEvent.KEYCODE_ENTER) { - imm.hideSoftInputFromWindow(editText.getWindowToken(), 0); - pushResult(editText.getText().toString()); - return true; - } - return false; - }); - alertDialog = builder.create(); - if (!this.isFinishing()) - alertDialog.show(); - alertDialog.setOnCancelListener(dialog -> { - pushResult(editText.getText().toString()); - setResult(Activity.RESULT_CANCELED); - alertDialog.dismiss(); - makeFullScreen(); - finish(); - }); - } - - private void pushResult(String text) { - Intent resultData = new Intent(); - resultData.putExtra("text", text); - setResult(AppCompatActivity.RESULT_OK, resultData); - alertDialog.dismiss(); - makeFullScreen(); - finish(); - } - - private void makeFullScreen() { - if (Build.VERSION.SDK_INT >= 19) - this.getWindow().getDecorView().setSystemUiVisibility( - View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | - View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | - View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); - } -} diff --git a/build/android/app/src/main/res/values/styles.xml b/build/android/app/src/main/res/values/styles.xml index 618507e63..291a4eaf1 100644 --- a/build/android/app/src/main/res/values/styles.xml +++ b/build/android/app/src/main/res/values/styles.xml @@ -8,15 +8,8 @@ shortEdges - - - diff --git a/build/android/build.gradle b/build/android/build.gradle index 8707b563c..111a506e1 100644 --- a/build/android/build.gradle +++ b/build/android/build.gradle @@ -15,8 +15,8 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.6.3' - classpath 'org.ajoberstar.grgit:grgit-gradle:4.0.2' + classpath 'com.android.tools.build:gradle:4.0.1' + classpath 'de.undercouch:gradle-download-task:4.1.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } @@ -31,4 +31,5 @@ allprojects { task clean(type: Delete) { delete rootProject.buildDir + delete 'native/deps' } diff --git a/build/android/gradle/wrapper/gradle-wrapper.properties b/build/android/gradle/wrapper/gradle-wrapper.properties index d612cf333..ed85703f3 100644 --- a/build/android/gradle/wrapper/gradle-wrapper.properties +++ b/build/android/gradle/wrapper/gradle-wrapper.properties @@ -1,2 +1,6 @@ -#Mon Apr 06 00:06:16 CEST 2020 -distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip +#Mon Sep 07 22:11:10 CEST 2020 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip diff --git a/build/android/native/build.gradle b/build/android/native/build.gradle index cbd50db6a..69e1cf461 100644 --- a/build/android/native/build.gradle +++ b/build/android/native/build.gradle @@ -1,16 +1,16 @@ apply plugin: 'com.android.library' -import org.ajoberstar.grgit.Grgit +apply plugin: 'de.undercouch.download' android { compileSdkVersion 29 - buildToolsVersion '29.0.3' - ndkVersion '21.1.6352462' + buildToolsVersion '30.0.2' + ndkVersion '21.3.6528147' defaultConfig { minSdkVersion 16 targetSdkVersion 29 externalNativeBuild { ndkBuild { - arguments '-j8', + arguments '-j' + Runtime.getRuntime().availableProcessors(), "versionMajor=${versionMajor}", "versionMinor=${versionMinor}", "versionPatch=${versionPatch}", @@ -45,15 +45,54 @@ android { } } -task cloneGitRepo() { - def destination = file('deps') - if(!destination.exists()) { - def grgit = Grgit.clone( - dir: destination, - uri: 'https://github.com/minetest/minetest_android_deps_binaries' - ) - grgit.close() +// get precompiled deps +def folder = 'minetest_android_deps_binaries' + +task downloadDeps(type: Download) { + src 'https://github.com/minetest/' + folder + '/archive/master.zip' + dest new File(buildDir, 'deps.zip') + overwrite false +} + +task getDeps(dependsOn: downloadDeps, type: Copy) { + def deps = file('deps') + def f = file("$buildDir/" + folder + "-master") + + if (!deps.exists() && !f.exists()) { + from zipTree(downloadDeps.dest) + into buildDir + } + + doLast { + if (!deps.exists()) { + file(f).renameTo(file(deps)) + } } } -preBuild.dependsOn cloneGitRepo +// get sqlite +def sqlite_ver = '3320200' +task downloadSqlite(dependsOn: getDeps, type: Download) { + src 'https://www.sqlite.org/2020/sqlite-amalgamation-' + sqlite_ver + '.zip' + dest new File(buildDir, 'sqlite.zip') + overwrite false +} + +task getSqlite(dependsOn: downloadSqlite, type: Copy) { + def sqlite = file('deps/Android/sqlite') + def f = file("$buildDir/sqlite-amalgamation-" + sqlite_ver) + + if (!sqlite.exists() && !f.exists()) { + from zipTree(downloadSqlite.dest) + into buildDir + } + + doLast { + if (!sqlite.exists()) { + file(f).renameTo(file(sqlite)) + } + } +} + +preBuild.dependsOn getDeps +preBuild.dependsOn getSqlite diff --git a/builtin/client/chatcommands.lua b/builtin/client/chatcommands.lua index 83b7f7b14..a45a76dfe 100644 --- a/builtin/client/chatcommands.lua +++ b/builtin/client/chatcommands.lua @@ -22,6 +22,11 @@ core.register_on_sending_chat_message(function(message) return true end + -- Run core.registered_on_chatcommand callbacks. + if core.run_callbacks(core.registered_on_chatcommand, 5, cmd, param) then + return true + end + local cmd_def = core.registered_chatcommands[cmd] if cmd_def then core.set_last_run_mod(cmd_def.mod_origin) @@ -40,4 +45,150 @@ function core.run_server_chatcommand(cmd, param) core.send_chat_message("/" .. cmd .. " " .. param) end +core.register_chatcommand("say", { + description = "Send raw text", + func = function(text) + core.send_chat_message(text) + return true + end, +}) +core.register_chatcommand("teleport", { + params = ",,", + description = "Teleport to coordinates.", + func = function(param) + local success, pos = core.parse_pos(param) + if success then + core.localplayer:set_pos(pos) + return true, "Teleporting to " .. core.pos_to_string(pos) + end + return false, pos + end, +}) + +core.register_chatcommand("teleportjump", { + params = ",,", + description = "Teleport to relative coordinates.", + func = function(param) + local success, pos = core.parse_relative_pos(param) + if success then + core.localplayer:set_pos(pos) + return true, "Teleporting to " .. core.pos_to_string(pos) + end + return false, pos + end, +}) + +core.register_chatcommand("wielded", { + description = "Print itemstring of wieleded item", + func = function() + return true, core.localplayer:get_wielded_item():get_name() + end +}) + +core.register_chatcommand("disconnect", { + description = "Exit to main menu", + func = function(param) + core.disconnect() + end, +}) + +core.register_chatcommand("players", { + description = "List online players", + func = function(param) + return true, "Online players: " .. table.concat(core.get_player_names(), ", ") + end +}) + +core.register_chatcommand("kill", { + description = "Kill yourself", + func = function() + core.send_damage(10000) + end, +}) + +core.register_chatcommand("set", { + params = "([-n] ) | ", + description = "Set or read client configuration setting", + func = function(param) + local arg, setname, setvalue = string.match(param, "(-[n]) ([^ ]+) (.+)") + if arg and arg == "-n" and setname and setvalue then + core.settings:set(setname, setvalue) + return true, setname .. " = " .. setvalue + end + + setname, setvalue = string.match(param, "([^ ]+) (.+)") + if setname and setvalue then + if not core.settings:get(setname) then + return false, "Failed. Use '.set -n ' to create a new setting." + end + core.settings:set(setname, setvalue) + return true, setname .. " = " .. setvalue + end + + setname = string.match(param, "([^ ]+)") + if setname then + setvalue = core.settings:get(setname) + if not setvalue then + setvalue = "" + end + return true, setname .. " = " .. setvalue + end + + return false, "Invalid parameters (see .help set)." + end, +}) + +core.register_chatcommand("place", { + params = ",,", + description = "Place wielded item", + func = function(param) + local success, pos = core.parse_relative_pos(param) + if success then + core.place_node(pos) + return true, "Node placed at " .. core.pos_to_string(pos) + end + return false, pos + end, +}) + +core.register_chatcommand("dig", { + params = ",,", + description = "Dig node", + func = function(param) + local success, pos = core.parse_relative_pos(param) + if success then + core.dig_node(pos) + return true, "Node at " .. core.pos_to_string(pos) .. " dug" + end + return false, pos + end, +}) + +core.register_chatcommand("setyaw", { + params = "", + description = "Set your yaw", + func = function(param) + local yaw = tonumber(param) + if yaw then + core.localplayer:set_yaw(yaw) + return true + else + return false, "Invalid usage (See /help setyaw)" + end + end +}) + +core.register_chatcommand("setpitch", { + params = "", + description = "Set your pitch", + func = function(param) + local pitch = tonumber(param) + if pitch then + core.localplayer:set_pitch(pitch) + return true + else + return false, "Invalid usage (See /help setpitch)" + end + end +}) diff --git a/builtin/client/cheats.lua b/builtin/client/cheats.lua index aabba7e2d..ffc86d631 100644 --- a/builtin/client/cheats.lua +++ b/builtin/client/cheats.lua @@ -1,25 +1,24 @@ core.cheats = { ["Combat"] = { - ["Killaura"] = "killaura", ["AntiKnockback"] = "antiknockback", ["FastHit"] = "spamclick", ["AttachmentFloat"] = "float_above_parent", - ["CrystalPvP"] = "crystal_pvp", - ["AutoTotem"] = "autototem", ["ThroughWalls"] = "dont_point_nodes", + ["AutoHit"] = "autohit", }, ["Movement"] = { ["Freecam"] = "freecam", - ["PrivBypass"] = "priv_bypass", ["AutoForward"] = "continuous_forward", ["PitchMove"] = "pitch_move", ["AutoJump"] = "autojump", ["Jesus"] = "jesus", ["NoSlow"] = "no_slow", - ["AutoSneak"] = "autosneak", - ["Autosprint"] = 'autosprint', ["AutoForwSprint"] = 'autofsprint', ["Jetpack"] = 'jetpack', + ["SpeedOverride"] = "override_speed", + ["JumpOverride"] = "override_jump", + ["GravityOverride"] = "override_gravity", + ["AntiSlip"] = "antislip", }, ["Render"] = { ["Xray"] = "xray", @@ -28,9 +27,14 @@ core.cheats = { ["NoHurtCam"] = "no_hurt_cam", ["BrightNight"] = "no_night", ["Coords"] = "coords", - ["Tracers"] = "enable_tracers", - ["ESP"] = "enable_esp", ["Clouds"] = "enable_clouds", + ["CheatHUD"] = "cheat_hud", + ["EntityESP"] = "enable_entity_esp", + ["EntityTracers"] = "enable_entity_tracers", + ["PlayerESP"] = "enable_player_esp", + ["PlayerTracers"] = "enable_player_tracers", + ["NodeESP"] = "enable_node_esp", + ["NodeTracers"] = "enable_node_tracers", }, ["World"] = { ["FastDig"] = "fastdig", @@ -38,14 +42,6 @@ core.cheats = { ["AutoDig"] = "autodig", ["AutoPlace"] = "autoplace", ["InstantBreak"] = "instant_break", - ["IncreasedRange"] = "increase_tool_range", - ["UnlimitedRange"] = "increase_tool_range_plus", - ["PointLiquids"] = "point_liquids", - ["Scaffold"] = "scaffold", - ["ScaffoldPlus"] = "scaffold_plus", - ["BlockWater"] = "block_water", - ["PlaceOnTop"] = "autotnt", - ["Replace"] = "replace", }, ["Exploit"] = { ["EntitySpeed"] = "entity_speed", @@ -62,15 +58,9 @@ core.cheats = { ["UnlimitedRange"] = "increase_tool_range_plus", ["PointLiquids"] = "point_liquids", ["PrivBypass"] = "priv_bypass", - ["AutoRespawn"] = "autorespawn", - }, - ["Inventory"] = { - ["AutoEject"] = "autoeject", - ["AutoTool"] = "autotool", - ["Enderchest"] = core.open_enderchest, - ["HandSlot"] = core.open_handslot, - ["NextItem"] = "next_item", }, + ["Chat"] = {}, + ["Inventory"] = {} } function core.register_cheat(cheatname, category, func) diff --git a/builtin/client/register.lua b/builtin/client/register.lua index 5e5196f78..1bc041c68 100644 --- a/builtin/client/register.lua +++ b/builtin/client/register.lua @@ -4,6 +4,13 @@ core.callback_origins = {} local getinfo = debug.getinfo debug.getinfo = nil +--- Runs given callbacks. +-- +-- Note: this function is also called from C++ +-- @tparam table callbacks a table with registered callbacks, like `core.registered_on_*` +-- @tparam number mode a RunCallbacksMode, as defined in src/script/common/c_internal.h +-- @param ... arguments for the callback +-- @return depends on mode function core.run_callbacks(callbacks, mode, ...) assert(type(callbacks) == "table") local cb_len = #callbacks @@ -83,6 +90,7 @@ core.registered_on_mods_loaded, core.register_on_mods_loaded = make_registration core.registered_on_shutdown, core.register_on_shutdown = make_registration() core.registered_on_receiving_chat_message, core.register_on_receiving_chat_message = make_registration() core.registered_on_sending_chat_message, core.register_on_sending_chat_message = make_registration() +core.registered_on_chatcommand, core.register_on_chatcommand = make_registration() core.registered_on_death, core.register_on_death = make_registration() core.registered_on_hp_modification, core.register_on_hp_modification = make_registration() core.registered_on_damage_taken, core.register_on_damage_taken = make_registration() @@ -96,3 +104,6 @@ core.registered_on_modchannel_signal, core.register_on_modchannel_signal = make_ core.registered_on_inventory_open, core.register_on_inventory_open = make_registration() core.registered_on_receiving_inventory_form, core.register_on_receiving_inventory_form = make_registration() core.registered_on_nodemeta_form_open, core.register_on_nodemeta_form_open = make_registration() +core.registered_on_recieve_physics_override, core.register_on_recieve_physics_override = make_registration() +core.registered_on_play_sound, core.register_on_play_sound = make_registration() +core.registered_on_spawn_particle, core.register_on_spawn_particle = make_registration() diff --git a/builtin/client/util.lua b/builtin/client/util.lua index 783d0ceb1..30f983af3 100644 --- a/builtin/client/util.lua +++ b/builtin/client/util.lua @@ -41,7 +41,7 @@ end function core.get_pointed_thing() local pos = core.camera:get_pos() - local pos2 = vector.add(pos, vector.multiply(core.camera:get_look_dir(), 5)) + local pos2 = vector.add(pos, vector.multiply(core.camera:get_look_dir(), 7)) local player = core.localplayer if not player then return end local item = player:get_wielded_item() @@ -49,4 +49,8 @@ function core.get_pointed_thing() local def = core.get_item_def(item:get_name()) local ray = core.raycast(pos, pos2, true, core.settings:get_bool("point_liquids") or def and def.liquids_pointable) return ray and ray:next() -end +end + +function core.close_formspec(formname) + return core.show_formspec(formname, "") +end diff --git a/builtin/common/after.lua b/builtin/common/after.lua index b314711c9..e20f292f0 100644 --- a/builtin/common/after.lua +++ b/builtin/common/after.lua @@ -31,11 +31,13 @@ function core.after(after, func, ...) assert(tonumber(after) and type(func) == "function", "Invalid minetest.after invocation") local expire = time + after - jobs[#jobs + 1] = { + local new_job = { func = func, expire = expire, arg = {...}, - mod_origin = core.get_last_run_mod() + mod_origin = core.get_last_run_mod(), } + jobs[#jobs + 1] = new_job time_next = math.min(time_next, expire) + return { cancel = function() new_job.func = function() end end } end diff --git a/builtin/common/chatcommands.lua b/builtin/common/chatcommands.lua index acde61a76..564a973db 100644 --- a/builtin/common/chatcommands.lua +++ b/builtin/common/chatcommands.lua @@ -29,6 +29,51 @@ function core.override_chatcommand(name, redefinition) core.registered_chatcommands[name] = chatcommand end +if INIT == "client" then + function core.register_list_command(command, desc, setting) + local def = {} + def.description = desc + def.params = "del | add | list" + function def.func(param) + local list = (minetest.settings:get(setting) or ""):split(",") + if param == "list" then + return true, table.concat(list, ", ") + else + local sparam = param:split(" ") + local cmd = sparam[1] + local item = sparam[2] + if cmd == "del" then + if not item then + return false, "Missing item." + end + local i = table.indexof(list, item) + if i == -1 then + return false, item .. " is not on the list." + else + table.remove(list, i) + core.settings:set(setting, table.concat(list, ",")) + return true, "Removed " .. item .. " from the list." + end + elseif cmd == "add" then + if not item then + return false, "Missing item." + end + local i = table.indexof(list, item) + if i ~= -1 then + return false, item .. " is already on the list." + else + table.insert(list, item) + core.settings:set(setting, table.concat(list, ",")) + return true, "Added " .. item .. " to the list." + end + end + end + return false, "Invalid usage. (See .help " .. command .. ")" + end + core.register_chatcommand(command, def) + end +end + local cmd_marker = "/" local function gettext(...) diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua index bd27a01dc..64d67bc72 100644 --- a/builtin/common/misc_helpers.lua +++ b/builtin/common/misc_helpers.lua @@ -594,6 +594,67 @@ function core.colorize(color, message) return table.concat(lines, "\n") .. core.get_color_escape_sequence("#ffffff") end +local function rgb_to_hex(rgb) + local hexadecimal = '#' + + for key, value in pairs(rgb) do + local hex = '' + + while(value > 0)do + local index = math.fmod(value, 16) + 1 + value = math.floor(value / 16) + hex = string.sub('0123456789ABCDEF', index, index) .. hex + end + + if(string.len(hex) == 0)then + hex = '00' + + elseif(string.len(hex) == 1)then + hex = '0' .. hex + end + + hexadecimal = hexadecimal .. hex + end + + return hexadecimal +end + +local function color_from_hue(hue) + local h = hue / 60 + local c = 255 + local x = (1 - math.abs(h%2 - 1)) * 255 + + local i = math.floor(h); + if (i == 0) then + return rgb_to_hex({c, x, 0}) + elseif (i == 1) then + return rgb_to_hex({x, c, 0}) + elseif (i == 2) then + return rgb_to_hex({0, c, x}) + elseif (i == 3) then + return rgb_to_hex({0, x, c}); + elseif (i == 4) then + return rgb_to_hex({x, 0, c}); + else + return rgb_to_hex({c, 0, x}); + end +end + +function core.rainbow(input) + local step = 360 / input:len() + local hue = 0 + local output = "" + for i = 1, input:len() do + local char = input:sub(i,i) + if char:match("%s") then + output = output .. char + else + output = output .. core.get_color_escape_sequence(color_from_hue(hue)) .. char + end + hue = hue + step + end + return output +end function core.strip_foreground_colors(str) return (str:gsub(ESCAPE_CHAR .. "%(c@[^)]+%)", "")) @@ -647,6 +708,19 @@ function core.get_translator(textdomain) return function(str, ...) return core.translate(textdomain or "", str, ...) end end +function core.get_pointed_thing_position(pointed_thing, above) + if pointed_thing.type == "node" then + if above then + -- The position where a node would be placed + return pointed_thing.above + end + -- The position where a node would be dug + return pointed_thing.under + elseif pointed_thing.type == "object" then + return pointed_thing.ref and pointed_thing.ref:get_pos() + end +end + -------------------------------------------------------------------------------- -- Returns the exact coordinate of a pointed surface -------------------------------------------------------------------------------- diff --git a/builtin/common/tests/vector_spec.lua b/builtin/common/tests/vector_spec.lua index 6f308a4a8..0f287363a 100644 --- a/builtin/common/tests/vector_spec.lua +++ b/builtin/common/tests/vector_spec.lua @@ -44,6 +44,10 @@ describe("vector", function() assert.same({ x = 2, y = 4, z = 6 }, vector.add(vector.new(1, 2, 3), { x = 1, y = 2, z = 3 })) end) + it("offset()", function() + assert.same({ x = 41, y = 52, z = 63 }, vector.offset(vector.new(1, 2, 3), 40, 50, 60)) + end) + -- This function is needed because of floating point imprecision. local function almost_equal(a, b) if type(a) == "number" then diff --git a/builtin/common/vector.lua b/builtin/common/vector.lua index 1fd784ce2..d6437deda 100644 --- a/builtin/common/vector.lua +++ b/builtin/common/vector.lua @@ -137,6 +137,12 @@ function vector.divide(a, b) end end +function vector.offset(v, x, y, z) + return {x = v.x + x, + y = v.y + y, + z = v.z + z} +end + function vector.sort(a, b) return {x = math.min(a.x, b.x), y = math.min(a.y, b.y), z = math.min(a.z, b.z)}, {x = math.max(a.x, b.x), y = math.max(a.y, b.y), z = math.max(a.z, b.z)} diff --git a/builtin/fstk/ui.lua b/builtin/fstk/ui.lua index 6d26aabf0..7eeebdd47 100644 --- a/builtin/fstk/ui.lua +++ b/builtin/fstk/ui.lua @@ -64,6 +64,7 @@ function ui.update() formspec = { "size[14,8]", "real_coordinates[true]", + "set_focus[btn_reconnect_yes;true]", "box[0.5,1.2;13,5;#000]", ("textarea[0.5,1.2;13,5;;%s;%s]"):format( fgettext("The server has requested a reconnect:"), error_message), @@ -82,6 +83,7 @@ function ui.update() formspec = { "size[14,8]", "real_coordinates[true]", + "set_focus[btn_error_confirm;true]", "box[0.5,1.2;13,5;#000]", ("textarea[0.5,1.2;13,5;;%s;%s]"):format( error_title, error_message), diff --git a/builtin/game/chat.lua b/builtin/game/chat.lua index aae811794..945707623 100644 --- a/builtin/game/chat.lua +++ b/builtin/game/chat.lua @@ -58,6 +58,11 @@ core.register_on_chat_message(function(name, message) param = param or "" + -- Run core.registered_on_chatcommands callbacks. + if core.run_callbacks(core.registered_on_chatcommands, 5, name, cmd, param) then + return true + end + local cmd_def = core.registered_chatcommands[cmd] if not cmd_def then core.chat_send_player(name, "-!- Invalid command: " .. cmd) @@ -66,8 +71,17 @@ core.register_on_chat_message(function(name, message) local has_privs, missing_privs = core.check_player_privs(name, cmd_def.privs) if has_privs then core.set_last_run_mod(cmd_def.mod_origin) - local _, result = cmd_def.func(name, param) - if result then + local success, result = cmd_def.func(name, param) + if success == false and result == nil then + core.chat_send_player(name, "-!- Invalid command usage") + local help_def = core.registered_chatcommands["help"] + if help_def then + local _, helpmsg = help_def.func(name, cmd) + if helpmsg then + core.chat_send_player(name, helpmsg) + end + end + elseif result then core.chat_send_player(name, result) end else @@ -1070,10 +1084,10 @@ core.register_chatcommand("last-login", { local pauth = core.get_auth_handler().get_auth(param) if pauth and pauth.last_login and pauth.last_login ~= -1 then -- Time in UTC, ISO 8601 format - return true, "Last login time was " .. + return true, param.."'s last login time was " .. os.date("!%Y-%m-%dT%H:%M:%SZ", pauth.last_login) end - return false, "Last login time is unknown" + return false, param.."'s last login time is unknown" end, }) diff --git a/builtin/game/falling.lua b/builtin/game/falling.lua index 714506a5f..8d044beaa 100644 --- a/builtin/game/falling.lua +++ b/builtin/game/falling.lua @@ -52,6 +52,7 @@ core.register_entity(":__builtin:falling_node", { floats = false, set_node = function(self, node, meta) + node.param2 = node.param2 or 0 self.node = node meta = meta or {} if type(meta.to_table) == "function" then @@ -83,6 +84,9 @@ core.register_entity(":__builtin:falling_node", { local textures if def.tiles and def.tiles[1] then local tile = def.tiles[1] + if def.drawtype == "torchlike" and def.paramtype2 ~= "wallmounted" then + tile = def.tiles[2] or def.tiles[1] + end if type(tile) == "table" then tile = tile.name end @@ -143,7 +147,11 @@ core.register_entity(":__builtin:falling_node", { -- Rotate entity if def.drawtype == "torchlike" then - self.object:set_yaw(math.pi*0.25) + if def.paramtype2 == "wallmounted" then + self.object:set_yaw(math.pi*0.25) + else + self.object:set_yaw(-math.pi*0.25) + end elseif (node.param2 ~= 0 and (def.wield_image == "" or def.wield_image == nil)) or def.drawtype == "signlike" diff --git a/builtin/game/features.lua b/builtin/game/features.lua index a15475333..4d3c90ff0 100644 --- a/builtin/game/features.lua +++ b/builtin/game/features.lua @@ -17,6 +17,7 @@ core.features = { area_store_persistent_ids = true, pathfinder_works = true, object_step_has_moveresult = true, + direct_velocity_on_players = true, } function core.has_feature(arg) diff --git a/builtin/game/item.lua b/builtin/game/item.lua index f680ce0d4..768df847b 100644 --- a/builtin/game/item.lua +++ b/builtin/game/item.lua @@ -24,19 +24,6 @@ function core.inventorycube(img1, img2, img3) .. "{" .. img3:gsub("%^", "&") end -function core.get_pointed_thing_position(pointed_thing, above) - if pointed_thing.type == "node" then - if above then - -- The position where a node would be placed - return pointed_thing.above - end - -- The position where a node would be dug - return pointed_thing.under - elseif pointed_thing.type == "object" then - return pointed_thing.ref and pointed_thing.ref:get_pos() - end -end - function core.dir_to_facedir(dir, is6d) --account for y if requested if is6d and math.abs(dir.y) > math.abs(dir.x) and math.abs(dir.y) > math.abs(dir.z) then @@ -551,8 +538,9 @@ function core.node_dig(pos, node, digger) local diggername = user_name(digger) local log = make_log(diggername) local def = core.registered_nodes[node.name] + -- Copy pos because the callback could modify it if def and (not def.diggable or - (def.can_dig and not def.can_dig(pos, digger))) then + (def.can_dig and not def.can_dig(vector.new(pos), digger))) then log("info", diggername .. " tried to dig " .. node.name .. " which is not diggable " .. core.pos_to_string(pos)) diff --git a/builtin/game/item_entity.lua b/builtin/game/item_entity.lua index 20dd18044..9b1b23bfd 100644 --- a/builtin/game/item_entity.lua +++ b/builtin/game/item_entity.lua @@ -54,8 +54,9 @@ core.register_entity(":__builtin:item", { local max_count = stack:get_stack_max() local count = math.min(stack:get_count(), max_count) local size = 0.2 + 0.1 * (count / max_count) ^ (1 / 3) - local def = core.registered_nodes[itemname] - local glow = def and math.floor(def.light_source / 2 + 0.5) + local def = core.registered_items[itemname] + local glow = def and def.light_source and + math.floor(def.light_source / 2 + 0.5) self.object:set_properties({ is_visible = true, diff --git a/builtin/game/misc.lua b/builtin/game/misc.lua index 341e613c2..96a0a2dda 100644 --- a/builtin/game/misc.lua +++ b/builtin/game/misc.lua @@ -151,6 +151,12 @@ function core.setting_get_pos(name) end +-- See l_env.cpp for the other functions +function core.get_artificial_light(param1) + return math.floor(param1 / 16) +end + + -- To be overriden by protection mods function core.is_protected(pos, name) diff --git a/builtin/game/register.lua b/builtin/game/register.lua index 1034d4f2b..1f94a9dca 100644 --- a/builtin/game/register.lua +++ b/builtin/game/register.lua @@ -118,6 +118,10 @@ function core.register_item(name, itemdef) end itemdef.name = name + -- default short_description to first line of description + itemdef.short_description = itemdef.short_description or + (itemdef.description or ""):gsub("\n.*","") + -- Apply defaults and add to registered_* table if itemdef.type == "node" then -- Use the nodebox as selection box if it's not set manually @@ -584,6 +588,7 @@ core.unregister_biome = make_wrap_deregistration(core.register_biome, core.clear_registered_biomes, core.registered_biomes) core.registered_on_chat_messages, core.register_on_chat_message = make_registration() +core.registered_on_chatcommands, core.register_on_chatcommand = make_registration() core.registered_globalsteps, core.register_globalstep = make_registration() core.registered_playerevents, core.register_playerevent = make_registration() core.registered_on_mods_loaded, core.register_on_mods_loaded = make_registration() diff --git a/builtin/mainmenu/dlg_contentstore.lua b/builtin/mainmenu/dlg_contentstore.lua index 01c42be0b..b3616bfc8 100644 --- a/builtin/mainmenu/dlg_contentstore.lua +++ b/builtin/mainmenu/dlg_contentstore.lua @@ -40,14 +40,17 @@ local num_per_page = 5 local filter_type = 1 local filter_types_titles = { fgettext("All packages"), - fgettext("Games"), - fgettext("Mods"), +-- fgettext("Games"), + fgettext("Clientmods"), fgettext("Texture packs"), } +local number_downloading = 0 +local download_queue = {} + local filter_types_type = { nil, - "game", +-- "game", "mod", "txp", } @@ -67,12 +70,14 @@ local function download_package(param) end end -local function start_install(calling_dialog, package) +local function start_install(package) local params = { package = package, filename = os.tempfolder() .. "_MODNAME_" .. package.name .. ".zip", } + number_downloading = number_downloading + 1 + local function callback(result) if result.successful then local path, msg = pkgmgr.install(package.type, @@ -121,9 +126,20 @@ local function start_install(calling_dialog, package) end package.downloading = false + + number_downloading = number_downloading - 1 + + local next = download_queue[1] + if next then + table.remove(download_queue, 1) + + start_install(next) + end + ui.update() end + package.queued = false package.downloading = true if not core.handle_async(download_package, params, callback) then @@ -133,6 +149,16 @@ local function start_install(calling_dialog, package) end end +local function queue_download(package) + local max_concurrent_downloads = tonumber(minetest.settings:get("contentdb_max_concurrent_downloads")) + if number_downloading < max_concurrent_downloads then + start_install(package) + else + table.insert(download_queue, package) + package.queued = true + end +end + local function get_file_extension(path) local parts = path:split(".") return parts[#parts] @@ -228,7 +254,7 @@ end function store.update_paths() local mod_hash = {} pkgmgr.refresh_globals() - for _, mod in pairs(pkgmgr.global_mods:get_list()) do + for _, mod in pairs(pkgmgr.clientmods:get_list()) do if mod.author then mod_hash[mod.author:lower() .. "/" .. mod.name] = mod end @@ -279,7 +305,7 @@ function store.filter_packages(query) table.insert(keywords, word) end - local function matches_keywords(package, keywords) + local function matches_keywords(package) for k = 1, #keywords do local keyword = keywords[k] @@ -296,7 +322,7 @@ function store.filter_packages(query) store.packages = {} for _, package in pairs(store.packages_full) do - if (query == "" or matches_keywords(package, keywords)) and + if (query == "" or matches_keywords(package)) and (filter_type == 1 or package.type == filter_types_type[filter_type]) then store.packages[#store.packages + 1] = package end @@ -321,11 +347,14 @@ function store.get_formspec(dlgdata) "formspec_version[3]", "size[15.75,9.5]", "position[0.5,0.55]", + + "style[status;border=false]", + "container[0.375,0.375]", - "field[0,0;10.225,0.8;search_string;;", core.formspec_escape(search_string), "]", + "field[0,0;7.225,0.8;search_string;;", core.formspec_escape(search_string), "]", "field_close_on_enter[search_string;false]", - "button[10.225,0;2,0.8;search;", fgettext("Search"), "]", - "dropdown[12.6,0;2.4,0.8;type;", table.concat(filter_types_titles, ","), ";", filter_type, "]", + "button[7.225,0;2,0.8;search;", fgettext("Search"), "]", + "dropdown[9.6,0;2.4,0.8;type;", table.concat(filter_types_titles, ","), ";", filter_type, "]", "container_end[]", -- Page nav buttons @@ -344,6 +373,35 @@ function store.get_formspec(dlgdata) "container_end[]", } + if number_downloading > 0 then + formspec[#formspec + 1] = "button[12.75,0.375;2.625,0.8;status;" + if #download_queue > 0 then + formspec[#formspec + 1] = fgettext("$1 downloading,\n$2 queued", number_downloading, #download_queue) + else + formspec[#formspec + 1] = fgettext("$1 downloading...", number_downloading) + end + formspec[#formspec + 1] = "]" + else + local num_avail_updates = 0 + for i=1, #store.packages_full do + local package = store.packages_full[i] + if package.path and package.installed_release < package.release and + not (package.downloading or package.queued) then + num_avail_updates = num_avail_updates + 1 + end + end + + if num_avail_updates == 0 then + formspec[#formspec + 1] = "button[12.75,0.375;2.625,0.8;status;" + formspec[#formspec + 1] = fgettext("No updates") + formspec[#formspec + 1] = "]" + else + formspec[#formspec + 1] = "button[12.75,0.375;2.625,0.8;update_all;" + formspec[#formspec + 1] = fgettext("Update All [$1]", num_avail_updates) + formspec[#formspec + 1] = "]" + end + end + if #store.packages == 0 then formspec[#formspec + 1] = "label[4,3;" formspec[#formspec + 1] = fgettext("No results") @@ -386,11 +444,13 @@ function store.get_formspec(dlgdata) formspec[#formspec + 1] = ",0.1]" if package.downloading then - formspec[#formspec + 1] = "style[download;border=false]" - - formspec[#formspec + 1] = "button[-3.5,0;2,0.8;download;" + formspec[#formspec + 1] = "button[-3.5,0;2,0.8;status;" formspec[#formspec + 1] = fgettext("Downloading...") formspec[#formspec + 1] = "]" + elseif package.queued then + formspec[#formspec + 1] = "button[-3.5,0;2,0.8;status;" + formspec[#formspec + 1] = fgettext("Queued") + formspec[#formspec + 1] = "]" elseif not package.path then formspec[#formspec + 1] = "button[-3,0;1.5,0.8;install_" formspec[#formspec + 1] = tostring(i) @@ -485,6 +545,17 @@ function store.handle_submit(this, fields) end end + if fields.update_all then + for i=1, #store.packages_full do + local package = store.packages_full[i] + if package.path and package.installed_release < package.release and + not (package.downloading or package.queued) then + queue_download(package) + end + end + return true + end + local start_idx = (cur_page - 1) * num_per_page + 1 assert(start_idx ~= nil) for i=start_idx, math.min(#store.packages, start_idx+num_per_page-1) do @@ -492,7 +563,7 @@ function store.handle_submit(this, fields) assert(package) if fields["install_" .. i] then - start_install(this, package) + queue_download(package) return true end @@ -505,8 +576,9 @@ function store.handle_submit(this, fields) end if fields["view_" .. i] then - local url = ("%s/packages/%s?protocol_version=%d"):format( - core.settings:get("contentdb_url"), package.id, core.get_max_supp_proto()) + local url = ("%s/packages/%s/%s?protocol_version=%d"):format( + core.settings:get("contentdb_url"), + package.author, package.name, core.get_max_supp_proto()) core.open_url(url) return true end diff --git a/builtin/mainmenu/dlg_create_world.lua b/builtin/mainmenu/dlg_create_world.lua index 36df23cce..7566d2409 100644 --- a/builtin/mainmenu/dlg_create_world.lua +++ b/builtin/mainmenu/dlg_create_world.lua @@ -61,6 +61,7 @@ local flag_checkboxes = { fgettext("Low humidity and high heat causes shallow or dry rivers") }, }, flat = { + cb_caverns, { "hills", fgettext("Hills"), "hills" }, { "lakes", fgettext("Lakes"), "lakes" }, }, @@ -362,10 +363,18 @@ local function create_world_buttonhandler(this, fields) local gameindex = core.get_textlist_index("games") if gameindex ~= nil then + -- For unnamed worlds use the generated name 'world', + -- where the number increments: it is set to 1 larger than the largest + -- generated name number found. if worldname == "" then - local random_number = math.random(10000, 99999) - local random_world_name = "Unnamed" .. random_number - worldname = random_world_name + local worldnum_max = 0 + for _, world in ipairs(menudata.worldlist:get_list()) do + if world.name:match("^world%d+$") then + local worldnum = tonumber(world.name:sub(6)) + worldnum_max = math.max(worldnum_max, worldnum) + end + end + worldname = "world" .. worldnum_max + 1 end core.settings:set("fixed_map_seed", fields["te_seed"]) diff --git a/builtin/mainmenu/init.lua b/builtin/mainmenu/init.lua index ae9ae8853..b7e867d2e 100644 --- a/builtin/mainmenu/init.lua +++ b/builtin/mainmenu/init.lua @@ -22,7 +22,6 @@ mt_color_dark_green = "#25C191" local menupath = core.get_mainmenu_path() local basepath = core.get_builtin_path() -local menustyle = core.settings:get("main_menu_style") defaulttexturedir = core.get_texturepath_share() .. DIR_DELIM .. "base" .. DIR_DELIM .. "pack" .. DIR_DELIM @@ -39,24 +38,18 @@ dofile(menupath .. DIR_DELIM .. "textures.lua") dofile(menupath .. DIR_DELIM .. "dlg_config_world.lua") dofile(menupath .. DIR_DELIM .. "dlg_settings_advanced.lua") dofile(menupath .. DIR_DELIM .. "dlg_contentstore.lua") -if menustyle ~= "simple" then - dofile(menupath .. DIR_DELIM .. "dlg_create_world.lua") - dofile(menupath .. DIR_DELIM .. "dlg_delete_content.lua") - dofile(menupath .. DIR_DELIM .. "dlg_delete_world.lua") - dofile(menupath .. DIR_DELIM .. "dlg_rename_modpack.lua") -end +dofile(menupath .. DIR_DELIM .. "dlg_create_world.lua") +dofile(menupath .. DIR_DELIM .. "dlg_delete_content.lua") +dofile(menupath .. DIR_DELIM .. "dlg_delete_world.lua") +dofile(menupath .. DIR_DELIM .. "dlg_rename_modpack.lua") local tabs = {} tabs.settings = dofile(menupath .. DIR_DELIM .. "tab_settings.lua") tabs.content = dofile(menupath .. DIR_DELIM .. "tab_content.lua") tabs.credits = dofile(menupath .. DIR_DELIM .. "tab_credits.lua") -if menustyle == "simple" then - tabs.simple_main = dofile(menupath .. DIR_DELIM .. "tab_simple_main.lua") -else - tabs.local_game = dofile(menupath .. DIR_DELIM .. "tab_local.lua") - tabs.play_online = dofile(menupath .. DIR_DELIM .. "tab_online.lua") -end +tabs.local_game = dofile(menupath .. DIR_DELIM .. "tab_local.lua") +tabs.play_online = dofile(menupath .. DIR_DELIM .. "tab_online.lua") -------------------------------------------------------------------------------- local function main_event_handler(tabview, event) @@ -71,68 +64,35 @@ local function init_globals() -- Init gamedata gamedata.worldindex = 0 - if menustyle == "simple" then - local world_list = core.get_worlds() - local world_index - - local found_singleplayerworld = false - for i, world in ipairs(world_list) do - if world.name == "singleplayerworld" then - found_singleplayerworld = true - world_index = i - break - end + menudata.worldlist = filterlist.create( + core.get_worlds, + compare_worlds, + -- Unique id comparison function + function(element, uid) + return element.name == uid + end, + -- Filter function + function(element, gameid) + return element.gameid == gameid end + ) - if not found_singleplayerworld then - core.create_world("singleplayerworld", 1) + menudata.worldlist:add_sort_mechanism("alphabetic", sort_worlds_alphabetic) + menudata.worldlist:set_sortmode("alphabetic") - world_list = core.get_worlds() - - for i, world in ipairs(world_list) do - if world.name == "singleplayerworld" then - world_index = i - break - end - end - end - - gamedata.worldindex = world_index - else - menudata.worldlist = filterlist.create( - core.get_worlds, - compare_worlds, - -- Unique id comparison function - function(element, uid) - return element.name == uid - end, - -- Filter function - function(element, gameid) - return element.gameid == gameid - end - ) - - menudata.worldlist:add_sort_mechanism("alphabetic", sort_worlds_alphabetic) - menudata.worldlist:set_sortmode("alphabetic") - - if not core.settings:get("menu_last_game") then - local default_game = core.settings:get("default_game") or "minetest" - core.settings:set("menu_last_game", default_game) - end - - mm_texture.init() + if not core.settings:get("menu_last_game") then + local default_game = core.settings:get("default_game") or "minetest" + core.settings:set("menu_last_game", default_game) end + mm_texture.init() + -- Create main tabview local tv_main = tabview_create("maintab", {x = 12, y = 5.4}, {x = 0, y = 0}) - if menustyle == "simple" then - tv_main:add(tabs.simple_main) - else - tv_main:set_autosave_tab(true) - tv_main:add(tabs.local_game) - tv_main:add(tabs.play_online) - end + tv_main:set_autosave_tab(true) + tv_main:add(tabs.local_game) + tv_main:add(tabs.play_online) tv_main:add(tabs.content) tv_main:add(tabs.settings) @@ -141,11 +101,9 @@ local function init_globals() tv_main:set_global_event_handler(main_event_handler) tv_main:set_fixed_size(false) - if menustyle ~= "simple" then - local last_tab = core.settings:get("maintab_LAST") - if last_tab and tv_main.current_tab ~= last_tab then - tv_main:set_tab(last_tab) - end + local last_tab = core.settings:get("maintab_LAST") + if last_tab and tv_main.current_tab ~= last_tab then + tv_main:set_tab(last_tab) end ui.set_default("maintab") tv_main:show() diff --git a/builtin/mainmenu/pkgmgr.lua b/builtin/mainmenu/pkgmgr.lua index 5b8807310..d4acb2b6a 100644 --- a/builtin/mainmenu/pkgmgr.lua +++ b/builtin/mainmenu/pkgmgr.lua @@ -592,7 +592,7 @@ function pkgmgr.install_dir(type, path, basename, targetpath) clean_path = get_last_folder(cleanup_path(basefolder.path)) end if clean_path then - targetpath = core.get_modpath() .. DIR_DELIM .. clean_path + targetpath = core.get_clientmodpath() .. DIR_DELIM .. clean_path else return nil, fgettext("Install Mod: Unable to find suitable folder name for modpack $1", @@ -619,7 +619,7 @@ function pkgmgr.install_dir(type, path, basename, targetpath) end if targetfolder ~= nil and pkgmgr.isValidModname(targetfolder) then - targetpath = core.get_modpath() .. DIR_DELIM .. targetfolder + targetpath = core.get_clientmodpath() .. DIR_DELIM .. targetfolder else return nil, fgettext("Install Mod: Unable to find real mod name for: $1", path) end @@ -671,6 +671,54 @@ function pkgmgr.install(type, modfilename, basename, dest) end -------------------------------------------------------------------------------- +function pkgmgr.prepareclientmodlist(data) + local retval = {} + + local clientmods = {} + + --read clientmods + local modpath = core.get_clientmodpath() + + if modpath ~= nil and + modpath ~= "" then + get_mods(modpath,clientmods) + end + + for i=1,#clientmods,1 do + clientmods[i].type = "mod" + clientmods[i].loc = "global" + clientmods[i].is_clientside = true + retval[#retval + 1] = clientmods[i] + end + + --read mods configuration + local filename = modpath .. + DIR_DELIM .. "mods.conf" + + local conffile = Settings(filename) + + for key,value in pairs(conffile:to_table()) do + if key:sub(1, 9) == "load_mod_" then + key = key:sub(10) + local element = nil + for i=1,#retval,1 do + if retval[i].name == key and + not retval[i].is_modpack then + element = retval[i] + break + end + end + if element ~= nil then + element.enabled = value ~= "false" and value ~= "nil" and value + else + core.log("info", "Clientmod: " .. key .. " " .. dump(value) .. " but not found") + end + end + end + + return retval +end + function pkgmgr.preparemodlist(data) local retval = {} @@ -813,6 +861,10 @@ function pkgmgr.refresh_globals() pkgmgr.comparemod, is_equal, nil, {}) pkgmgr.global_mods:add_sort_mechanism("alphabetic", sort_mod_list) pkgmgr.global_mods:set_sortmode("alphabetic") + pkgmgr.clientmods = filterlist.create(pkgmgr.prepareclientmodlist, + pkgmgr.comparemod, is_equal, nil, {}) + pkgmgr.clientmods:add_sort_mechanism("alphabetic", sort_mod_list) + pkgmgr.clientmods:set_sortmode("alphabetic") end -------------------------------------------------------------------------------- diff --git a/builtin/mainmenu/tab_content.lua b/builtin/mainmenu/tab_content.lua index 336730bf4..623210597 100644 --- a/builtin/mainmenu/tab_content.lua +++ b/builtin/mainmenu/tab_content.lua @@ -19,6 +19,10 @@ local packages_raw local packages +local function modname_valid(name) + return not name:find("[^a-z0-9_]") +end + -------------------------------------------------------------------------------- local function get_formspec(tabview, name, tabdata) if pkgmgr.global_mods == nil then @@ -33,6 +37,7 @@ local function get_formspec(tabview, name, tabdata) table.insert_all(packages_raw, pkgmgr.games) table.insert_all(packages_raw, pkgmgr.get_texture_packs()) table.insert_all(packages_raw, pkgmgr.global_mods:get_list()) + table.insert_all(packages_raw, pkgmgr.clientmods:get_list()) local function get_data() return packages_raw @@ -45,6 +50,38 @@ local function get_formspec(tabview, name, tabdata) packages = filterlist.create(get_data, pkgmgr.compare_package, is_equal, nil, {}) + + local filename = core.get_clientmodpath() .. DIR_DELIM .. "mods.conf" + + local conffile = Settings(filename) + local mods = conffile:to_table() + + for i = 1, #packages_raw do + local mod = packages_raw[i] + if mod.is_clientside and not mod.is_modpack then + if modname_valid(mod.name) then + conffile:set("load_mod_" .. mod.name, + mod.enabled and "true" or "false") + elseif mod.enabled then + gamedata.errormessage = fgettext_ne("Failed to enable clientmo" .. + "d \"$1\" as it contains disallowed characters. " .. + "Only characters [a-z0-9_] are allowed.", + mod.name) + end + mods["load_mod_" .. mod.name] = nil + end + end + + -- Remove mods that are not present anymore + for key in pairs(mods) do + if key:sub(1, 9) == "load_mod_" then + conffile:remove(key) + end + end + + if not conffile:write() then + core.log("error", "Failed to write clientmod config file") + end end if tabdata.selected_pkg == nil then @@ -94,9 +131,21 @@ local function get_formspec(tabview, name, tabdata) if selected_pkg.type == "mod" then if selected_pkg.is_modpack then - retval = retval .. - "button[8.65,4.65;3.25,1;btn_mod_mgr_rename_modpack;" .. - fgettext("Rename") .. "]" + if selected_pkg.is_clientside then + if pkgmgr.is_modpack_entirely_enabled({list = packages}, selected_pkg.name) then + retval = retval .. + "button[8.65,4.65;3.25,1;btn_mod_mgr_mp_disable;" .. + fgettext("Disable modpack") .. "]" + else + retval = retval .. + "button[8.65,4.65;3.25,1;btn_mod_mgr_mp_enable;" .. + fgettext("Enable modpack") .. "]" + end + else + retval = retval .. + "button[8.65,4.65;3.25,1;btn_mod_mgr_rename_modpack;" .. + fgettext("Rename") .. "]" + end else --show dependencies desc = desc .. "\n\n" @@ -117,6 +166,17 @@ local function get_formspec(tabview, name, tabdata) "\n" .. toadd_soft end end + if selected_pkg.is_clientside then + if selected_pkg.enabled then + retval = retval .. + "button[8.65,4.65;3.25,1;btn_mod_mgr_disable_mod;" .. + fgettext("Disable") .. "]" + else + retval = retval .. + "button[8.65,4.65;3.25,1;btn_mod_mgr_enable_mod;" .. + fgettext("Enable") .. "]" + end + end end else @@ -150,6 +210,26 @@ local function handle_buttons(tabview, fields, tabname, tabdata) if fields["pkglist"] ~= nil then local event = core.explode_table_event(fields["pkglist"]) tabdata.selected_pkg = event.row + local mod = packages:get_list()[tabdata.selected_pkg] + + if event.type == "DCL" and mod.is_clientside then + pkgmgr.enable_mod({data = {list = packages, selected_mod = tabdata.selected_pkg}}) + packages = nil + end + return true + end + + if fields.btn_mod_mgr_mp_enable ~= nil or + fields.btn_mod_mgr_mp_disable ~= nil then + pkgmgr.enable_mod({data = {list = packages, selected_mod = tabdata.selected_pkg}}, fields.btn_mod_mgr_mp_enable ~= nil) + packages = nil + return true + end + + if fields.btn_mod_mgr_enable_mod ~= nil or + fields.btn_mod_mgr_disable_mod ~= nil then + pkgmgr.enable_mod({data = {list = packages, selected_mod = tabdata.selected_pkg}}, fields.btn_mod_mgr_enable_mod ~= nil) + packages = nil return true end diff --git a/builtin/mainmenu/tab_credits.lua b/builtin/mainmenu/tab_credits.lua index c568eddb5..617823319 100644 --- a/builtin/mainmenu/tab_credits.lua +++ b/builtin/mainmenu/tab_credits.lua @@ -17,8 +17,13 @@ -------------------------------------------------------------------------------- local dragonfire_team = { - "Elias Fleckenstein [Main Developer]", - "DerZombiiie [Bots, User Support]", + "Elias Fleckenstein [Main Developer]", + "cora [Core Developer]", + "emilia [Core Developer]", + "oneplustwo [Developer]", + "joshia_wi [Developer]", + "Code-Sploit [Developer]", + "DerZombiiie [User Support]", } local core_developers = { diff --git a/builtin/mainmenu/tab_local.lua b/builtin/mainmenu/tab_local.lua index a21cf12b1..1aee246fc 100644 --- a/builtin/mainmenu/tab_local.lua +++ b/builtin/mainmenu/tab_local.lua @@ -114,45 +114,44 @@ local function get_formspec(tabview, name, tabdata) ) retval = retval .. - "button[4,3.95;2.6,1;world_delete;".. fgettext("Delete") .. "]" .. - "button[6.5,3.95;2.8,1;world_configure;".. fgettext("Configure") .. "]" .. - "button[9.2,3.95;2.5,1;world_create;".. fgettext("New") .. "]" .. - "label[4,-0.25;".. fgettext("Select World:") .. "]".. - "checkbox[0.25,0.25;cb_creative_mode;".. fgettext("Creative Mode") .. ";" .. + "button[3.9,3.8;2.8,1;world_delete;".. fgettext("Delete") .. "]" .. + "button[6.55,3.8;2.8,1;world_configure;".. fgettext("Configure") .. "]" .. + "button[9.2,3.8;2.8,1;world_create;".. fgettext("New") .. "]" .. + "label[3.9,-0.05;".. fgettext("Select World:") .. "]".. + "checkbox[0,-0.20;cb_creative_mode;".. fgettext("Creative Mode") .. ";" .. dump(core.settings:get_bool("creative_mode")) .. "]".. - "checkbox[0.25,0.7;cb_enable_damage;".. fgettext("Enable Damage") .. ";" .. + "checkbox[0,0.25;cb_enable_damage;".. fgettext("Enable Damage") .. ";" .. dump(core.settings:get_bool("enable_damage")) .. "]".. - "checkbox[0.25,1.15;cb_server;".. fgettext("Host Server") ..";" .. + "checkbox[0,0.7;cb_server;".. fgettext("Host Server") ..";" .. dump(core.settings:get_bool("enable_server")) .. "]" .. - "textlist[4,0.25;7.5,3.7;sp_worlds;" .. + "textlist[3.9,0.4;7.9,3.45;sp_worlds;" .. menu_render_worldlist() .. ";" .. index .. "]" if core.settings:get_bool("enable_server") then retval = retval .. - "button[8.5,4.8;3.2,1;play;".. fgettext("Host Game") .. "]" .. - "checkbox[0.25,1.6;cb_server_announce;" .. fgettext("Announce Server") .. ";" .. + "button[7.9,4.75;4.1,1;play;".. fgettext("Host Game") .. "]" .. + "checkbox[0,1.15;cb_server_announce;" .. fgettext("Announce Server") .. ";" .. dump(core.settings:get_bool("server_announce")) .. "]" .. - "label[0.25,2.2;" .. fgettext("Name/Password") .. "]" .. - "field[0.55,3.2;3.5,0.5;te_playername;;" .. + "field[0.3,2.85;3.8,0.5;te_playername;" .. fgettext("Name") .. ";" .. core.formspec_escape(core.settings:get("name")) .. "]" .. - "pwdfield[0.55,4;3.5,0.5;te_passwd;]" + "pwdfield[0.3,4.05;3.8,0.5;te_passwd;" .. fgettext("Password") .. "]" local bind_addr = core.settings:get("bind_address") if bind_addr ~= nil and bind_addr ~= "" then retval = retval .. - "field[0.55,5.2;2.25,0.5;te_serveraddr;" .. fgettext("Bind Address") .. ";" .. + "field[0.3,5.25;2.5,0.5;te_serveraddr;" .. fgettext("Bind Address") .. ";" .. core.formspec_escape(core.settings:get("bind_address")) .. "]" .. - "field[2.8,5.2;1.25,0.5;te_serverport;" .. fgettext("Port") .. ";" .. + "field[2.85,5.25;1.25,0.5;te_serverport;" .. fgettext("Port") .. ";" .. core.formspec_escape(core.settings:get("port")) .. "]" else retval = retval .. - "field[0.55,5.2;3.5,0.5;te_serverport;" .. fgettext("Server Port") .. ";" .. + "field[0.3,5.25;3.8,0.5;te_serverport;" .. fgettext("Server Port") .. ";" .. core.formspec_escape(core.settings:get("port")) .. "]" end else retval = retval .. - "button[8.5,4.8;3.2,1;play;".. fgettext("Play Game") .. "]" + "button[7.9,4.75;4.1,1;play;" .. fgettext("Play Game") .. "]" end return retval diff --git a/builtin/mainmenu/tab_settings.lua b/builtin/mainmenu/tab_settings.lua index 1e5264904..29744048a 100644 --- a/builtin/mainmenu/tab_settings.lua +++ b/builtin/mainmenu/tab_settings.lua @@ -122,56 +122,6 @@ local function antialiasing_fname_to_name(fname) return 0 end -local function dlg_confirm_reset_formspec(data) - return "size[8,3]" .. - "label[1,1;" .. fgettext("Are you sure to reset your singleplayer world?") .. "]" .. - "button[1,2;2.6,0.5;dlg_reset_singleplayer_confirm;" .. fgettext("Yes") .. "]" .. - "button[4,2;2.8,0.5;dlg_reset_singleplayer_cancel;" .. fgettext("No") .. "]" -end - -local function dlg_confirm_reset_btnhandler(this, fields, dialogdata) - - if fields["dlg_reset_singleplayer_confirm"] ~= nil then - local worldlist = core.get_worlds() - local found_singleplayerworld = false - - for i = 1, #worldlist do - if worldlist[i].name == "singleplayerworld" then - found_singleplayerworld = true - gamedata.worldindex = i - end - end - - if found_singleplayerworld then - core.delete_world(gamedata.worldindex) - end - - core.create_world("singleplayerworld", 1) - worldlist = core.get_worlds() - - for i = 1, #worldlist do - if worldlist[i].name == "singleplayerworld" then - gamedata.worldindex = i - end - end - end - - this.parent:show() - this:hide() - this:delete() - return true -end - -local function showconfirm_reset(tabview) - local new_dlg = dialog_create("reset_spworld", - dlg_confirm_reset_formspec, - dlg_confirm_reset_btnhandler, - nil) - new_dlg:set_parent(tabview) - tabview:hide() - new_dlg:show() -end - local function formspec(tabview, name, tabdata) local tab_string = "box[0,0;3.75,4.5;#999999]" .. @@ -204,30 +154,26 @@ local function formspec(tabview, name, tabdata) "box[8,0;3.75,4.5;#999999]" local video_driver = core.settings:get("video_driver") - local shaders_supported = video_driver == "opengl" - local shaders_enabled = false - if shaders_supported then - shaders_enabled = core.settings:get_bool("enable_shaders") + local shaders_enabled = core.settings:get_bool("enable_shaders") + if video_driver == "opengl" then tab_string = tab_string .. "checkbox[8.25,0;cb_shaders;" .. fgettext("Shaders") .. ";" .. tostring(shaders_enabled) .. "]" + elseif video_driver == "ogles2" then + tab_string = tab_string .. + "checkbox[8.25,0;cb_shaders;" .. fgettext("Shaders (experimental)") .. ";" + .. tostring(shaders_enabled) .. "]" else core.settings:set_bool("enable_shaders", false) + shaders_enabled = false tab_string = tab_string .. "label[8.38,0.2;" .. core.colorize("#888888", fgettext("Shaders (unavailable)")) .. "]" end - if core.settings:get("main_menu_style") == "simple" then - -- 'Reset singleplayer world' only functions with simple menu - tab_string = tab_string .. - "button[8,4.75;3.95,1;btn_reset_singleplayer;" - .. fgettext("Reset singleplayer world") .. "]" - else - tab_string = tab_string .. - "button[8,4.75;3.95,1;btn_change_keys;" - .. fgettext("Change Keys") .. "]" - end + tab_string = tab_string .. + "button[8,4.75;3.95,1;btn_change_keys;" + .. fgettext("Change Keys") .. "]" tab_string = tab_string .. "button[0,4.75;3.95,1;btn_advanced_settings;" @@ -244,35 +190,23 @@ local function formspec(tabview, name, tabdata) if shaders_enabled then tab_string = tab_string .. - "checkbox[8.25,0.5;cb_bumpmapping;" .. fgettext("Bump Mapping") .. ";" - .. dump(core.settings:get_bool("enable_bumpmapping")) .. "]" .. - "checkbox[8.25,1;cb_tonemapping;" .. fgettext("Tone Mapping") .. ";" + "checkbox[8.25,0.5;cb_tonemapping;" .. fgettext("Tone Mapping") .. ";" .. dump(core.settings:get_bool("tone_mapping")) .. "]" .. - "checkbox[8.25,1.5;cb_generate_normalmaps;" .. fgettext("Generate Normal Maps") .. ";" - .. dump(core.settings:get_bool("generate_normalmaps")) .. "]" .. - "checkbox[8.25,2;cb_parallax;" .. fgettext("Parallax Occlusion") .. ";" - .. dump(core.settings:get_bool("enable_parallax_occlusion")) .. "]" .. - "checkbox[8.25,2.5;cb_waving_water;" .. fgettext("Waving Liquids") .. ";" + "checkbox[8.25,1;cb_waving_water;" .. fgettext("Waving Liquids") .. ";" .. dump(core.settings:get_bool("enable_waving_water")) .. "]" .. - "checkbox[8.25,3;cb_waving_leaves;" .. fgettext("Waving Leaves") .. ";" + "checkbox[8.25,1.5;cb_waving_leaves;" .. fgettext("Waving Leaves") .. ";" .. dump(core.settings:get_bool("enable_waving_leaves")) .. "]" .. - "checkbox[8.25,3.5;cb_waving_plants;" .. fgettext("Waving Plants") .. ";" + "checkbox[8.25,2;cb_waving_plants;" .. fgettext("Waving Plants") .. ";" .. dump(core.settings:get_bool("enable_waving_plants")) .. "]" else tab_string = tab_string .. "label[8.38,0.7;" .. core.colorize("#888888", - fgettext("Bump Mapping")) .. "]" .. - "label[8.38,1.2;" .. core.colorize("#888888", fgettext("Tone Mapping")) .. "]" .. - "label[8.38,1.7;" .. core.colorize("#888888", - fgettext("Generate Normal Maps")) .. "]" .. - "label[8.38,2.2;" .. core.colorize("#888888", - fgettext("Parallax Occlusion")) .. "]" .. - "label[8.38,2.7;" .. core.colorize("#888888", + "label[8.38,1.2;" .. core.colorize("#888888", fgettext("Waving Liquids")) .. "]" .. - "label[8.38,3.2;" .. core.colorize("#888888", + "label[8.38,1.7;" .. core.colorize("#888888", fgettext("Waving Leaves")) .. "]" .. - "label[8.38,3.7;" .. core.colorize("#888888", + "label[8.38,2.2;" .. core.colorize("#888888", fgettext("Waving Plants")) .. "]" end @@ -324,22 +258,10 @@ local function handle_settings_buttons(this, fields, tabname, tabdata) end return true end - if fields["cb_bumpmapping"] then - core.settings:set("enable_bumpmapping", fields["cb_bumpmapping"]) - return true - end if fields["cb_tonemapping"] then core.settings:set("tone_mapping", fields["cb_tonemapping"]) return true end - if fields["cb_generate_normalmaps"] then - core.settings:set("generate_normalmaps", fields["cb_generate_normalmaps"]) - return true - end - if fields["cb_parallax"] then - core.settings:set("enable_parallax_occlusion", fields["cb_parallax"]) - return true - end if fields["cb_waving_water"] then core.settings:set("enable_waving_water", fields["cb_waving_water"]) return true @@ -359,10 +281,6 @@ local function handle_settings_buttons(this, fields, tabname, tabdata) core.settings:set("touchtarget", fields["cb_touchscreen_target"]) return true end - if fields["btn_reset_singleplayer"] then - showconfirm_reset(this) - return true - end --Note dropdowns have to be handled LAST! local ddhandled = false diff --git a/builtin/mainmenu/tab_simple_main.lua b/builtin/mainmenu/tab_simple_main.lua deleted file mode 100644 index 7ec95158a..000000000 --- a/builtin/mainmenu/tab_simple_main.lua +++ /dev/null @@ -1,220 +0,0 @@ ---Minetest ---Copyright (C) 2013 sapier --- ---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 2.1 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. - --------------------------------------------------------------------------------- -local function get_formspec(tabview, name, tabdata) - -- Update the cached supported proto info, - -- it may have changed after a change by the settings menu. - common_update_cached_supp_proto() - local fav_selected = menudata.favorites[tabdata.fav_selected] - - local retval = - "label[9.5,0;".. fgettext("Name / Password") .. "]" .. - "field[0.25,3.35;5.5,0.5;te_address;;" .. - core.formspec_escape(core.settings:get("address")) .."]" .. - "field[5.75,3.35;2.25,0.5;te_port;;" .. - core.formspec_escape(core.settings:get("remote_port")) .."]" .. - "button[10,2.6;2,1.5;btn_mp_connect;".. fgettext("Connect") .. "]" .. - "field[9.8,1;2.6,0.5;te_name;;" .. - core.formspec_escape(core.settings:get("name")) .."]" .. - "pwdfield[9.8,2;2.6,0.5;te_pwd;]" - - - if tabdata.fav_selected and fav_selected then - if gamedata.fav then - retval = retval .. "button[7.7,2.6;2.3,1.5;btn_delete_favorite;" .. - fgettext("Del. Favorite") .. "]" - end - end - - retval = retval .. "tablecolumns[" .. - image_column(fgettext("Favorite"), "favorite") .. ";" .. - image_column(fgettext("Ping"), "") .. ",padding=0.25;" .. - "color,span=3;" .. - "text,align=right;" .. -- clients - "text,align=center,padding=0.25;" .. -- "/" - "text,align=right,padding=0.25;" .. -- clients_max - image_column(fgettext("Creative mode"), "creative") .. ",padding=1;" .. - image_column(fgettext("Damage enabled"), "damage") .. ",padding=0.25;" .. - image_column(fgettext("PvP enabled"), "pvp") .. ",padding=0.25;" .. - "color,span=1;" .. - "text,padding=1]" .. -- name - "table[-0.05,0;9.2,2.75;favourites;" - - if #menudata.favorites > 0 then - local favs = core.get_favorites("local") - if #favs > 0 then - for i = 1, #favs do - for j = 1, #menudata.favorites do - if menudata.favorites[j].address == favs[i].address and - menudata.favorites[j].port == favs[i].port then - table.insert(menudata.favorites, i, - table.remove(menudata.favorites, j)) - end - end - if favs[i].address ~= menudata.favorites[i].address then - table.insert(menudata.favorites, i, favs[i]) - end - end - end - retval = retval .. render_serverlist_row(menudata.favorites[1], (#favs > 0)) - for i = 2, #menudata.favorites do - retval = retval .. "," .. render_serverlist_row(menudata.favorites[i], (i <= #favs)) - end - end - - if tabdata.fav_selected then - retval = retval .. ";" .. tabdata.fav_selected .. "]" - else - retval = retval .. ";0]" - end - - -- separator - retval = retval .. "box[-0.28,3.75;12.4,0.1;#FFFFFF]" - - -- checkboxes - retval = retval .. - "checkbox[8.0,3.9;cb_creative;".. fgettext("Creative Mode") .. ";" .. - dump(core.settings:get_bool("creative_mode")) .. "]".. - "checkbox[8.0,4.4;cb_damage;".. fgettext("Enable Damage") .. ";" .. - dump(core.settings:get_bool("enable_damage")) .. "]" - -- buttons - retval = retval .. - "button[0,3.7;8,1.5;btn_start_singleplayer;" .. fgettext("Start Singleplayer") .. "]" .. - "button[0,4.5;8,1.5;btn_config_sp_world;" .. fgettext("Config mods") .. "]" - - return retval -end - --------------------------------------------------------------------------------- -local function main_button_handler(tabview, fields, name, tabdata) - if fields.btn_start_singleplayer then - gamedata.selected_world = gamedata.worldindex - gamedata.singleplayer = true - core.start() - return true - end - - if fields.favourites then - local event = core.explode_table_event(fields.favourites) - if event.type == "CHG" then - if event.row <= #menudata.favorites then - gamedata.fav = false - local favs = core.get_favorites("local") - local fav = menudata.favorites[event.row] - local address = fav.address - local port = fav.port - gamedata.serverdescription = fav.description - - for i = 1, #favs do - if fav.address == favs[i].address and - fav.port == favs[i].port then - gamedata.fav = true - end - end - - if address and port then - core.settings:set("address", address) - core.settings:set("remote_port", port) - end - tabdata.fav_selected = event.row - end - return true - end - end - - if fields.btn_delete_favorite then - local current_favourite = core.get_table_index("favourites") - if not current_favourite then return end - - core.delete_favorite(current_favourite) - asyncOnlineFavourites() - tabdata.fav_selected = nil - - core.settings:set("address", "") - core.settings:set("remote_port", "30000") - return true - end - - if fields.cb_creative then - core.settings:set("creative_mode", fields.cb_creative) - return true - end - - if fields.cb_damage then - core.settings:set("enable_damage", fields.cb_damage) - return true - end - - if fields.btn_mp_connect or fields.key_enter then - gamedata.playername = fields.te_name - gamedata.password = fields.te_pwd - gamedata.address = fields.te_address - gamedata.port = fields.te_port - local fav_idx = core.get_textlist_index("favourites") - - if fav_idx and fav_idx <= #menudata.favorites and - menudata.favorites[fav_idx].address == fields.te_address and - menudata.favorites[fav_idx].port == fields.te_port then - local fav = menudata.favorites[fav_idx] - gamedata.servername = fav.name - gamedata.serverdescription = fav.description - - if menudata.favorites_is_public and - not is_server_protocol_compat_or_error( - fav.proto_min, fav.proto_max) then - return true - end - else - gamedata.servername = "" - gamedata.serverdescription = "" - end - - gamedata.selected_world = 0 - - core.settings:set("address", fields.te_address) - core.settings:set("remote_port", fields.te_port) - - core.start() - return true - end - - if fields.btn_config_sp_world then - local configdialog = create_configure_world_dlg(1) - if configdialog then - configdialog:set_parent(tabview) - tabview:hide() - configdialog:show() - end - return true - end -end - --------------------------------------------------------------------------------- -local function on_activate(type,old_tab,new_tab) - if type == "LEAVE" then return end - asyncOnlineFavourites() -end - --------------------------------------------------------------------------------- -return { - name = "main", - caption = fgettext("Main"), - cbf_formspec = get_formspec, - cbf_button_handler = main_button_handler, - on_change = on_activate -} diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index e80a7f53e..344762a6b 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -110,9 +110,9 @@ doubletap_jump (Double tap jump for fly) bool false # enabled. always_fly_fast (Always fly and fast) bool true -# The time in seconds it takes between repeated right clicks when holding the right -# mouse button. -repeat_rightclick_time (Rightclick repetition interval) float 0.25 0.001 +# The time in seconds it takes between repeated node placements when holding +# the place button. +repeat_place_time (Place repetition interval) float 0.25 0.001 # Automatically jump up single-node obstacles. autojump (Automatic jumping) bool false @@ -182,6 +182,14 @@ keymap_jump (Jump key) key KEY_SPACE # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 keymap_sneak (Sneak key) key KEY_LSHIFT +# Key for digging. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_dig (Dig key) key KEY_LBUTTON + +# Key for placing. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_place (Place key) key KEY_RBUTTON + # Key for opening the inventory. # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 keymap_inventory (Inventory key) key KEY_KEY_I @@ -517,8 +525,13 @@ texture_clean_transparent (Clean transparent textures) bool false # texture autoscaling. texture_min_size (Minimum texture size) int 64 -# Experimental option, might cause visible spaces between blocks -# when set to higher number than 0. +# Use multi-sample antialiasing (MSAA) to smooth out block edges. +# This algorithm smooths out the 3D viewport while keeping the image sharp, +# but it doesn't affect the insides of textures +# (which is especially noticeable with transparent textures). +# This option is experimental and might cause visible spaces between blocks +# when set above 0. +# A restart is required after changing this option. fsaa (FSAA) enum 0 0,1,2,4,8,16 # Undersampling is similar to using a lower screen resolution, but it applies @@ -545,43 +558,6 @@ shader_path (Shader path) path # enhanced, highlights and shadows are gradually compressed. tone_mapping (Filmic tone mapping) bool false -[***Bumpmapping] - -# Enables bumpmapping for textures. Normalmaps need to be supplied by the texture pack -# or need to be auto-generated. -# Requires shaders to be enabled. -enable_bumpmapping (Bumpmapping) bool false - -# Enables on the fly normalmap generation (Emboss effect). -# Requires bumpmapping to be enabled. -generate_normalmaps (Generate normalmaps) bool false - -# Strength of generated normalmaps. -normalmaps_strength (Normalmaps strength) float 0.6 - -# Defines sampling step of texture. -# A higher value results in smoother normal maps. -normalmaps_smooth (Normalmaps sampling) int 0 0 2 - -[***Parallax Occlusion] - -# Enables parallax occlusion mapping. -# Requires shaders to be enabled. -enable_parallax_occlusion (Parallax occlusion) bool false - -# 0 = parallax occlusion with slope information (faster). -# 1 = relief mapping (slower, more accurate). -parallax_occlusion_mode (Parallax occlusion mode) int 1 0 1 - -# Number of parallax occlusion iterations. -parallax_occlusion_iterations (Parallax occlusion iterations) int 4 - -# Overall scale of parallax occlusion effect. -parallax_occlusion_scale (Parallax occlusion scale) float 0.08 - -# Overall bias of parallax occlusion effect, usually scale/2. -parallax_occlusion_bias (Parallax occlusion bias) float 0.04 - [***Waving Nodes] # Set to true to enable waving liquids (like water). @@ -622,15 +598,15 @@ arm_inertia (Arm inertia) bool true # to not waste CPU power for no benefit. fps_max (Maximum FPS) int 60 1 -# Maximum FPS when game is paused. -pause_fps_max (FPS in pause menu) int 20 1 +# Maximum FPS when the window is not focused, or when the game is paused. +fps_max_unfocused (FPS when unfocused or paused) int 20 1 # Open the pause menu when the window's focus is lost. Does not pause if a formspec is # open. pause_on_lost_focus (Pause on lost window focus) bool false # View distance in nodes. -viewing_range (Viewing range) int 100 20 4000 +viewing_range (Viewing range) int 190 20 4000 # Camera 'near clipping plane' distance in nodes, between 0 and 0.25 # Only works on GLES platforms. Most users will not need to change this. @@ -694,8 +670,8 @@ texture_path (Texture path) path # The rendering back-end for Irrlicht. # A restart is required after changing this. # Note: On Android, stick with OGLES1 if unsure! App may fail to start otherwise. -# On other platforms, OpenGL is recommended, and it’s the only driver with -# shader support currently. +# On other platforms, OpenGL is recommended. +# Shaders are supported by OpenGL (desktop only) and OGLES2 (experimental) video_driver (Video driver) enum opengl null,software,burningsvideo,direct3d8,direct3d9,opengl,ogles1,ogles2 # Radius of cloud area stated in number of 64 node cloud squares. @@ -783,7 +759,7 @@ mesh_generation_interval (Mapblock mesh generation delay) int 0 0 50 # Size of the MapBlock cache of the mesh generator. Increasing this will # increase the cache hit %, reducing the data being copied from the main # thread, thus reducing jitter. -meshgen_block_cache_size (Mapblock mesh generator's MapBlock cache size in MB) int 20 0 1000 +meshgen_block_cache_size (Mapblock mesh generator's MapBlock cache size in MB) int 40 0 1000 # Enables minimap. enable_minimap (Minimap) bool true @@ -1014,7 +990,7 @@ client_unload_unused_data_timeout (Mapblock unload timeout) int 600 # Maximum number of mapblocks for client to be kept in memory. # Set to -1 for unlimited amount. -client_mapblock_limit (Mapblock limit) int 5000 +client_mapblock_limit (Mapblock limit) int 7500 # Whether to show the client debug info (has the same effect as hitting F5). show_debug (Show debug info) bool false @@ -1073,7 +1049,7 @@ ipv6_server (IPv6 server) bool false # Maximum number of blocks that are simultaneously sent per client. # The maximum total count is calculated dynamically: # max_total = ceil((#clients + max_users) * per_client / 4) -max_simultaneous_block_sends_per_client (Maximum simultaneous block sends per client) int 40 +max_simultaneous_block_sends_per_client (Maximum simultaneous block sends per client) int 128 # To reduce lag, block transfers are slowed down when a player is building something. # This determines how long they are slowed down after placing or removing a node. @@ -1173,17 +1149,17 @@ ask_reconnect_on_crash (Ask to reconnect after crash) bool false # Setting this larger than active_block_range will also cause the server # to maintain active objects up to this distance in the direction the # player is looking. (This can avoid mobs suddenly disappearing from view) -active_object_send_range_blocks (Active object send range) int 4 +active_object_send_range_blocks (Active object send range) int 8 # The radius of the volume of blocks around every player that is subject to the # active block stuff, stated in mapblocks (16 nodes). # In active blocks objects are loaded and ABMs run. # This is also the minimum range in which active objects (mobs) are maintained. # This should be configured together with active_object_send_range_blocks. -active_block_range (Active block range) int 3 +active_block_range (Active block range) int 4 # From how far blocks are sent to clients, stated in mapblocks (16 nodes). -max_block_send_distance (Max block send distance) int 10 +max_block_send_distance (Max block send distance) int 12 # Maximum number of forceloaded mapblocks. max_forceloaded_blocks (Maximum forceloaded blocks) int 16 @@ -1259,10 +1235,10 @@ movement_gravity (Gravity) float 9.81 [**Advanced] # Handling for deprecated Lua API calls: -# - legacy: (try to) mimic old behaviour (default for release). -# - log: mimic and log backtrace of deprecated call (default for debug). +# - none: Do not log deprecated calls +# - log: mimic and log backtrace of deprecated call (default). # - error: abort on usage of deprecated call (suggested for mod developers). -deprecated_lua_api_handling (Deprecated Lua API handling) enum legacy legacy,log,error +deprecated_lua_api_handling (Deprecated Lua API handling) enum log none,log,error # Number of extra blocks that can be loaded by /clearobjects at once. # This is a trade-off between sqlite transaction overhead and @@ -1289,6 +1265,10 @@ active_block_mgmt_interval (Active block management interval) float 2.0 # Length of time between Active Block Modifier (ABM) execution cycles abm_interval (ABM interval) float 1.0 +# The time budget allowed for ABMs to execute on each step +# (as a fraction of the ABM Interval) +abm_time_budget (ABM time budget) float 0.2 0.1 0.9 + # Length of time between NodeTimer execution cycles nodetimer_interval (NodeTimer interval) float 0.2 @@ -1449,12 +1429,6 @@ curl_file_download_timeout (cURL file download timeout) int 300000 # Makes DirectX work with LuaJIT. Disable if it causes troubles. high_precision_fpu (High-precision FPU) bool true -# Changes the main menu UI: -# - Full: Multiple singleplayer worlds, game choice, texture pack chooser, etc. -# - Simple: One singleplayer world, no game or texture pack choosers. May be -# necessary for smaller screens. -main_menu_style (Main menu style) enum full full,simple - # Replaces the default main menu with a custom one. main_menu_script (Main menu script) string @@ -1474,7 +1448,7 @@ mg_name (Mapgen name) enum v7 v7,valleys,carpathian,v5,flat,fractal,singlenode,v water_level (Water level) int 1 # From how far blocks are generated for clients, stated in mapblocks (16 nodes). -max_block_generate_distance (Max block generate distance) int 8 +max_block_generate_distance (Max block generate distance) int 10 # Limit of map generation, in nodes, in all 6 directions from (0, 0, 0). # Only mapchunks completely within the mapgen limit are generated. @@ -1484,7 +1458,7 @@ mapgen_limit (Map generation limit) int 31000 0 31000 # Global map generation attributes. # In Mapgen v6 the 'decorations' flag controls all decorations except trees # and junglegrass, in all other mapgens this flag controls all decorations. -mg_flags (Mapgen flags) flags caves,dungeons,light,decorations,biomes caves,dungeons,light,decorations,biomes,nocaves,nodungeons,nolight,nodecorations,nobiomes +mg_flags (Mapgen flags) flags caves,dungeons,light,decorations,biomes,ores caves,dungeons,light,decorations,biomes,ores,nocaves,nodungeons,nolight,nodecorations,nobiomes,noores [*Biome API temperature and humidity noise parameters] @@ -1875,7 +1849,7 @@ mgcarpathian_np_dungeons (Dungeon noise) noise_params_3d 0.9, 0.5, (500, 500, 50 # Map generation attributes specific to Mapgen Flat. # Occasional lakes and hills can be added to the flat world. -mgflat_spflags (Mapgen Flat specific flags) flags nolakes,nohills lakes,hills,nolakes,nohills +mgflat_spflags (Mapgen Flat specific flags) flags nolakes,nohills,nocaverns lakes,hills,caverns,nolakes,nohills,nocaverns # Y of flat ground. mgflat_ground_level (Ground level) int 8 @@ -1919,6 +1893,15 @@ mgflat_hill_threshold (Hill threshold) float 0.45 # Controls steepness/height of hills. mgflat_hill_steepness (Hill steepness) float 64.0 +# Y-level of cavern upper limit. +mgflat_cavern_limit (Cavern limit) int -256 + +# Y-distance over which caverns expand to full size. +mgflat_cavern_taper (Cavern taper) int 256 + +# Defines full size of caverns, smaller values create larger caverns. +mgflat_cavern_threshold (Cavern threshold) float 0.7 + # Lower Y limit of dungeons. mgflat_dungeon_ymin (Dungeon minimum Y) int -31000 @@ -1939,6 +1922,9 @@ mgflat_np_cave1 (Cave1 noise) noise_params_3d 0, 12, (61, 61, 61), 52534, 3, 0.5 # Second of two 3D noises that together define tunnels. mgflat_np_cave2 (Cave2 noise) noise_params_3d 0, 12, (67, 67, 67), 10325, 3, 0.5, 2.0 +# 3D noise defining giant caverns. +mgflat_np_cavern (Cavern noise) noise_params_3d 0, 1, (384, 128, 384), 723, 5, 0.63, 2.0 + # 3D noise that determines number of dungeons per mapchunk. mgflat_np_dungeons (Dungeon noise) noise_params_3d 0.9, 0.5, (500, 500, 500), 0, 2, 0.8, 2.0 @@ -2181,15 +2167,15 @@ chunksize (Chunk size) int 5 enable_mapgen_debug_info (Mapgen debug) bool false # Maximum number of blocks that can be queued for loading. -emergequeue_limit_total (Absolute limit of queued blocks to emerge) int 512 +emergequeue_limit_total (Absolute limit of queued blocks to emerge) int 1024 # Maximum number of blocks to be queued that are to be loaded from file. # This limit is enforced per player. -emergequeue_limit_diskonly (Per-player limit of queued blocks load from disk) int 64 +emergequeue_limit_diskonly (Per-player limit of queued blocks load from disk) int 128 # Maximum number of blocks to be queued that are to be generated. # This limit is enforced per player. -emergequeue_limit_generate (Per-player limit of queued blocks to generate) int 64 +emergequeue_limit_generate (Per-player limit of queued blocks to generate) int 128 # Number of emerge threads to use. # Value 0: @@ -2216,17 +2202,42 @@ contentdb_url (ContentDB URL) string https://content.minetest.net # so see a full list at https://content.minetest.net/help/content_flags/ contentdb_flag_blacklist (ContentDB Flag Blacklist) string nonfree, desktop_default +# Maximum number of concurrent downloads. Downloads exceeding this limit will be queued. +contentdb_max_concurrent_downloads (ContentDB Max Concurrent Downloads) int 3 + +[Cheat Menu] + +# Font to use for cheat menu +cheat_menu_font (MenuFont) enum FM_Mono FM_Standard,FM_Mono,FM_Fallback,FM_Simple,FM_SimpleMono,FM_MaxMode,FM_Unspecified + +# (RGB value) +cheat_menu_bg_color (Cell background color) v3f 255, 145, 88 + +cheat_menu_bg_color_alpha (Cell background color alpha) int 192 + +# (RGB value) +cheat_menu_active_bg_color (Active cell background color) v3f 255, 87, 53 + +cheat_menu_active_bg_color_alpha (Active cell background color alpha) int 192 + +# (RGB value) +cheat_menu_font_color (Font color) v3f 0, 0, 0 + +cheat_menu_font_color_alpha (Font color alpha) int 255 + +# (RGB value) +cheat_menu_selected_font_color (Selected font color) v3f 255, 252, 88 + +cheat_menu_selected_font_color_alpha (Selected font color alpha) int 255 + [Cheats] fullbright (Fullbright) bool false -# Enable xray, requires fullbright -xray (X-Ray) bool false +xray (XRay) bool false -# Node to apply xray -xray_node (X-RayTexture) string default:stone +xray_nodes (XRay Nodes) string default:stone,mcl_core:stone -# Make the Client think it has all privs priv_bypass (PrivBypass) bool true fastdig (FastDig) bool false @@ -2249,7 +2260,6 @@ increase_tool_range (IncreasedRange) bool true increase_tool_range_plus (IncreasedRangePlus) bool true -# HUD Flags Bypass hud_flags_bypass (HUDBypass) bool true antiknockback (AntiKnockback) bool false @@ -2272,10 +2282,92 @@ spamclick (FastHit) bool false no_force_rotate (NoForceRotate) bool false -enable_tracers (Tracers) bool false - -enable_esp (ESP) bool false - no_slow (NoSlow) bool false -trace_players_only (Only trace to players) bool true +ignore_status_messages (IgnoreStatus) bool true + +mark_deathmessages (Deathmessages) bool true + +autosneak (AutoSneak) bool false + +autoeject (AutoEject) bool false + +eject_items (AutoEject Items) string + +autotool (AutoTool) bool false + +autorespawn (AutoRespawn) bool false + +scaffold (Scaffold) bool false + +scaffold_plus (ScaffoldPlus) bool false + +block_water (BlockWater) bool false + +autotnt (PlaceOnTop) bool false + +replace (Replace) bool false + +crystal_pvp (CrystalPvP) bool false + +autototem (AutoTotem) bool false + +dont_point_nodes (ThroughWalls) bool false + +strip (Strip) bool false + +autorefill (AutoRefill) bool false + +nuke (Nuke) bool false + +chat_color (Chat Color) string rainbow + +use_chat_color (ColoredChat) bool false + +chat_reverse (ReversedChat) bool false + +forcefield (Forcefield) bool false + +friendlist (Killaura / Forcefield Friendlist) string + +cheat_hud (CheatHUD) bool true + +node_esp_nodes (NodeESP Nodes) string + +autosprint (AutoSprint) bool false + +override_speed (SpeedOverride) bool false + +override_jump (JumpOverride) bool false + +override_gravity (GravityOverride) bool false + +override_speed_factor (SpeedOverride Factor) float 1.2 + +override_jump_factor (JumpOverride Factor) float 2.0 + +override_gravity_factor (GravityOverride) float 0.8 + +jetpack (JetPack) bool false + +autohit (AutoHit) bool false + +antislip (AntiSlip) bool false + +enable_entity_tracers (EntityTracers) bool false + +enable_entity_esp (EntityESP) bool false + +enable_player_tracers (PlayerTracers) bool false + +enable_player_esp (PlayerESP) bool false + +enable_node_esp (NodeESP) bool false + +enable_node_tracers (NodeTracers) bool false + +entity_esp_color (EntityESP Color) v3f 255, 255, 255 + +player_esp_color (PlayerESP Color) v3f 0, 255, 0 + +noweather (NoWeather) bool false diff --git a/client/shaders/3d_interlaced_merge/opengl_fragment.glsl b/client/shaders/3d_interlaced_merge/opengl_fragment.glsl index 25945ad7f..7cba61b39 100644 --- a/client/shaders/3d_interlaced_merge/opengl_fragment.glsl +++ b/client/shaders/3d_interlaced_merge/opengl_fragment.glsl @@ -6,9 +6,11 @@ uniform sampler2D textureFlags; #define rightImage normalTexture #define maskImage textureFlags +varying mediump vec2 varTexCoord; + void main(void) { - vec2 uv = gl_TexCoord[0].st; + vec2 uv = varTexCoord.st; vec4 left = texture2D(leftImage, uv).rgba; vec4 right = texture2D(rightImage, uv).rgba; vec4 mask = texture2D(maskImage, uv).rgba; diff --git a/client/shaders/3d_interlaced_merge/opengl_vertex.glsl b/client/shaders/3d_interlaced_merge/opengl_vertex.glsl index 4e0b2b125..860049481 100644 --- a/client/shaders/3d_interlaced_merge/opengl_vertex.glsl +++ b/client/shaders/3d_interlaced_merge/opengl_vertex.glsl @@ -1,6 +1,7 @@ +varying mediump vec2 varTexCoord; + void main(void) { - gl_TexCoord[0] = gl_MultiTexCoord0; - gl_Position = gl_Vertex; - gl_FrontColor = gl_BackColor = gl_Color; + varTexCoord = inTexCoord0; + gl_Position = inVertexPosition; } diff --git a/client/shaders/default_shader/opengl_fragment.glsl b/client/shaders/default_shader/opengl_fragment.glsl index 925ab6e1d..5018ac6ea 100644 --- a/client/shaders/default_shader/opengl_fragment.glsl +++ b/client/shaders/default_shader/opengl_fragment.glsl @@ -1,4 +1,6 @@ +varying lowp vec4 varColor; + void main(void) { - gl_FragColor = gl_Color; + gl_FragColor = varColor; } diff --git a/client/shaders/default_shader/opengl_vertex.glsl b/client/shaders/default_shader/opengl_vertex.glsl index d0b16c8b0..d95a3c2d3 100644 --- a/client/shaders/default_shader/opengl_vertex.glsl +++ b/client/shaders/default_shader/opengl_vertex.glsl @@ -1,9 +1,7 @@ -uniform mat4 mWorldViewProj; +varying lowp vec4 varColor; void main(void) { - gl_TexCoord[0] = gl_MultiTexCoord0; - gl_Position = mWorldViewProj * gl_Vertex; - - gl_FrontColor = gl_BackColor = gl_Color; + gl_Position = mWorldViewProj * inVertexPosition; + varColor = inVertexColor; } diff --git a/client/shaders/minimap_shader/opengl_fragment.glsl b/client/shaders/minimap_shader/opengl_fragment.glsl index fa4f9cb1a..cef359e8a 100644 --- a/client/shaders/minimap_shader/opengl_fragment.glsl +++ b/client/shaders/minimap_shader/opengl_fragment.glsl @@ -2,9 +2,12 @@ uniform sampler2D baseTexture; uniform sampler2D normalTexture; uniform vec3 yawVec; +varying lowp vec4 varColor; +varying mediump vec2 varTexCoord; + void main (void) { - vec2 uv = gl_TexCoord[0].st; + vec2 uv = varTexCoord.st; //texture sampling rate const float step = 1.0 / 256.0; @@ -27,6 +30,6 @@ void main (void) vec3 color = (1.1 * diffuse + 0.05 * height + 0.5 * specular) * base.rgb; vec4 col = vec4(color.rgb, base.a); - col *= gl_Color; + col *= varColor; gl_FragColor = vec4(col.rgb, base.a); } diff --git a/client/shaders/minimap_shader/opengl_vertex.glsl b/client/shaders/minimap_shader/opengl_vertex.glsl index 88f9356d5..1a9491805 100644 --- a/client/shaders/minimap_shader/opengl_vertex.glsl +++ b/client/shaders/minimap_shader/opengl_vertex.glsl @@ -1,9 +1,11 @@ -uniform mat4 mWorldViewProj; uniform mat4 mWorld; +varying lowp vec4 varColor; +varying mediump vec2 varTexCoord; + void main(void) { - gl_TexCoord[0] = gl_MultiTexCoord0; - gl_Position = mWorldViewProj * gl_Vertex; - gl_FrontColor = gl_BackColor = gl_Color; + varTexCoord = inTexCoord0.st; + gl_Position = mWorldViewProj * inVertexPosition; + varColor = inVertexColor; } diff --git a/client/shaders/nodes_shader/opengl_fragment.glsl b/client/shaders/nodes_shader/opengl_fragment.glsl index 19e6c2d86..82c87073a 100644 --- a/client/shaders/nodes_shader/opengl_fragment.glsl +++ b/client/shaders/nodes_shader/opengl_fragment.glsl @@ -1,6 +1,4 @@ uniform sampler2D baseTexture; -uniform sampler2D normalTexture; -uniform sampler2D textureFlags; uniform vec4 skyBgColor; uniform float fogDistance; @@ -17,19 +15,12 @@ varying vec3 vPosition; // cameraOffset + worldPosition (for large coordinates the limits of float // precision must be considered). varying vec3 worldPosition; -varying float area_enable_parallax; - +varying lowp vec4 varColor; +varying mediump vec2 varTexCoord; varying vec3 eyeVec; -varying vec3 tsEyeVec; -varying vec3 lightVec; -varying vec3 tsLightVec; -bool normalTexturePresent = false; - -const float e = 2.718281828459; -const float BS = 10.0; const float fogStart = FOG_START; -const float fogShadingParameter = 1 / ( 1 - fogStart); +const float fogShadingParameter = 1.0 / ( 1.0 - fogStart); #ifdef ENABLE_TONE_MAPPING @@ -63,148 +54,24 @@ vec4 applyToneMapping(vec4 color) } #endif -void get_texture_flags() -{ - vec4 flags = texture2D(textureFlags, vec2(0.0, 0.0)); - if (flags.r > 0.5) { - normalTexturePresent = true; - } -} - -float intensity(vec3 color) -{ - return (color.r + color.g + color.b) / 3.0; -} - -float get_rgb_height(vec2 uv) -{ - return intensity(texture2D(baseTexture, uv).rgb); -} - -vec4 get_normal_map(vec2 uv) -{ - vec4 bump = texture2D(normalTexture, uv).rgba; - bump.xyz = normalize(bump.xyz * 2.0 - 1.0); - return bump; -} - -float find_intersection(vec2 dp, vec2 ds) -{ - float depth = 1.0; - float best_depth = 0.0; - float size = 0.0625; - for (int i = 0; i < 15; i++) { - depth -= size; - float h = texture2D(normalTexture, dp + ds * depth).a; - if (depth <= h) { - best_depth = depth; - break; - } - } - depth = best_depth; - for (int i = 0; i < 4; i++) { - size *= 0.5; - float h = texture2D(normalTexture,dp + ds * depth).a; - if (depth <= h) { - best_depth = depth; - depth += size; - } else { - depth -= size; - } - } - return best_depth; -} - -float find_intersectionRGB(vec2 dp, vec2 ds) -{ - const float depth_step = 1.0 / 24.0; - float depth = 1.0; - for (int i = 0 ; i < 24 ; i++) { - float h = get_rgb_height(dp + ds * depth); - if (h >= depth) - break; - depth -= depth_step; - } - return depth; -} - void main(void) { vec3 color; - vec4 bump; - vec2 uv = gl_TexCoord[0].st; - bool use_normalmap = false; - get_texture_flags(); + vec2 uv = varTexCoord.st; -#ifdef ENABLE_PARALLAX_OCCLUSION - vec2 eyeRay = vec2 (tsEyeVec.x, -tsEyeVec.y); - const float scale = PARALLAX_OCCLUSION_SCALE / PARALLAX_OCCLUSION_ITERATIONS; - const float bias = PARALLAX_OCCLUSION_BIAS / PARALLAX_OCCLUSION_ITERATIONS; - -#if PARALLAX_OCCLUSION_MODE == 0 - // Parallax occlusion with slope information - if (normalTexturePresent && area_enable_parallax > 0.0) { - for (int i = 0; i < PARALLAX_OCCLUSION_ITERATIONS; i++) { - vec4 normal = texture2D(normalTexture, uv.xy); - float h = normal.a * scale - bias; - uv += h * normal.z * eyeRay; - } -#endif - -#if PARALLAX_OCCLUSION_MODE == 1 - // Relief mapping - if (normalTexturePresent && area_enable_parallax > 0.0) { - vec2 ds = eyeRay * PARALLAX_OCCLUSION_SCALE; - float dist = find_intersection(uv, ds); - uv += dist * ds; -#endif - } else if (GENERATE_NORMALMAPS == 1 && area_enable_parallax > 0.0) { - vec2 ds = eyeRay * PARALLAX_OCCLUSION_SCALE; - float dist = find_intersectionRGB(uv, ds); - uv += dist * ds; - } -#endif - -#if USE_NORMALMAPS == 1 - if (normalTexturePresent) { - bump = get_normal_map(uv); - use_normalmap = true; - } -#endif - -#if GENERATE_NORMALMAPS == 1 - if (normalTexturePresent == false) { - float tl = get_rgb_height(vec2(uv.x - SAMPLE_STEP, uv.y + SAMPLE_STEP)); - float t = get_rgb_height(vec2(uv.x - SAMPLE_STEP, uv.y - SAMPLE_STEP)); - float tr = get_rgb_height(vec2(uv.x + SAMPLE_STEP, uv.y + SAMPLE_STEP)); - float r = get_rgb_height(vec2(uv.x + SAMPLE_STEP, uv.y)); - float br = get_rgb_height(vec2(uv.x + SAMPLE_STEP, uv.y - SAMPLE_STEP)); - float b = get_rgb_height(vec2(uv.x, uv.y - SAMPLE_STEP)); - float bl = get_rgb_height(vec2(uv.x -SAMPLE_STEP, uv.y - SAMPLE_STEP)); - float l = get_rgb_height(vec2(uv.x - SAMPLE_STEP, uv.y)); - float dX = (tr + 2.0 * r + br) - (tl + 2.0 * l + bl); - float dY = (bl + 2.0 * b + br) - (tl + 2.0 * t + tr); - bump = vec4(normalize(vec3 (dX, dY, NORMALMAPS_STRENGTH)), 1.0); - use_normalmap = true; - } -#endif vec4 base = texture2D(baseTexture, uv).rgba; - -#ifdef ENABLE_BUMPMAPPING - if (use_normalmap) { - vec3 L = normalize(lightVec); - vec3 E = normalize(eyeVec); - float specular = pow(clamp(dot(reflect(L, bump.xyz), E), 0.0, 1.0), 1.0); - float diffuse = dot(-E,bump.xyz); - color = (diffuse + 0.1 * specular) * base.rgb; - } else { - color = base.rgb; +#ifdef USE_DISCARD + // If alpha is zero, we can just discard the pixel. This fixes transparency + // on GPUs like GC7000L, where GL_ALPHA_TEST is not implemented in mesa, + // and also on GLES 2, where GL_ALPHA_TEST is missing entirely. + if (base.a == 0.0) { + discard; } -#else - color = base.rgb; #endif - vec4 col = vec4(color.rgb * gl_Color.rgb, 1.0); + color = base.rgb; + + vec4 col = vec4(color.rgb * varColor.rgb, 1.0); #ifdef ENABLE_TONE_MAPPING col = applyToneMapping(col); diff --git a/client/shaders/nodes_shader/opengl_vertex.glsl b/client/shaders/nodes_shader/opengl_vertex.glsl index 0d8d0a2a5..cb344f6e6 100644 --- a/client/shaders/nodes_shader/opengl_vertex.glsl +++ b/client/shaders/nodes_shader/opengl_vertex.glsl @@ -1,4 +1,3 @@ -uniform mat4 mWorldViewProj; uniform mat4 mWorld; // Color of the light emitted by the sun. @@ -16,12 +15,9 @@ varying vec3 vPosition; // cameraOffset + worldPosition (for large coordinates the limits of float // precision must be considered). varying vec3 worldPosition; - +varying lowp vec4 varColor; +varying mediump vec2 varTexCoord; varying vec3 eyeVec; -varying vec3 lightVec; -varying vec3 tsEyeVec; -varying vec3 tsLightVec; -varying float area_enable_parallax; // Color of the light emitted by the light sources. const vec3 artificialLight = vec3(1.04, 1.04, 1.04); @@ -85,25 +81,13 @@ float snoise(vec3 p) void main(void) { - gl_TexCoord[0] = gl_MultiTexCoord0; - //TODO: make offset depending on view angle and parallax uv displacement - //thats for textures that doesnt align vertically, like dirt with grass - //gl_TexCoord[0].y += 0.008; + varTexCoord = inTexCoord0.st; - //Allow parallax/relief mapping only for certain kind of nodes - //Variable is also used to control area of the effect -#if (DRAW_TYPE == NDT_NORMAL || DRAW_TYPE == NDT_LIQUID || DRAW_TYPE == NDT_FLOWINGLIQUID) - area_enable_parallax = 1.0; -#else - area_enable_parallax = 0.0; -#endif - - -float disp_x; -float disp_z; + float disp_x; + float disp_z; // OpenGL < 4.3 does not support continued preprocessor lines #if (MATERIAL_TYPE == TILE_MATERIAL_WAVING_LEAVES && ENABLE_WAVING_LEAVES) || (MATERIAL_TYPE == TILE_MATERIAL_WAVING_PLANTS && ENABLE_WAVING_PLANTS) - vec4 pos2 = mWorld * gl_Vertex; + vec4 pos2 = mWorld * inVertexPosition; float tOffset = (pos2.x + pos2.y) * 0.001 + pos2.z * 0.002; disp_x = (smoothTriangleWave(animationTimer * 23.0 + tOffset) + smoothTriangleWave(animationTimer * 11.0 + tOffset)) * 0.4; @@ -112,68 +96,43 @@ float disp_z; smoothTriangleWave(animationTimer * 13.0 + tOffset)) * 0.5; #endif - worldPosition = (mWorld * gl_Vertex).xyz; + worldPosition = (mWorld * inVertexPosition).xyz; // OpenGL < 4.3 does not support continued preprocessor lines #if (MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT || MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_OPAQUE || MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_BASIC) && ENABLE_WAVING_WATER // Generate waves with Perlin-type noise. // The constants are calibrated such that they roughly // correspond to the old sine waves. - vec4 pos = gl_Vertex; + vec4 pos = inVertexPosition; vec3 wavePos = worldPosition + cameraOffset; // The waves are slightly compressed along the z-axis to get // wave-fronts along the x-axis. - wavePos.x /= WATER_WAVE_LENGTH * 3; - wavePos.z /= WATER_WAVE_LENGTH * 2; - wavePos.z += animationTimer * WATER_WAVE_SPEED * 10; - pos.y += (snoise(wavePos) - 1) * WATER_WAVE_HEIGHT * 5; + wavePos.x /= WATER_WAVE_LENGTH * 3.0; + wavePos.z /= WATER_WAVE_LENGTH * 2.0; + wavePos.z += animationTimer * WATER_WAVE_SPEED * 10.0; + pos.y += (snoise(wavePos) - 1.0) * WATER_WAVE_HEIGHT * 5.0; gl_Position = mWorldViewProj * pos; #elif MATERIAL_TYPE == TILE_MATERIAL_WAVING_LEAVES && ENABLE_WAVING_LEAVES - vec4 pos = gl_Vertex; + vec4 pos = inVertexPosition; pos.x += disp_x; pos.y += disp_z * 0.1; pos.z += disp_z; gl_Position = mWorldViewProj * pos; #elif MATERIAL_TYPE == TILE_MATERIAL_WAVING_PLANTS && ENABLE_WAVING_PLANTS - vec4 pos = gl_Vertex; - if (gl_TexCoord[0].y < 0.05) { + vec4 pos = inVertexPosition; + if (varTexCoord.y < 0.05) { pos.x += disp_x; pos.z += disp_z; } gl_Position = mWorldViewProj * pos; #else - gl_Position = mWorldViewProj * gl_Vertex; + gl_Position = mWorldViewProj * inVertexPosition; #endif vPosition = gl_Position.xyz; - // Don't generate heightmaps when too far from the eye - float dist = distance (vec3(0.0, 0.0, 0.0), vPosition); - if (dist > 150.0) { - area_enable_parallax = 0.0; - } - - vec3 sunPosition = vec3 (0.0, eyePosition.y * BS + 900.0, 0.0); - - vec3 normal, tangent, binormal; - normal = normalize(gl_NormalMatrix * gl_Normal); - tangent = normalize(gl_NormalMatrix * gl_MultiTexCoord1.xyz); - binormal = normalize(gl_NormalMatrix * gl_MultiTexCoord2.xyz); - - vec3 v; - - lightVec = sunPosition - worldPosition; - v.x = dot(lightVec, tangent); - v.y = dot(lightVec, binormal); - v.z = dot(lightVec, normal); - tsLightVec = normalize (v); - - eyeVec = -(gl_ModelViewMatrix * gl_Vertex).xyz; - v.x = dot(eyeVec, tangent); - v.y = dot(eyeVec, binormal); - v.z = dot(eyeVec, normal); - tsEyeVec = normalize (v); + eyeVec = -(mWorldView * inVertexPosition).xyz; // Calculate color. // Red, green and blue components are pre-multiplied with @@ -182,16 +141,16 @@ float disp_z; // The pre-baked colors are halved to prevent overflow. vec4 color; // The alpha gives the ratio of sunlight in the incoming light. - float nightRatio = 1 - gl_Color.a; - color.rgb = gl_Color.rgb * (gl_Color.a * dayLight.rgb + - nightRatio * artificialLight.rgb) * 2; - color.a = 1; + float nightRatio = 1.0 - inVertexColor.a; + color.rgb = inVertexColor.rgb * (inVertexColor.a * dayLight.rgb + + nightRatio * artificialLight.rgb) * 2.0; + color.a = 1.0; // Emphase blue a bit in darker places // See C++ implementation in mapblock_mesh.cpp final_color_blend() - float brightness = (color.r + color.g + color.b) / 3; + float brightness = (color.r + color.g + color.b) / 3.0; color.b += max(0.0, 0.021 - abs(0.2 * brightness - 0.021) + 0.07 * brightness); - gl_FrontColor = gl_BackColor = clamp(color, 0.0, 1.0); + varColor = clamp(color, 0.0, 1.0); } diff --git a/client/shaders/object_shader/opengl_fragment.glsl b/client/shaders/object_shader/opengl_fragment.glsl index 0534dc049..7ac182a63 100644 --- a/client/shaders/object_shader/opengl_fragment.glsl +++ b/client/shaders/object_shader/opengl_fragment.glsl @@ -1,6 +1,4 @@ uniform sampler2D baseTexture; -uniform sampler2D normalTexture; -uniform sampler2D textureFlags; uniform vec4 emissiveColor; uniform vec4 skyBgColor; @@ -10,20 +8,16 @@ uniform vec3 eyePosition; varying vec3 vNormal; varying vec3 vPosition; varying vec3 worldPosition; +varying lowp vec4 varColor; +varying mediump vec2 varTexCoord; varying vec3 eyeVec; -varying vec3 lightVec; varying float vIDiff; -bool normalTexturePresent = false; -bool texTileableHorizontal = false; -bool texTileableVertical = false; -bool texSeamless = false; - const float e = 2.718281828459; const float BS = 10.0; const float fogStart = FOG_START; -const float fogShadingParameter = 1 / ( 1 - fogStart); +const float fogShadingParameter = 1.0 / (1.0 - fogStart); #ifdef ENABLE_TONE_MAPPING @@ -57,95 +51,27 @@ vec4 applyToneMapping(vec4 color) } #endif -void get_texture_flags() -{ - vec4 flags = texture2D(textureFlags, vec2(0.0, 0.0)); - if (flags.r > 0.5) { - normalTexturePresent = true; - } - if (flags.g > 0.5) { - texTileableHorizontal = true; - } - if (flags.b > 0.5) { - texTileableVertical = true; - } - if (texTileableHorizontal && texTileableVertical) { - texSeamless = true; - } -} - -float intensity(vec3 color) -{ - return (color.r + color.g + color.b) / 3.0; -} - -float get_rgb_height(vec2 uv) -{ - if (texSeamless) { - return intensity(texture2D(baseTexture, uv).rgb); - } else { - return intensity(texture2D(baseTexture, clamp(uv, 0.0, 0.999)).rgb); - } -} - -vec4 get_normal_map(vec2 uv) -{ - vec4 bump = texture2D(normalTexture, uv).rgba; - bump.xyz = normalize(bump.xyz * 2.0 - 1.0); - return bump; -} - void main(void) { vec3 color; - vec4 bump; - vec2 uv = gl_TexCoord[0].st; - bool use_normalmap = false; - get_texture_flags(); - -#if USE_NORMALMAPS == 1 - if (normalTexturePresent) { - bump = get_normal_map(uv); - use_normalmap = true; - } -#endif - -#if GENERATE_NORMALMAPS == 1 - if (normalTexturePresent == false) { - float tl = get_rgb_height(vec2(uv.x - SAMPLE_STEP, uv.y + SAMPLE_STEP)); - float t = get_rgb_height(vec2(uv.x - SAMPLE_STEP, uv.y - SAMPLE_STEP)); - float tr = get_rgb_height(vec2(uv.x + SAMPLE_STEP, uv.y + SAMPLE_STEP)); - float r = get_rgb_height(vec2(uv.x + SAMPLE_STEP, uv.y)); - float br = get_rgb_height(vec2(uv.x + SAMPLE_STEP, uv.y - SAMPLE_STEP)); - float b = get_rgb_height(vec2(uv.x, uv.y - SAMPLE_STEP)); - float bl = get_rgb_height(vec2(uv.x -SAMPLE_STEP, uv.y - SAMPLE_STEP)); - float l = get_rgb_height(vec2(uv.x - SAMPLE_STEP, uv.y)); - float dX = (tr + 2.0 * r + br) - (tl + 2.0 * l + bl); - float dY = (bl + 2.0 * b + br) - (tl + 2.0 * t + tr); - bump = vec4(normalize(vec3 (dX, dY, NORMALMAPS_STRENGTH)), 1.0); - use_normalmap = true; - } -#endif + vec2 uv = varTexCoord.st; vec4 base = texture2D(baseTexture, uv).rgba; -#ifdef ENABLE_BUMPMAPPING - if (use_normalmap) { - vec3 L = normalize(lightVec); - vec3 E = normalize(eyeVec); - float specular = pow(clamp(dot(reflect(L, bump.xyz), E), 0.0, 1.0), 1.0); - float diffuse = dot(-E,bump.xyz); - color = (diffuse + 0.1 * specular) * base.rgb; - } else { - color = base.rgb; +#ifdef USE_DISCARD + // If alpha is zero, we can just discard the pixel. This fixes transparency + // on GPUs like GC7000L, where GL_ALPHA_TEST is not implemented in mesa, + // and also on GLES 2, where GL_ALPHA_TEST is missing entirely. + if (base.a == 0.0) { + discard; } -#else - color = base.rgb; #endif + color = base.rgb; + vec4 col = vec4(color.rgb, base.a); - col.rgb *= gl_Color.rgb; + col.rgb *= varColor.rgb; col.rgb *= emissiveColor.rgb * vIDiff; diff --git a/client/shaders/object_shader/opengl_vertex.glsl b/client/shaders/object_shader/opengl_vertex.glsl index 968a07e22..e44984dc8 100644 --- a/client/shaders/object_shader/opengl_vertex.glsl +++ b/client/shaders/object_shader/opengl_vertex.glsl @@ -1,4 +1,3 @@ -uniform mat4 mWorldViewProj; uniform mat4 mWorld; uniform vec3 eyePosition; @@ -7,9 +6,10 @@ uniform float animationTimer; varying vec3 vNormal; varying vec3 vPosition; varying vec3 worldPosition; +varying lowp vec4 varColor; +varying mediump vec2 varTexCoord; varying vec3 eyeVec; -varying vec3 lightVec; varying float vIDiff; const float e = 2.718281828459; @@ -19,35 +19,31 @@ float directional_ambient(vec3 normal) { vec3 v = normal * normal; - if (normal.y < 0) - return dot(v, vec3(0.670820f, 0.447213f, 0.836660f)); + if (normal.y < 0.0) + return dot(v, vec3(0.670820, 0.447213, 0.836660)); - return dot(v, vec3(0.670820f, 1.000000f, 0.836660f)); + return dot(v, vec3(0.670820, 1.000000, 0.836660)); } void main(void) { - gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; - gl_Position = mWorldViewProj * gl_Vertex; + varTexCoord = (mTexture * inTexCoord0).st; + gl_Position = mWorldViewProj * inVertexPosition; vPosition = gl_Position.xyz; - vNormal = gl_Normal; - worldPosition = (mWorld * gl_Vertex).xyz; - - vec3 sunPosition = vec3 (0.0, eyePosition.y * BS + 900.0, 0.0); - - lightVec = sunPosition - worldPosition; - eyeVec = -(gl_ModelViewMatrix * gl_Vertex).xyz; + vNormal = inVertexNormal; + worldPosition = (mWorld * inVertexPosition).xyz; + eyeVec = -(mWorldView * inVertexPosition).xyz; #if (MATERIAL_TYPE == TILE_MATERIAL_PLAIN) || (MATERIAL_TYPE == TILE_MATERIAL_PLAIN_ALPHA) vIDiff = 1.0; #else // This is intentional comparison with zero without any margin. // If normal is not equal to zero exactly, then we assume it's a valid, just not normalized vector - vIDiff = length(gl_Normal) == 0.0 + vIDiff = length(inVertexNormal) == 0.0 ? 1.0 - : directional_ambient(normalize(gl_Normal)); + : directional_ambient(normalize(inVertexNormal)); #endif - gl_FrontColor = gl_BackColor = gl_Color; + varColor = inVertexColor; } diff --git a/client/shaders/selection_shader/opengl_fragment.glsl b/client/shaders/selection_shader/opengl_fragment.glsl index c679d0e12..35b1f8902 100644 --- a/client/shaders/selection_shader/opengl_fragment.glsl +++ b/client/shaders/selection_shader/opengl_fragment.glsl @@ -1,9 +1,12 @@ uniform sampler2D baseTexture; +varying lowp vec4 varColor; +varying mediump vec2 varTexCoord; + void main(void) { - vec2 uv = gl_TexCoord[0].st; + vec2 uv = varTexCoord.st; vec4 color = texture2D(baseTexture, uv); - color.rgb *= gl_Color.rgb; + color.rgb *= varColor.rgb; gl_FragColor = color; } diff --git a/client/shaders/selection_shader/opengl_vertex.glsl b/client/shaders/selection_shader/opengl_vertex.glsl index d0b16c8b0..9ca87a9cf 100644 --- a/client/shaders/selection_shader/opengl_vertex.glsl +++ b/client/shaders/selection_shader/opengl_vertex.glsl @@ -1,9 +1,10 @@ -uniform mat4 mWorldViewProj; +varying lowp vec4 varColor; +varying mediump vec2 varTexCoord; void main(void) { - gl_TexCoord[0] = gl_MultiTexCoord0; - gl_Position = mWorldViewProj * gl_Vertex; + varTexCoord = inTexCoord0.st; + gl_Position = mWorldViewProj * inVertexPosition; - gl_FrontColor = gl_BackColor = gl_Color; + varColor = inVertexColor; } diff --git a/client/shaders/stars_shader/opengl_fragment.glsl b/client/shaders/stars_shader/opengl_fragment.glsl new file mode 100644 index 000000000..a9ed741bf --- /dev/null +++ b/client/shaders/stars_shader/opengl_fragment.glsl @@ -0,0 +1,6 @@ +uniform vec4 starColor; + +void main(void) +{ + gl_FragColor = starColor; +} diff --git a/client/shaders/stars_shader/opengl_vertex.glsl b/client/shaders/stars_shader/opengl_vertex.glsl new file mode 100644 index 000000000..77c401f34 --- /dev/null +++ b/client/shaders/stars_shader/opengl_vertex.glsl @@ -0,0 +1,4 @@ +void main(void) +{ + gl_Position = mWorldViewProj * inVertexPosition; +} diff --git a/clientmods/dragonfire/autosneak/init.lua b/clientmods/dragonfire/autosneak/init.lua deleted file mode 100644 index 04a5a86fd..000000000 --- a/clientmods/dragonfire/autosneak/init.lua +++ /dev/null @@ -1,25 +0,0 @@ -local was_enabled = false -local pwas_enabled = false - -minetest.register_globalstep(function() - if minetest.settings:get_bool("autosneak") then - minetest.set_keypress("sneak", true) - was_enabled = true - elseif was_enabled then - was_enabled = false - minetest.set_keypress("sneak", false) - end - if minetest.settings:get_bool("autosneak_conditional") then - local blck=minetest.get_node_or_nil(vector.add(minetest.localplayer:get_pos(),{x=0,y=-1,z=0})) - if blck ~= nil and blck.name~="air" then - minetest.set_keypress("sneak", true) - pwas_enabled = true - end - elseif pwas_enabled then - pwas_enabled = false - minetest.set_keypress("sneak", false) - end -end) - -minetest.register_cheat("AutoSneak", "Movement", "autosneak") -minetest.register_cheat("GroundASneak", "Movement", "autosneak_conditional") diff --git a/clientmods/dragonfire/chat/colors.lua b/clientmods/dragonfire/chat/colors.lua deleted file mode 100644 index b3ab596fb..000000000 --- a/clientmods/dragonfire/chat/colors.lua +++ /dev/null @@ -1,33 +0,0 @@ -function chat.send(message) - local starts_with = message:sub(1, 1) - - if starts_with == "/" or starts_with == "." then return end - - local reverse = minetest.settings:get_bool("chat_reverse") - - if reverse then - local msg = "" - for i = 1, #message do - msg = message:sub(i, i) .. msg - end - message = msg - end - - local color = minetest.settings:get("chat_color") - - if color then - local msg - if color == "rainbow" then - msg = chat.rainbow(message) - else - msg = minetest.colorize(color, message) - end - message = msg - end - - minetest.send_chat_message(message) - return true -end - -minetest.register_on_sending_chat_message(chat.send) - diff --git a/clientmods/dragonfire/chat/init.lua b/clientmods/dragonfire/chat/init.lua index 5086bc570..aa8a71189 100644 --- a/clientmods/dragonfire/chat/init.lua +++ b/clientmods/dragonfire/chat/init.lua @@ -1,13 +1,48 @@ -chat = {} +minetest.register_on_receiving_chat_message(function(message) + if message:sub(1, 1) == "#" and minetest.settings:get_bool("ignore_status_messages") ~= false then + return true + elseif message:find('\1b@mcl_death_messages\1b') and minetest.settings:get_bool("mark_deathmessages") ~= false then + minetest.display_chat_message(minetest.colorize("#F25819", "[Deathmessage] ") .. message) + return true + end +end) -local modname = minetest.get_current_modname() -local modpath = minetest.get_modpath(modname) +function minetest.send_colorized(message) + local starts_with = message:sub(1, 1) + + if starts_with == "/" or starts_with == "." then return end -chat.rainbow = dofile(modpath .. "/rainbow.lua") + local reverse = minetest.settings:get_bool("chat_reverse") + + if reverse then + local msg = "" + for i = 1, #message do + msg = message:sub(i, i) .. msg + end + message = msg + end + + local use_chat_color = minetest.settings:get_bool("use_chat_color") + local color = minetest.settings:get("chat_color") -dofile(modpath .. "/colors.lua") -dofile(modpath .. "/spam.lua") -dofile(modpath .. "/status.lua") -dofile(modpath .. "/leak.lua") + if use_chat_color and color then + local msg + if color == "rainbow" then + msg = minetest.rainbow(message) + else + msg = minetest.colorize(color, message) + end + message = msg + end + + minetest.send_chat_message(message) + return true +end + +minetest.register_on_sending_chat_message(minetest.send_colorized) +minetest.register_cheat("IgnoreStatus", "Chat", "ignore_status_messages") +minetest.register_cheat("DeathMessages", "Chat", "mark_deathmessages") +minetest.register_cheat("ColoredChat", "Chat", "use_chat_color") +minetest.register_cheat("ReversedChat", "Chat", "chat_reverse") diff --git a/clientmods/dragonfire/chat/leak.lua b/clientmods/dragonfire/chat/leak.lua deleted file mode 100644 index 80440ebbb..000000000 --- a/clientmods/dragonfire/chat/leak.lua +++ /dev/null @@ -1,20 +0,0 @@ - ---[[minetest.register_globalstep(function(dtime) - if not minetest.settings:get_bool("leak") then return end - etime = etime + dtime - if etime < 5 then return end - etime = 0 - local player = minetest.localplayer - minetest.send_chat_message(minetest.pos_to_string(vector.floor(player:get_pos()))) -end)--]] -function cleak() - minetest.send_chat_message(minetest.pos_to_string(vector.floor(minetest.localplayer:get_pos()))) -end -minetest.register_chatcommand("cleak", { - params = "", - description = "Open node inventory metadata at position.", - func = cleak - }) - - ---minetest.register_cheat("Leak", "Player", "leak") diff --git a/clientmods/dragonfire/chat/rainbow.lua b/clientmods/dragonfire/chat/rainbow.lua deleted file mode 100644 index 1519619a7..000000000 --- a/clientmods/dragonfire/chat/rainbow.lua +++ /dev/null @@ -1,61 +0,0 @@ -local function rgb_to_hex(rgb) - local hexadecimal = '#' - - for key, value in pairs(rgb) do - local hex = '' - - while(value > 0)do - local index = math.fmod(value, 16) + 1 - value = math.floor(value / 16) - hex = string.sub('0123456789ABCDEF', index, index) .. hex - end - - if(string.len(hex) == 0)then - hex = '00' - - elseif(string.len(hex) == 1)then - hex = '0' .. hex - end - - hexadecimal = hexadecimal .. hex - end - - return hexadecimal -end - -local function color_from_hue(hue) - local h = hue / 60 - local c = 255 - local x = (1 - math.abs(h%2 - 1)) * 255 - - local i = math.floor(h); - if (i == 0) then - return rgb_to_hex({c, x, 0}) - elseif (i == 1) then - return rgb_to_hex({x, c, 0}) - elseif (i == 2) then - return rgb_to_hex({0, c, x}) - elseif (i == 3) then - return rgb_to_hex({0, x, c}); - elseif (i == 4) then - return rgb_to_hex({x, 0, c}); - else - return rgb_to_hex({c, 0, x}); - end -end - -return function(input) - local step = 360 / input:len() - local hue = 0 - local output = "" - for i = 1, input:len() do - local char = input:sub(i,i) - if char:match("%s") then - output = output .. char - else - output = output .. minetest.get_color_escape_sequence(color_from_hue(hue)) .. char - end - hue = hue + step - end - return output -end diff --git a/clientmods/dragonfire/chat/settingtypes.txt b/clientmods/dragonfire/chat/settingtypes.txt index 9428dd955..35313e998 100644 --- a/clientmods/dragonfire/chat/settingtypes.txt +++ b/clientmods/dragonfire/chat/settingtypes.txt @@ -1,6 +1,3 @@ chat_color (Chat Color) string white chat_reverse (Reverse Chat messages) bool false -chat_enable_spam (Spam Chat) bool false -chat_spam (Message to spam into Chat) string -ignore_status_messages (Ignore status messages from server) bool true -leak (Frequently leak your coordinates to chat) bool false +ignore_status_messages (Ignore status messages from server) bool false diff --git a/clientmods/dragonfire/chat/spam.lua b/clientmods/dragonfire/chat/spam.lua deleted file mode 100644 index 991846d33..000000000 --- a/clientmods/dragonfire/chat/spam.lua +++ /dev/null @@ -1,12 +0,0 @@ -local etime = 0 - -minetest.register_globalstep(function(dtime) - etime = etime + dtime - if etime < 10/8 then return end - etime = 0 - local spam = minetest.settings:get("chat_spam") - local enable_spam = minetest.settings:get("chat_enable_spam") - if enable_spam and spam then - local _ = chat.send(spam) or minetest.send_chat_message(spam) - end -end) diff --git a/clientmods/dragonfire/chat/status.lua b/clientmods/dragonfire/chat/status.lua deleted file mode 100644 index ba79048f8..000000000 --- a/clientmods/dragonfire/chat/status.lua +++ /dev/null @@ -1,7 +0,0 @@ -minetest.register_on_receiving_chat_message(function(message) - if message:sub(1, 1) == "#" and minetest.settings:get_bool("ignore_status_messages") ~= false then - return true - end -end) - -minetest.register_cheat("IgnoreStatus", "Player", "ignore_status_messages") diff --git a/clientmods/dragonfire/combat/init.lua b/clientmods/dragonfire/combat/init.lua new file mode 100644 index 000000000..2b7aac533 --- /dev/null +++ b/clientmods/dragonfire/combat/init.lua @@ -0,0 +1,83 @@ +local placed_crystal +local switched_to_totem = 0 +local used_sneak = true +local totem_move_action = InventoryAction("move") +totem_move_action:to("current_player", "main", 9) + +minetest.register_list_command("friend", "Configure Friend List (friends dont get attacked by Killaura or Forcefield)", "friendlist") + +minetest.register_globalstep(function(dtime) + local player = minetest.localplayer + if not player then return end + local control = player:get_control() + local pointed = minetest.get_pointed_thing() + local item = player:get_wielded_item():get_name() + if minetest.settings:get_bool("killaura") or minetest.settings:get_bool("forcefield") and control.dig then + local friendlist = minetest.settings:get("friendlist"):split(",") + for _, obj in ipairs(minetest.get_objects_inside_radius(player:get_pos(), 5)) do + local do_attack = true + if obj:is_local_player() then + do_attack = false + else + for _, friend in ipairs(friendlist) do + if obj:get_name() == friend or obj:get_nametag() == friend then + do_attack = false + break + end + end + end + if do_attack then + obj:punch() + end + end + elseif minetest.settings:get_bool("crystal_pvp") then + if placed_crystal then + if minetest.switch_to_item("mobs_mc:totem") then + switched_to_totem = 5 + end + placed_crystal = false + elseif switched_to_totem > 0 then + if item ~= "mobs_mc:totem" then + switched_to_totem = 0 + elseif pointed and pointed.type == "object" then + pointed.ref:punch() + switched_to_totem = 0 + else + switched_to_totem = switched_to_totem + end + elseif control.place and item == "mcl_end:crystal" then + placed_crystal = true + elseif control.sneak then + if pointed and pointed.type == "node" and not used_sneak then + local pos = minetest.get_pointed_thing_position(pointed) + local node = minetest.get_node_or_nil(pos) + if node and (node.name == "mcl_core:obsidian" or node.name == "mcl_core:bedrock") then + minetest.switch_to_item("mcl_end:crystal") + minetest.place_node(pos) + placed_crystal = true + end + end + used_sneak = true + else + used_sneak = false + end + end + + if minetest.settings:get_bool("autototem") then + local totem_stack = minetest.get_inventory("current_player").main[9] + if totem_stack and totem_stack:get_name() ~= "mobs_mc:totem" then + local totem_index = minetest.find_item("mobs_mc:totem") + if totem_index then + totem_move_action:from("current_player", "main", totem_index) + totem_move_action:apply() + player:set_wield_index(9) + end + end + end +end) + + +minetest.register_cheat("Killaura", "Combat", "killaura") +minetest.register_cheat("Forcefield", "Combat", "forcefield") +minetest.register_cheat("CrystalPvP", "Combat", "crystal_pvp") +minetest.register_cheat("AutoTotem", "Combat", "autototem") diff --git a/clientmods/dragonfire/inventory/autoeject.lua b/clientmods/dragonfire/inventory/autoeject.lua deleted file mode 100644 index 5e61b1ef9..000000000 --- a/clientmods/dragonfire/inventory/autoeject.lua +++ /dev/null @@ -1,19 +0,0 @@ -minetest.register_globalstep(function() - if minetest.settings:get_bool("autoeject") then - local player = minetest.localplayer - local list = (minetest.settings:get("eject_items") or ""):split(",") - local inventory = minetest.get_inventory("current_player") - for index, stack in pairs(inventory.main) do - if table.indexof(list, stack:get_name()) ~= -1 then - local old_index = player:get_wield_index() - player:set_wield_index(index - 1) - minetest.drop_selected_item() - player:set_wield_index(old_index) - return - end - end - end -end) - -minetest.register_chatcommand("eject", list.new("Configure AutoEject", "eject_items")) -minetest.register_cheat("AutoEject", "Player", "autoeject") diff --git a/clientmods/dragonfire/inventory/autotool.lua b/clientmods/dragonfire/inventory/autotool.lua deleted file mode 100644 index ff4432c35..000000000 --- a/clientmods/dragonfire/inventory/autotool.lua +++ /dev/null @@ -1,49 +0,0 @@ -autotool = {} -local odx = nil - -function autotool.check_tool(stack, node_groups, old_best_time) - local toolcaps = stack:get_tool_capabilities() - if not toolcaps then return end - local best_time = old_best_time - for group, groupdef in pairs(toolcaps.groupcaps) do - local level = node_groups[group] - if level then - local this_time = groupdef.times[level] - if this_time < best_time then - best_time = this_time - end - end - end - return best_time < old_best_time, best_time -end - -function autotool.autotool(pos) - if not minetest.settings:get_bool("autotool") then return end - local node = minetest.get_node_or_nil(pos) - if not node then return end - local player = minetest.localplayer - local inventory = minetest.get_inventory("current_player") - local node_groups = minetest.get_node_def(node.name).groups - local new_index = player:get_wield_index() - local is_better, best_time = false, math.huge - is_better, best_time = autotool.check_tool(player:get_wielded_item(), node_groups, best_time) - if inventory.hand then - is_better, best_time = autotool.check_tool(inventory.hand[1], node_groups, best_time) - end - - for index, stack in pairs(inventory.main) do - is_better, best_time = autotool.check_tool(stack, node_groups, best_time) - if is_better then - new_index = index - 1 - end - end - local odx = player:get_wield_index() - player:set_wield_index(new_index) - minetest.after(best_time + 0.05,function() player:set_wield_index(odx) end) -end - -minetest.register_on_punchnode(function(pos,node) - autotool.autotool(pos) -end) - -minetest.register_cheat("AutoTool", "Inventory", "autotool") diff --git a/clientmods/dragonfire/inventory/enderchest.lua b/clientmods/dragonfire/inventory/enderchest.lua deleted file mode 100644 index 45ac216f3..000000000 --- a/clientmods/dragonfire/inventory/enderchest.lua +++ /dev/null @@ -1,27 +0,0 @@ -function get_itemslot_bg(x, y, w, h) - local out = "" - for i = 0, w - 1, 1 do - for j = 0, h - 1, 1 do - out = out .."image["..x+i..","..y+j..";1,1;mcl_formspec_itemslot.png]" - end - end - return out -end - -local formspec = "size[9,8.75]".. - "label[0,0;"..minetest.formspec_escape(minetest.colorize("#313131", "Ender Chest")).."]".. - "list[current_player;enderchest;0,0.5;9,3;]".. - get_itemslot_bg(0,0.5,9,3).. - "label[0,4.0;"..minetest.formspec_escape(minetest.colorize("#313131", "Inventory")).."]".. - "list[current_player;main;0,4.5;9,3;9]".. - get_itemslot_bg(0,4.5,9,3).. - "list[current_player;main;0,7.74;9,1;]".. - get_itemslot_bg(0,7.74,9,1).. - "listring[current_player;enderchest]".. - "listring[current_player;main]" - -function minetest.open_special_inventory() - minetest.show_formspec("enderchest:enderchest", formspec) -end - -minetest.register_cheat("Enderchest", "Inventory", minetest.open_special_inventory) diff --git a/clientmods/dragonfire/inventory/hand.lua b/clientmods/dragonfire/inventory/hand.lua deleted file mode 100644 index 05ec66b5d..000000000 --- a/clientmods/dragonfire/inventory/hand.lua +++ /dev/null @@ -1,27 +0,0 @@ -function get_itemslot_bg(x, y, w, h) - local out = "" - for i = 0, w - 1, 1 do - for j = 0, h - 1, 1 do - out = out .."image["..x+i..","..y+j..";1,1;mcl_formspec_itemslot.png]" - end - end - return out -end - -local formspec = "size[9,8.75]".. - "label[0,0;"..minetest.formspec_escape(minetest.colorize("#313131", "Hand")).."]".. - "list[current_player;hand;0,0.5;1,1;]".. - get_itemslot_bg(0,0.5,1,1).. - "label[0,4.0;"..minetest.formspec_escape(minetest.colorize("#313131", "Inventory")).."]".. - "list[current_player;main;0,4.5;9,3;9]".. - get_itemslot_bg(0,4.5,9,3).. - "list[current_player;main;0,7.74;9,1;]".. - get_itemslot_bg(0,7.74,9,1).. - "listring[current_player;hand]".. - "listring[current_player;main]" - -local function hand() - minetest.show_formspec("inventory:hand", formspec) -end - -minetest.register_cheat("Hand", "Player", hand) diff --git a/clientmods/dragonfire/inventory/init.lua b/clientmods/dragonfire/inventory/init.lua index ce5ea401c..fed1bb452 100644 --- a/clientmods/dragonfire/inventory/init.lua +++ b/clientmods/dragonfire/inventory/init.lua @@ -1,11 +1,179 @@ -inventory_mod = {} +local drop_action = InventoryAction("drop") + +local strip_move_act = InventoryAction("move") +strip_move_act:to("current_player", "craft", 1) +local strip_craft_act = InventoryAction("craft") +strip_craft_act:craft("current_player") +local strip_move_back_act = InventoryAction("move") +strip_move_back_act:from("current_player", "craftresult", 1) + +minetest.register_globalstep(function(dtime) + local player = minetest.localplayer + if not player then return end + local item = player:get_wielded_item() + local itemdef = minetest.get_item_def(item:get_name()) + local wieldindex = player:get_wield_index() + -- AutoRefill + if minetest.settings:get_bool("autorefill") and itemdef then + local space = item:get_free_space() + local i = minetest.find_item(item:get_name(), wieldindex + 1) + if i and space > 0 then + local move_act = InventoryAction("move") + move_act:to("current_player", "main", wieldindex) + move_act:from("current_player", "main", i) + move_act:set_count(space) + move_act:apply() + end + end + -- AutoPlanks (Strip in DF) + if minetest.settings:get_bool("autoplanks") then + if itemdef and itemdef.groups.tree and player:get_control().place then + strip_move_act:from("current_player", "main", wieldindex) + strip_move_back_act:to("current_player", "main", wieldindex) + strip_move_act:apply() + strip_craft_act:apply() + strip_move_back_act:apply() + end + end + -- AutoEject + if minetest.settings:get_bool("autoeject") then + local list = (minetest.settings:get("eject_items") or ""):split(",") + local inventory = minetest.get_inventory("current_player") + for index, stack in pairs(inventory.main) do + if table.indexof(list, stack:get_name()) ~= -1 then + drop_action:from("current_player", "main", index) + drop_action:apply() + end + end + end +end) + +minetest.register_list_command("eject", "Configure AutoEject", "eject_items") + +-- AutoTool + +local function check_tool(stack, node_groups, old_best_time) + local toolcaps = stack:get_tool_capabilities() + if not toolcaps then return end + local best_time = old_best_time + for group, groupdef in pairs(toolcaps.groupcaps) do + local level = node_groups[group] + if level then + local this_time = groupdef.times[level] + if this_time < best_time then + best_time = this_time + end + end + end + return best_time < old_best_time, best_time +end + +local function find_best_tool(nodename, switch) + local player = minetest.localplayer + local inventory = minetest.get_inventory("current_player") + local node_groups = minetest.get_node_def(nodename).groups + local new_index = player:get_wield_index() + local is_better, best_time = false, math.huge + + is_better, best_time = check_tool(player:get_wielded_item(), node_groups, best_time) + if inventory.hand then + is_better, best_time = check_tool(inventory.hand[1], node_groups, best_time) + end + + for index, stack in ipairs(inventory.main) do + is_better, best_time = check_tool(stack, node_groups, best_time) + if is_better then + new_index = index + end + end + + return new_index +end + +function minetest.select_best_tool(nodename) + minetest.localplayer:set_wield_index(find_best_tool(nodename)) +end + +local new_index, old_index, pointed_pos + +minetest.register_on_punchnode(function(pos, node) + if minetest.settings:get_bool("autotool") then + pointed_pos = pos + old_index = old_index or minetest.localplayer:get_wield_index() + new_index = find_best_tool(node.name) + end +end) + +minetest.register_globalstep(function() + local player = minetest.localplayer + if not new_index then return end + if minetest.settings:get_bool("autotool") then + local pt = minetest.get_pointed_thing() + if pt and pt.type == "node" and vector.equals(minetest.get_pointed_thing_position(pt), pointed_pos) and player:get_control().dig then + player:set_wield_index(new_index) + return + end + end + player:set_wield_index(old_index) + new_index, old_index, pointed_pos = nil +end) + +-- Enderchest + +function get_itemslot_bg(x, y, w, h) + local out = "" + for i = 0, w - 1, 1 do + for j = 0, h - 1, 1 do + out = out .."image["..x+i..","..y+j..";1,1;mcl_formspec_itemslot.png]" + end + end + return out +end + +local enderchest_formspec = "size[9,8.75]".. + "label[0,0;"..minetest.formspec_escape(minetest.colorize("#313131", "Ender Chest")).."]".. + "list[current_player;enderchest;0,0.5;9,3;]".. + get_itemslot_bg(0,0.5,9,3).. + "label[0,4.0;"..minetest.formspec_escape(minetest.colorize("#313131", "Inventory")).."]".. + "list[current_player;main;0,4.5;9,3;9]".. + get_itemslot_bg(0,4.5,9,3).. + "list[current_player;main;0,7.74;9,1;]".. + get_itemslot_bg(0,7.74,9,1).. + "listring[current_player;enderchest]".. + "listring[current_player;main]" + +function minetest.open_enderchest() + minetest.show_formspec("inventory:enderchest", enderchest_formspec) +end + +-- HandSlot + +local hand_formspec = "size[9,8.75]".. + "label[0,0;"..minetest.formspec_escape(minetest.colorize("#313131", "Hand")).."]".. + "list[current_player;hand;0,0.5;1,1;]".. + get_itemslot_bg(0,0.5,1,1).. + "label[0,4.0;"..minetest.formspec_escape(minetest.colorize("#313131", "Inventory")).."]".. + "list[current_player;main;0,4.5;9,3;9]".. + get_itemslot_bg(0,4.5,9,3).. + "list[current_player;main;0,7.74;9,1;]".. + get_itemslot_bg(0,7.74,9,1).. + "listring[current_player;hand]".. + "listring[current_player;main]" + +function minetest.open_handslot() + minetest.show_formspec("inventory:hand", hand_formspec) +end + +minetest.register_cheat("AutoEject", "Inventory", "autoeject") +minetest.register_cheat("AutoTool", "Inventory", "autotool") +minetest.register_cheat("Hand", "Inventory", minetest.open_handslot) +minetest.register_cheat("Enderchest", "Inventory", minetest.open_enderchest) +minetest.register_cheat("AutoPlanks", "Inventory", "autoplanks") +minetest.register_cheat("AutoRefill", "Inventory", "autorefill") local modname = minetest.get_current_modname() local modpath = minetest.get_modpath(modname) -dofile(modpath .. "/invhack.lua") -dofile(modpath .. "/enderchest.lua") -dofile(modpath .. "/hand.lua") dofile(modpath .. "/next_item.lua") -dofile(modpath .. "/autotool.lua") -dofile(modpath .. "/autoeject.lua") +dofile(modpath .. "/invhack.lua") + diff --git a/clientmods/dragonfire/movement/init.lua b/clientmods/dragonfire/movement/init.lua new file mode 100644 index 000000000..9d28e6ca9 --- /dev/null +++ b/clientmods/dragonfire/movement/init.lua @@ -0,0 +1,44 @@ +local function register_keypress_cheat(cheat, keyname, condition) + local was_active = false + minetest.register_globalstep(function() + local is_active = minetest.settings:get_bool(cheat) and (not condition or condition()) + if is_active then + minetest.set_keypress(keyname, true) + elseif was_active then + minetest.set_keypress(keyname, false) + end + was_active = is_active + end) +end + +register_keypress_cheat("autosneak", "sneak", function() + return minetest.localplayer:is_touching_ground() +end) +register_keypress_cheat("autosprint", "special1") + +local legit_override + +local function get_override_factor(name) + if minetest.settings:get_bool("override_" .. name) then + return tonumber(minetest.settings:get("override_" .. name .. "_factor")) or 1 + else + return 1.0 + end +end + +minetest.register_globalstep(function() + if not legit_override then return end + local override = table.copy(legit_override) + override.speed = override.speed * get_override_factor("speed") + override.jump = override.jump * get_override_factor("jump") + override.gravity = override.gravity * get_override_factor("gravity") + minetest.localplayer:set_physics_override(override) +end) + +minetest.register_on_recieve_physics_override(function(override) + legit_override = override + return true +end) + +minetest.register_cheat("AutoSneak", "Movement", "autosneak") +minetest.register_cheat("AutoSprint", "Movement", "autosprint") diff --git a/clientmods/dragonfire/autosneak/mod.conf b/clientmods/dragonfire/movement/mod.conf similarity index 100% rename from clientmods/dragonfire/autosneak/mod.conf rename to clientmods/dragonfire/movement/mod.conf diff --git a/clientmods/dragonfire/autosneak/settingtypes.txt b/clientmods/dragonfire/movement/settingtypes.txt similarity index 100% rename from clientmods/dragonfire/autosneak/settingtypes.txt rename to clientmods/dragonfire/movement/settingtypes.txt diff --git a/clientmods/dragonfire/render/init.lua b/clientmods/dragonfire/render/init.lua new file mode 100644 index 000000000..5e768082d --- /dev/null +++ b/clientmods/dragonfire/render/init.lua @@ -0,0 +1,16 @@ +core.register_list_command("xray", "Configure X-Ray", "xray_nodes") +core.register_list_command("search", "Configure NodeESP", "node_esp_nodes") + +core.register_on_spawn_particle(function(particle) + if core.settings:get_bool("noweather") and particle.texture:sub(1, 12) == "weather_pack" then + return true + end +end) + +core.register_on_play_sound(function(sound) + if core.settings:get_bool("noweather") and sound.name == "weather_rain" then + return true + end +end) + +minetest.register_cheat("NoWeather", "Render", "noweather") diff --git a/clientmods/dragonfire/world/init.lua b/clientmods/dragonfire/world/init.lua index 5decbb64e..9f54a4b9d 100644 --- a/clientmods/dragonfire/world/init.lua +++ b/clientmods/dragonfire/world/init.lua @@ -71,7 +71,11 @@ minetest.register_globalstep(function(dtime) local nodes_per_tick = tonumber(minetest.settings:get("nodes_per_tick")) or 8 if item:get_count() > 0 and def.node_placement_prediction ~= "" then if minetest.settings:get_bool("scaffold") then - minetest.place_node(vector.add(pos, {x = 0, y = -0.6, z = 0})) + local p = vector.round(vector.add(pos, {x = 0, y = -0.6, z = 0})) + local node = minetest.get_node_or_nil(p) + if not node or minetest.get_node_def(node.name).buildable_to then + minetest.place_node(p) + end elseif minetest.settings:get_bool("mscaffold") then --local z = pos.z local positions = { @@ -109,7 +113,7 @@ minetest.register_globalstep(function(dtime) minetest.place_node(p) end elseif minetest.settings:get_bool("block_water") then - local positions = minetest.find_nodes_near(pos, 5, {"mcl_core:water_source", "mcl_core:water_floating"}, true) + local positions = minetest.find_nodes_near(pos, 5, {"mcl_core:water_source", "mcl_core:water_flowing"}, true) for i, p in pairs(positions) do if i > nodes_per_tick then break end minetest.place_node(p) @@ -122,6 +126,23 @@ minetest.register_globalstep(function(dtime) end end end + if minetest.settings:get_bool("nuke") then + local i = 0 + for x = pos.x - 4, pos.x + 4 do + for y = pos.y - 4, pos.y + 4 do + for z = pos.z - 4, pos.z + 4 do + local p = vector.new(x, y, z) + local node = minetest.get_node_or_nil(p) + local def = node and minetest.get_node_def(node.name) + if def and def.diggable then + if i > nodes_per_tick then return end + minetest.dig_node(p) + i = i + 1 + end + end + end + end + end end) minetest.register_cheat("mScaffold", "World", "mscaffold") @@ -130,3 +151,4 @@ minetest.register_cheat("HighwayZ", "World", "highway_z") minetest.register_cheat("BlockWater", "World", "block_water") minetest.register_cheat("AutoTNT", "World", "autotnt") minetest.register_cheat("Replace", "World", "replace") +minetest.register_cheat("Nuke", "World", "nuke") diff --git a/clientmods/mods.conf b/clientmods/mods.conf index 997b25363..1347ca2e5 100644 --- a/clientmods/mods.conf +++ b/clientmods/mods.conf @@ -31,7 +31,6 @@ load_mod_autodupe = true load_mod_randomscreenshot = true load_mod_util = true load_mod_scaffold = true -load_mod_adupe = false load_mod_speedlimit = true load_mod_frenemies = true load_mod_autocraft = true @@ -41,3 +40,5 @@ load_mod_nlist = true load_mod_kamikaze = true load_mod_muse = true load_mod_optimize = true +load_mod_render = true +load_mod_combat = true diff --git a/clientmods/mods_here.txt b/clientmods/mods_here.txt new file mode 100644 index 000000000..ea2175f2d --- /dev/null +++ b/clientmods/mods_here.txt @@ -0,0 +1,4 @@ +You can install Minetest or Dragonfire clientmods by copying (and extracting) them into this folder. +To enable them write + load_mod_ = true +in mods.conf in this directory. diff --git a/doc/builtin_entities.txt b/doc/builtin_entities.txt new file mode 100644 index 000000000..be3f73357 --- /dev/null +++ b/doc/builtin_entities.txt @@ -0,0 +1,101 @@ +# Builtin Entities +Minetest registers two entities by default: Falling nodes and dropped items. +This document describes how they behave and what you can do with them. + +## Falling node (`__builtin:falling_node`) + +This entity is created by `minetest.check_for_falling` in place of a node +with the special group `falling_node=1`. Falling nodes can also be created +artificially with `minetest.spawn_falling_node`. + +Needs manual initialization when spawned using `/spawnentity`. + +Default behaviour: + +* Falls down in a straight line (gravity = `movement_gravity` setting) +* Collides with `walkable` node +* Collides with all physical objects except players +* If the node group `float=1` is set, it also collides with liquid nodes +* When it hits a solid (=`walkable`) node, it will try to place itself as a + node, replacing the node above. + * If the falling node cannot replace the destination node, it is dropped. + * If the destination node is a leveled node (`paramtype2="leveled"`) of the + same node name, the levels of both are summed. + +### Entity fields + +* `set_node(self, node[, meta])` + * Function to initialize the falling node + * `node` and `meta` are explained below. + * The `meta` argument is optional. +* `node`: Node table of the node (`name`, `param1`, `param2`) that this + entity represents. Read-only. +* `meta`: Node metadata of the falling node. Will be used when the falling + nodes tries to place itself as a node. Read-only. + +### Rendering / supported nodes + +Falling nodes have visuals to look as close as possible to the original node. +This works for most drawtypes, but there are limitations. + +Supported drawtypes: + +* `normal` +* `signlike` +* `torchlike` +* `nodebox` +* `raillike` +* `glasslike` +* `glasslike_framed` +* `glasslike_framed_optional` +* `allfaces` +* `allfaces_optional` +* `firelike` +* `mesh` +* `fencelike` +* `liquid` +* `airlike` (not pointable) + +Other drawtypes still kinda work, but they might look weird. + +Supported `paramtype2` values: + +* `wallmounted` +* `facedir` +* `colorwallmounted` +* `colorfacedir` +* `color` + +## Dropped item stack (`__builtin:item`) + +This is an item stack in a collectable form. + +Common cases that spawn a dropped item: + +* Item dropped by player +* The root node of a node with the group `attached_node=1` is removed +* `minetest.add_item` is called + +Needs manual initialization when spawned using `/spawnentity`. + +### Behavior + +* Players can collect it by punching +* Lifespan is defined by the setting `item_entity_ttl` +* Slides on `slippery` nodes +* Subject to gravity (uses `movement_gravity` setting) +* Collides with `walkable` nodes +* Does not collide physical objects +* When it's inside a solid (`walkable=true`) node, it tries to escape to a + neighboring non-solid (`walkable=false`) node + +### Entity fields + +* `set_item(self, item)`: + * Function to initialize the dropped item + * `item` (type `ItemStack`) specifies the item to represent +* `age`: Age in seconds. Behaviour according to the setting `item_entity_ttl` +* `itemstring`: Itemstring of the item that this item entity represents. + Read-only. + +Other fields are for internal use only. diff --git a/doc/client_lua_api.txt b/doc/client_lua_api.txt index 5022c8fd3..a8109fa59 100644 --- a/doc/client_lua_api.txt +++ b/doc/client_lua_api.txt @@ -219,7 +219,7 @@ For helper functions see "Vector helpers". ### pointed_thing * `{type="nothing"}` * `{type="node", under=pos, above=pos}` -* `{type="object", id=ObjectID}` +* `{type="object", ref=ClientObjectRef}` Flag Specifier Format --------------------- @@ -674,9 +674,8 @@ Minetest namespace reference ### Global callback registration functions Call these functions only at load time! -* `minetest.open_special_inventory()` +* `minetest.open_enderchest()` * This function is called if the client uses the Keybind for it (by default "O") - * It is used for a client provided inventory * You can override it * `minetest.register_globalstep(function(dtime))` * Called every client environment step, usually interval of 0.1s @@ -697,6 +696,18 @@ Call these functions only at load time! * Adds definition to minetest.registered_chatcommands * `minetest.unregister_chatcommand(name)` * Unregisters a chatcommands registered with register_chatcommand. +* `minetest.register_list_command(command, desc, setting)` + * Registers a chatcommand `command` to manage a list that takes the args `del | add | list ` + * The list is stored comma-seperated in `setting` + * `desc` is the description + * `add` adds something to the list + * `del` del removes something from the list + * `list` lists all items on the list +* `minetest.register_on_chatcommand(function(command, params))` + * Called always when a chatcommand is triggered, before `minetest.registered_chatcommands` + is checked to see if that the command exists, but after the input is parsed. + * Return `true` to mark the command as handled, which means that the default + handlers will be prevented. * `minetest.register_on_death(function())` * Called when the local player dies * `minetest.register_on_hp_modification(function(hp))` @@ -740,6 +751,18 @@ Call these functions only at load time! * Called when the local player open inventory * Newest functions are called first * If any function returns true, inventory doesn't open +* `minetest.register_on_recieve_physics_override(function(override))` + * Called when recieving physics_override from server + * Newest functions are called first + * If any function returns true, the physics override does not change +* `minetest.register_on_play_sound(function(SimpleSoundSpec))` + * Called when recieving a play sound command from server + * Newest functions are called first + * If any function returns true, the sound does not play +* `minetest.register_on_spawn_partice(function(particle definition))` + * Called when recieving a spawn particle command from server + * Newest functions are called first + * If any function returns true, the particel does not spawn ### Setting-related * `minetest.settings`: Settings object containing all of the settings from the @@ -837,6 +860,13 @@ Call these functions only at load time! * `pos2`: end of the ray * `objects`: if false, only nodes will be returned. Default is `true`. * `liquids`: if false, liquid nodes won't be returned. Default is `false`. +* `minetest.get_pointed_thing()` returns `PointedThing` + * Returns the thing currently pointed by player +* `minetest.get_pointed_thing_position(pointed_thing, above)` + * Returns the position of a `pointed_thing` or `nil` if the `pointed_thing` + does not refer to a node or entity. + * If the optional `above` parameter is true and the `pointed_thing` refers + to a node, then it will return the `above` position of the `pointed_thing`. * `minetest.find_path(pos1,pos2,searchdistance,max_jump,max_drop,algorithm)` * returns table containing path that can be walked on * returns a table of 3D points representing a path from `pos1` to `pos2` or @@ -891,6 +921,9 @@ Call these functions only at load time! ### Client Environment * `minetest.get_player_names()` * Returns list of player names on server (nil if CSM_RF_READ_PLAYERINFO is enabled by server) +* `minetest.get_objects_inside_radius(pos, radius)`: returns a list of + ClientObjectRefs. + * `radius`: using an euclidean metric * `minetest.disconnect()` * Disconnect from the server and exit to main menu. * Returns `false` if the client is already disconnecting otherwise returns `true`. @@ -996,6 +1029,13 @@ Passed to `HTTPApiTable.fetch` callback. Returned by * e.g. minetest.set_keypress("jump", true) will cause te player to jump until minetest.set_keypress("jump", false) is called or the player presses & releases the space bar himself * `minetest.get_inventory(location)` * Returns the inventory at location +* `minetest.find_item(item)` + * finds and an item in the inventory + * returns index on success or nil if item is not found +* `minetest.switch_to_item(item)` + * `item` is an Itemstring + * searches to item in inventory, sets the wield index to it if found + * returns true on success, false if item was not found * `minetest.register_cheat(name, category, setting | function)` * Register an entry for the cheat menu * If the Category is nonexistant, it will be created @@ -1060,6 +1100,8 @@ Passed to `HTTPApiTable.fetch` callback. Returned by * returns the exact position on the surface of a pointed node * `minetest.global_exists(name)` * Checks if a global variable has been set, without triggering a warning. +* `minetest.make_screenshot()` + * Triggers the MT makeScreenshot functionality ### UI * `minetest.ui.minimap` @@ -1069,6 +1111,14 @@ Passed to `HTTPApiTable.fetch` callback. Returned by * Reference to the camera object. See [`Camera`](#camera) class reference for methods. * `minetest.show_formspec(formname, formspec)` : returns true on success * Shows a formspec to the player +* `minetest.close_formspec(formname)` + * `formname`: has to exactly match the one given in `show_formspec`, or the + formspec will not close. + * calling `show_formspec(formname, "")` is equal to this + expression. + * to close a formspec regardless of the formname, call + `minetest.close_formspec("")`. + **USE THIS ONLY WHEN ABSOLUTELY NECESSARY!** * `minetest.display_chat_message(message)` returns true on success * Shows a chat message to the current player. @@ -1152,6 +1202,10 @@ Methods: * returns the yaw (degrees) * `set_yaw(yaw)` * sets the yaw (degrees) +* `get_pitch()` + * returns the pitch (degrees) +* `set_pitch(pitch)` + * sets the pitch (degrees) * `get_velocity()` * returns player speed vector * `set_velocity(vel)` @@ -1161,9 +1215,9 @@ Methods: * `get_name()` * returns player name * `get_wield_index()` - * returns the index of the wielded item + * returns the index of the wielded item (starts at 1) * `set_wield_index()` - * sets the index + * sets the index (starts at 1) * `get_wielded_item()` * returns the itemstack the player is holding * `is_attached()` @@ -1194,6 +1248,17 @@ Methods: } ``` +* `set_physics_override(override_table)` + * `override_table` is a table with the following fields: + * `speed`: multiplier to default walking speed value (default: `1`) + * `jump`: multiplier to default jump value (default: `1`) + * `gravity`: multiplier to default gravity value (default: `1`) + * `sneak`: whether player can sneak (default: `true`) + * `sneak_glitch`: whether player can use the new move code replications + of the old sneak side-effects: sneak ladders and 2 node sneak jump + (default: `false`) + * `new_move`: use new move/sneak code. When `false` the exact old code + is used for the specific old sneak behaviour (default: `true`) * `get_override_pos()` * returns override position * `get_last_pos()` @@ -1255,8 +1320,8 @@ Methods: aux1 = boolean, sneak = boolean, zoom = boolean, - LMB = boolean, - RMB = boolean, + dig = boolean, + place = boolean, } ``` @@ -1273,6 +1338,8 @@ Methods: * change a value of a previously added HUD element * element `stat` values: `position`, `name`, `scale`, `text`, `number`, `item`, `dir` * Returns `true` on success, otherwise returns `nil` +* `get_object()` + * Returns the ClientObjectRef for the player ### Settings An interface to read config files in the format of `minetest.conf`. @@ -1301,6 +1368,26 @@ Can be obtained via `minetest.get_meta(pos)`. * `fields`: key-value storage * `inventory`: `{list1 = {}, ...}}` +### ClientObjectRef + +Moving things in the game are generally these. +This is basically a reference to a C++ `GenericCAO`. + +#### Methods + +* `get_pos()`: returns `{x=num, y=num, z=num}` +* `get_velocity()`: returns the velocity, a vector +* `get_acceleration()`: returns the acceleration, a vector +* `get_rotation()`: returns the rotation, a vector (radians) +* `is_player()`: returns true if the object is a player +* `is_local_player()`: returns true if the object is the local player +* `get_attach()`: returns parent or nil if it isn't attached. +* `get_nametag()`: returns the nametag (string) +* `get_item_textures()`: returns the textures +* `get_max_hp()`: returns the maximum heath +* `punch()`: punches the object +* `rightclick()`: rightclicks the object + ### `Raycast` A raycast on the map. It works with selection boxes. @@ -1480,6 +1567,8 @@ The following functions provide escape sequences: `minetest.get_color_escape_sequence(color) .. message .. minetest.get_color_escape_sequence("#ffffff")` +* `minetest.rainbow(message)`: + * Rainbow colorizes the message. * `minetest.get_background_escape_sequence(color)` * `color` is a [ColorString](#colorstring) * The escape sequence sets the background of the whole text element to @@ -1665,3 +1754,47 @@ Same as `image`, but does not accept a `position`; the position is instead deter -- ^ Uses texture (string) } +### InventoryAction +A reference to a C++ InventoryAction. You can move, drop and craft items in all accessible inventories using InventoryActions. + +#### methods + +* `InventoryAction(type)`: + * creates a new InventoryAction + * type is on of "move", "drop", or "craft", else returns nil + * indexing starts at 1 +* `apply()`: + * applies the InventoryAction (InventoryActions can be applied multible times) +* `from(inventorylocation, listname, stack)` + * this is valid for move or drop actions + * when `apply()` is called items are moved / dropped from `listname` `inventorylocation` in` at `stack` +* `to(inventorylocation, listname, stack)` + * this is valid for move actions + * when `apply()` is called items are moved to `listname` in`inventorylocation` at `stack` +* `craft(inventoryaction)` + * this is valid for craft actions + * when `apply()` is called a craft event for this inventory will be triggered +* `set_count(count)` + * this is valid for all actions + * it specifies how many items to drop / craft / move + * `0` means move all items + * default count: `0` + +#### example + `local move_act = InventoryAction("move") + move_act:from("current_player", "main", 1) + move_act:to("current_player", "craft", 1) + move_act:set_count(1) + local craft_act = InventoryAction("craft") + craft_act:craft("current_player") + local drop_act = InventoryAction("drop") + drop_act:from("current_player", "craft_result",10) + move_act:apply() + craft_act:apply() + drop_act:apply() + ` + * e.g. In first hotbar slot there are tree logs: Move one to craft field, then craft wood out of it and immediately drop it + + + + diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 353ccc924..0fe524035 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -62,12 +62,12 @@ Where `` is unique to each game. The game directory can contain the following files: * `game.conf`, with the following keys: - * `name`: Required, human readable name e.g. `name = Minetest` + * `name`: Required, a human readable title to address the game, e.g. `name = Minetest`. * `description`: Short description to be shown in the content tab * `allowed_mapgens = ` e.g. `allowed_mapgens = v5,v6,flat` - Mapgens not in this list are removed from the list of mapgens for - the game. + Mapgens not in this list are removed from the list of mapgens for the + game. If not specified, all mapgens are allowed. * `disallowed_mapgens = ` e.g. `disallowed_mapgens = v5,v6,flat` @@ -79,6 +79,10 @@ The game directory can contain the following files: e.g. `disallowed_mapgen_settings = mgv5_spflags` These settings are hidden for this game in the world creation dialog and game start menu. + * `author`: The author of the game. It only appears when downloaded from + ContentDB. + * `release`: Ignore this: Should only ever be set by ContentDB, as it is + an internal ID used to track versions. * `minetest.conf`: Used to set default settings when running this game. * `settingtypes.txt`: @@ -134,9 +138,15 @@ Mods can be put in a subdirectory, if the parent directory, which otherwise should be a mod, contains a file named `modpack.conf`. The file is a key-value store of modpack details. -* `name`: The modpack name. +* `name`: The modpack name. Allows Minetest to determine the modpack name even + if the folder is wrongly named. * `description`: Description of mod to be shown in the Mods tab of the main menu. +* `author`: The author of the modpack. It only appears when downloaded from + ContentDB. +* `release`: Ignore this: Should only ever be set by ContentDB, as it is an + internal ID used to track versions. +* `title`: A human-readable title to address the modpack. Note: to support 0.4.x, please also create an empty modpack.txt file. @@ -152,7 +162,12 @@ Mod directory structure │   ├── models │   ├── textures │   │   ├── modname_stuff.png - │   │   └── modname_something_else.png + │   │   ├── modname_stuff_normal.png + │   │   ├── modname_something_else.png + │   │   ├── subfolder_foo + │   │   │ ├── modname_more_stuff.png + │   │   │ └── another_subfolder + │   │   └── bar_subfolder │   ├── sounds │   ├── media │   ├── locale @@ -176,6 +191,11 @@ A `Settings` file that provides meta information about the mod. loaded before this mod. * `optional_depends`: A comma separated list of optional dependencies. Like a dependency, but no error if the mod doesn't exist. +* `author`: The author of the mod. It only appears when downloaded from + ContentDB. +* `release`: Ignore this: Should only ever be set by ContentDB, as it is an + internal ID used to track versions. +* `title`: A human-readable title to address the mod. Note: to support 0.4.x, please also provide depends.txt. @@ -221,18 +241,20 @@ registered callbacks. `minetest.settings` can be used to read custom or existing settings at load time, if necessary. (See [`Settings`]) -### `models` - -Models for entities or meshnodes. - -### `textures`, `sounds`, `media` +### `textures`, `sounds`, `media`, `models`, `locale` Media files (textures, sounds, whatever) that will be transferred to the -client and will be available for use by the mod. +client and will be available for use by the mod and translation files for +the clients (see [Translations]). -### `locale` +It is suggested to use the folders for the purpous they are thought for, +eg. put textures into `textures`, translation files into `locale`, +models for entities or meshnodes into `models` et cetera. -Translation files for the clients. (See [Translations]) +These folders and subfolders can contain subfolders. +Subfolders with names starting with `_` or `.` are ignored. +If a subfolder contains a media file with the same name as a media file +in one of its parents, the parent's file is used. Naming conventions ------------------ @@ -378,11 +400,14 @@ stripping out the file extension: * e.g. `foomod_foothing.png` * e.g. `foomod_foothing` + Texture modifiers ----------------- There are various texture modifiers that can be used -to generate textures on-the-fly. +to let the client generate textures on-the-fly. +The modifiers are applied directly in sRGB colorspace, +i.e. without gamma-correction. ### Texture overlaying @@ -780,7 +805,7 @@ Example (colored grass block): -- Overlay tiles: define them in the same style -- The top and bottom tile does not have overlay overlay_tiles = {"", "", - {name = "default_grass_side.png", tileable_vertical = false}}, + {name = "default_grass_side.png"}}, -- Global color, used in inventory color = "green", -- Palette in the world @@ -1150,7 +1175,7 @@ Look for examples in `games/devtest` or `games/minetest_game`. base cube without affecting them. * The base cube texture tiles are defined as normal, the `plantlike` extension uses the defined special tile, for example: - `special_tiles = {{name = "default_papyrus.png", tileable_vertical = true}},` + `special_tiles = {{name = "default_papyrus.png"}},` `*_optional` drawtypes need less rendering time if deactivated (always client-side). @@ -1414,7 +1439,32 @@ Same as `image`, but does not accept a `position`; the position is instead deter * `world_pos`: World position of the waypoint. * `offset`: offset in pixels from position. +### `compass` +Displays an image oriented or translated according to current heading direction. + +* `size`: The size of this element. Negative values represent percentage + of the screen; e.g. `x=-100` means 100% (width). +* `scale`: Scale of the translated image (used only for dir = 2 or dir = 3). +* `text`: The name of the texture to use. +* `alignment`: The alignment of the image. +* `offset`: Offset in pixels from position. +* `dir`: How the image is rotated/translated: + * 0 - Rotate as heading direction + * 1 - Rotate in reverse direction + * 2 - Translate as landscape direction + * 3 - Translate in reverse direction + +If translation is chosen, texture is repeated horizontally to fill the whole element. + +### `minimap` + +Displays a minimap on the HUD. + +* `size`: Size of the minimap to display. Minimap should be a square to avoid + distortion. +* `alignment`: The alignment of the minimap. +* `offset`: offset in pixels from position. Representations of simple things ================================ @@ -1973,8 +2023,10 @@ Item metadata only contains a key-value store. Some of the values in the key-value store are handled specially: -* `description`: Set the item stack's description. Defaults to - `idef.description`. +* `description`: Set the item stack's description. + See also: `get_description` in [`ItemStack`] +* `short_description`: Set the item stack's short description. + See also: `get_short_description` in [`ItemStack`] * `color`: A `ColorString`, which sets the stack's color. * `palette_index`: If the item has a palette, this is used to get the current color from the palette. @@ -2220,6 +2272,18 @@ Elements * `frame duration`: Milliseconds between each frame. `0` means the frames don't advance. * `frame start` (Optional): The index of the frame to start on. Default `1`. +### `model[,;,;;;;;;]` + +* Show a mesh model. +* `name`: Element name that can be used for styling +* `mesh`: The mesh model to use. +* `textures`: The mesh textures to use according to the mesh materials. + Texture names must be separated by commas. +* `rotation {X,Y}` (Optional): Initial rotation of the camera. + The axes are euler angles in degrees. +* `continuous` (Optional): Whether the rotation is continuous. Default `false`. +* `mouse control` (Optional): Whether the model can be controlled with the mouse. Default `true`. + ### `item_image[,;,;]` * Show an inventory image of registered item/node @@ -2442,6 +2506,8 @@ Elements * Simple colored box * `color` is color specified as a `ColorString`. If the alpha component is left blank, the box will be semitransparent. + If the color is not specified, the box will use the options specified by + its style. If the color is specified, all styling options will be ignored. ### `dropdown[,;;;,, ...,;;]` @@ -2708,21 +2774,23 @@ Setting a property to nothing will reset it to the default value. For example: Some types may inherit styles from parent types. * animated_image, inherits from image +* box * button * button_exit, inherits from button * checkbox -* scrollbar -* table -* textlist * dropdown * field -* pwdfield, inherits from field -* textarea -* label -* vertlabel, inherits from field +* image * image_button * item_image_button +* label +* pwdfield, inherits from field +* scrollbar * tabheader +* table +* textarea +* textlist +* vertlabel, inherits from label ### Valid Properties @@ -2731,7 +2799,18 @@ Some types may inherit styles from parent types. * noclip - boolean, set to true to allow the element to exceed formspec bounds. * box * noclip - boolean, set to true to allow the element to exceed formspec bounds. - * Default to false in formspec_version version 3 or higher + * Defaults to false in formspec_version version 3 or higher + * **Note**: `colors`, `bordercolors`, and `borderwidths` accept multiple input types: + * Single value (e.g. `#FF0`): All corners/borders. + * Two values (e.g. `red,#FFAAFF`): top-left and bottom-right,top-right and bottom-left/ + top and bottom,left and right. + * Four values (e.g. `blue,#A0F,green,#FFFA`): top-left/top and rotates clockwise. + * These work similarly to CSS borders. + * colors - `ColorString`. Sets the color(s) of the box corners. Default `black`. + * bordercolors - `ColorString`. Sets the color(s) of the borders. Default `black`. + * borderwidths - Integer. Sets the width(s) of the borders in pixels. If the width is + negative, the border will extend inside the box, whereas positive extends outside + the box. A width of zero results in no border; this is default. * button, button_exit, image_button, item_image_button * alpha - boolean, whether to draw alpha in bgimg. Default true. * bgcolor - color, sets button tint. @@ -2764,23 +2843,24 @@ Some types may inherit styles from parent types. * noclip - boolean, set to true to allow the element to exceed formspec bounds. * padding - rect, adds space between the edges of the button and the content. This value is relative to bgimg_middle. + * sound - a sound to be played when clicked. * textcolor - color, default white. * checkbox * noclip - boolean, set to true to allow the element to exceed formspec bounds. -* scrollbar - * noclip - boolean, set to true to allow the element to exceed formspec bounds. -* table, textlist - * font - Sets font type. See button `font` property for more information. - * font_size - Sets font size. See button `font_size` property for more information. - * noclip - boolean, set to true to allow the element to exceed formspec bounds. + * sound - a sound to be played when clicked. * dropdown * noclip - boolean, set to true to allow the element to exceed formspec bounds. + * sound - a sound to be played when clicked. * field, pwdfield, textarea * border - set to false to hide the textbox background and border. Default true. * font - Sets font type. See button `font` property for more information. * font_size - Sets font size. See button `font_size` property for more information. * noclip - boolean, set to true to allow the element to exceed formspec bounds. * textcolor - color. Default white. +* model + * bgcolor - color, sets background color. + * noclip - boolean, set to true to allow the element to exceed formspec bounds. + * Default to false in formspec_version version 3 or higher * image * noclip - boolean, set to true to allow the element to exceed formspec bounds. * Default to false in formspec_version version 3 or higher @@ -2797,9 +2877,17 @@ Some types may inherit styles from parent types. * fgimg_pressed - image when pressed. Defaults to fgimg when not provided. * This is deprecated, use states instead. * NOTE: The parameters of any given image_button will take precedence over fgimg/fgimg_pressed + * sound - a sound to be played when clicked. +* scrollbar + * noclip - boolean, set to true to allow the element to exceed formspec bounds. * tabheader * noclip - boolean, set to true to allow the element to exceed formspec bounds. + * sound - a sound to be played when clicked. * textcolor - color. Default white. +* table, textlist + * font - Sets font type. See button `font` property for more information. + * font_size - Sets font size. See button `font_size` property for more information. + * noclip - boolean, set to true to allow the element to exceed formspec bounds. ### Valid States @@ -2988,6 +3076,8 @@ The following functions provide escape sequences: `minetest.get_color_escape_sequence(color) .. message .. minetest.get_color_escape_sequence("#ffffff")` +* `minetest.rainbow(message)`: + * Rainbow colorizes the message. * `minetest.get_background_escape_sequence(color)` * `color` is a ColorString * The escape sequence sets the background of the whole text element to @@ -3012,7 +3102,8 @@ Internally, it is implemented as a table with the 3 fields `x`, `y` and `z`. Example: `{x = 0, y = 1, z = 0}`. For the following functions, `v`, `v1`, `v2` are vectors, -`p1`, `p2` are positions: +`p1`, `p2` are positions, +`s` is a scalar (a number): * `vector.new(a[, b, c])`: * Returns a vector. @@ -3041,10 +3132,12 @@ For the following functions, `v`, `v1`, `v2` are vectors, * Returns in order minp, maxp vectors of the cuboid defined by `v1`, `v2`. * `vector.angle(v1, v2)`: * Returns the angle between `v1` and `v2` in radians. -* `vector.dot(v1, v2)` - * Returns the dot product of `v1` and `v2` -* `vector.cross(v1, v2)` - * Returns the cross product of `v1` and `v2` +* `vector.dot(v1, v2)`: + * Returns the dot product of `v1` and `v2`. +* `vector.cross(v1, v2)`: + * Returns the cross product of `v1` and `v2`. +* `vector.offset(v, x, y, z)`: + * Returns the sum of the vectors `v` and `{x = x, y = y, z = z}`. For the following functions `x` can be either a vector or a number: @@ -3056,10 +3149,12 @@ For the following functions `x` can be either a vector or a number: * Returns a vector. * If `x` is a vector: Returns the difference of `v` subtracted by `x`. * If `x` is a number: Subtracts `x` from each component of `v`. -* `vector.multiply(v, x)`: - * Returns a scaled vector or Schur product. -* `vector.divide(v, x)`: - * Returns a scaled vector or Schur quotient. +* `vector.multiply(v, s)`: + * Returns a scaled vector. + * Deprecated: If `s` is a vector: Returns the Schur product. +* `vector.divide(v, s)`: + * Returns a scaled vector. + * Deprecated: If `s` is a vector: Returns the Schur quotient. For the following functions `a` is an angle in radians and `r` is a rotation vector ({x = , y = , z = }) where pitch, yaw and roll are @@ -4252,6 +4347,8 @@ Utilities pathfinder_works = true, -- Whether Collision info is available to an objects' on_step (5.3.0) object_step_has_moveresult = true, + -- Whether get_velocity() and add_velocity() can be used on players (5.4.0) + direct_velocity_on_players = true, } * `minetest.has_feature(arg)`: returns `boolean, missing_features` @@ -4518,6 +4615,11 @@ Call these functions only at load time! * Called always when a player says something * Return `true` to mark the message as handled, which means that it will not be sent to other players. +* `minetest.register_on_chatcommand(function(name, command, params))` + * Called always when a chatcommand is triggered, before `minetest.registered_chatcommands` + is checked to see if the command exists, but after the input is parsed. + * Return `true` to mark the command as handled, which means that the default + handlers will be prevented. * `minetest.register_on_player_receive_fields(function(player, formname, fields))` * Called when the server received input from `player` in a formspec with the given `formname`. Specifically, this is called on any of the @@ -4720,6 +4822,22 @@ Environment access * `pos`: The position where to measure the light. * `timeofday`: `nil` for current time, `0` for night, `0.5` for day * Returns a number between `0` and `15` or `nil` + * `nil` is returned e.g. when the map isn't loaded at `pos` +* `minetest.get_natural_light(pos[, timeofday])` + * Figures out the sunlight (or moonlight) value at pos at the given time of + day. + * `pos`: The position of the node + * `timeofday`: `nil` for current time, `0` for night, `0.5` for day + * Returns a number between `0` and `15` or `nil` + * This function tests 203 nodes in the worst case, which happens very + unlikely +* `minetest.get_artificial_light(param1)` + * Calculates the artificial light (light from e.g. torches) value from the + `param1` value. + * `param1`: The param1 value of a `paramtype = "light"` node. + * Returns a number between `0` and `15` + * Currently it's the same as `math.floor(param1 / 16)`, except that it + ensures compatibility. * `minetest.place_node(pos, node)` * Place node with the same effects that a player would cause * `minetest.dig_node(pos)` @@ -5238,17 +5356,23 @@ Sounds * `minetest.sound_fade(handle, step, gain)` * `handle` is a handle returned by `minetest.sound_play` * `step` determines how fast a sound will fade. - Negative step will lower the sound volume, positive step will increase - the sound volume. + The gain will change by this much per second, + until it reaches the target gain. + Note: Older versions used a signed step. This is deprecated, but old + code will still work. (the client uses abs(step) to correct it) * `gain` the target gain for the fade. + Fading to zero will delete the sound. Timing ------ -* `minetest.after(time, func, ...)` +* `minetest.after(time, func, ...)` : returns job table to use as below. * Call the function `func` after `time` seconds, may be fractional * Optional: Variable number of arguments that are passed to `func` +* `job:cancel()` + * Cancels the job function from being called + Server ------ @@ -5712,6 +5836,9 @@ Global tables * Map of registered tool definitions, indexed by name * `minetest.registered_entities` * Map of registered entity prototypes, indexed by name + * Values in this table may be modified directly. + Note: changes to initial properties will only affect entities spawned afterwards, + as they are only read when spawning. * `minetest.object_refs` * Map of object references, indexed by active object id * `minetest.luaentities` @@ -5742,6 +5869,7 @@ Global tables * Map of registered chat command definitions, indexed by name * `minetest.registered_privileges` * Map of registered privilege definitions, indexed by name + * Registered privileges can be modified directly in this table. ### Registered callback tables @@ -5856,6 +5984,31 @@ An `InvRef` is a reference to an inventory. `minetest.get_inventory(location)`. * returns `{type="undefined"}` in case location is not known +### Callbacks + +Detached & nodemeta inventories provide the following callbacks for move actions: + +#### Before + +The `allow_*` callbacks return how many items can be moved. + +* `allow_move`/`allow_metadata_inventory_move`: Moving items in the inventory +* `allow_take`/`allow_metadata_inventory_take`: Taking items from the inventory +* `allow_put`/`allow_metadata_inventory_put`: Putting items to the inventory + +#### After + +The `on_*` callbacks are called after the items have been placed in the inventories. + +* `on_move`/`on_metadata_inventory_move`: Moving items in the inventory +* `on_take`/`on_metadata_inventory_take`: Taking items from the inventory +* `on_put`/`on_metadata_inventory_put`: Putting items to the inventory + +#### Swapping + +When a player tries to put an item to a place where another item is, the items are *swapped*. +This means that all callbacks will be called twice (once for each action). + `ItemStack` ----------- @@ -5881,6 +6034,18 @@ an itemstring, a table or `nil`. stack). * `set_metadata(metadata)`: (DEPRECATED) Returns true. * `get_description()`: returns the description shown in inventory list tooltips. + * The engine uses the same as this function for item descriptions. + * Fields for finding the description, in order: + * `description` in item metadata (See [Item Metadata].) + * `description` in item definition + * item name +* `get_short_description()`: returns the short description. + * Unlike the description, this does not include new lines. + * The engine uses the same as this function for short item descriptions. + * Fields for finding the short description, in order: + * `short_description` in item metadata (See [Item Metadata].) + * `short_description` in item definition + * first line of the description (See `get_description()`.) * `clear()`: removes all items from the stack, making it empty. * `replace(item)`: replace the contents of this stack. * `item` can also be an itemstring or table. @@ -6034,6 +6199,19 @@ object you are working with still exists. * `get_pos()`: returns `{x=num, y=num, z=num}` * `set_pos(pos)`: `pos`=`{x=num, y=num, z=num}` +* `get_velocity()`: returns the velocity, a vector. +* `add_velocity(vel)` + * `vel` is a vector, e.g. `{x=0.0, y=2.3, z=1.0}` + * In comparison to using get_velocity, adding the velocity and then using + set_velocity, add_velocity is supposed to avoid synchronization problems. + Additionally, players also do not support set_velocity. + * If a player: + * Does not apply during free_move. + * Note that since the player speed is normalized at each move step, + increasing e.g. Y velocity beyond what would usually be achieved + (see: physics overrides) will cause existing X/Z velocity to be reduced. + * Example: `add_velocity({x=0, y=6.5, z=0})` is equivalent to + pressing the jump key (assuming default settings) * `move_to(pos, continuous=false)` * Does an interpolated move for Lua entities for visually smooth transitions. * If `continuous` is true, the Lua entity will not be moved to the current @@ -6068,12 +6246,16 @@ object you are working with still exists. `frame_loop`. * `set_animation_frame_speed(frame_speed)` * `frame_speed`: number, default: `15.0` -* `set_attach(parent, bone, position, rotation)` +* `set_attach(parent, bone, position, rotation, forced_visible)` * `bone`: string * `position`: `{x=num, y=num, z=num}` (relative) * `rotation`: `{x=num, y=num, z=num}` = Rotation on each axis, in degrees -* `get_attach()`: returns parent, bone, position, rotation or nil if it isn't - attached. + * `forced_visible`: Boolean to control whether the attached entity + should appear in first person. +* `get_attach()`: returns parent, bone, position, rotation, forced_visible, + or nil if it isn't attached. +* `get_children()`: returns a list of ObjectRefs that are attached to the + object. * `set_detach()` * `set_bone_position(bone, position, rotation)` * `bone`: string @@ -6105,11 +6287,6 @@ object you are working with still exists. no effect and returning `nil`. * `set_velocity(vel)` * `vel` is a vector, e.g. `{x=0.0, y=2.3, z=1.0}` -* `add_velocity(vel)` - * `vel` is a vector, e.g. `{x=0.0, y=2.3, z=1.0}` - * In comparison to using get_velocity, adding the velocity and then using - set_velocity, add_velocity is supposed to avoid synchronization problems. -* `get_velocity()`: returns the velocity, a vector * `set_acceleration(acc)` * `acc` is a vector * `get_acceleration()`: returns the acceleration, a vector @@ -6117,35 +6294,37 @@ object you are working with still exists. * `rot` is a vector (radians). X is pitch (elevation), Y is yaw (heading) and Z is roll (bank). * `get_rotation()`: returns the rotation, a vector (radians) -* `set_yaw(radians)`: sets the yaw (heading). +* `set_yaw(yaw)`: sets the yaw in radians (heading). * `get_yaw()`: returns number in radians * `set_texture_mod(mod)` + * Set a texture modifier to the base texture, for sprites and meshes. + * When calling `set_texture_mod` again, the previous one is discarded. + * `mod` the texture modifier. See [Texture modifiers]. * `get_texture_mod()` returns current texture modifier -* `set_sprite(p, num_frames, framelength, select_horiz_by_yawpitch)` - * Select sprite from spritesheet with optional animation and Dungeon Master - style texture selection based on yaw relative to camera - * `p`: {x=number, y=number}, the coordinate of the first frame - (x: column, y: row), default: `{x=0, y=0}` - * `num_frames`: number, default: `1` - * `framelength`: number, default: `0.2` - * `select_horiz_by_yawpitch`: boolean, this was once used for the Dungeon - Master mob, default: `false` +* `set_sprite(start_frame, num_frames, framelength, select_x_by_camera)` + * Specifies and starts a sprite animation + * Animations iterate along the frame `y` position. + * `start_frame`: {x=column number, y=row number}, the coordinate of the + first frame, default: `{x=0, y=0}` + * `num_frames`: Total frames in the texture, default: `1` + * `framelength`: Time per animated frame in seconds, default: `0.2` + * `select_x_by_camera`: Only for visual = `sprite`. Changes the frame `x` + position according to the view direction. default: `false`. + * First column: subject facing the camera + * Second column: subject looking to the left + * Third column: subject backing the camera + * Fourth column: subject looking to the right + * Fifth column: subject viewed from above + * Sixth column: subject viewed from below * `get_entity_name()` (**Deprecated**: Will be removed in a future version) * `get_luaentity()` #### Player only (no-op for other objects) * `get_player_name()`: returns `""` if is not a player -* `get_player_velocity()`: returns `nil` if is not a player, otherwise a +* `get_player_velocity()`: **DEPRECATED**, use get_velocity() instead. table {x, y, z} representing the player's instantaneous velocity in nodes/s -* `add_player_velocity(vel)` - * Adds to player velocity, this happens client-side and only once. - * Does not apply during free_move. - * Note that since the player speed is normalized at each move step, - increasing e.g. Y velocity beyond what would usually be achieved - (see: physics overrides) will cause existing X/Z velocity to be reduced. - * Example: `add_player_velocity({x=0, y=6.5, z=0})` is equivalent to - pressing the jump key (assuming default settings) +* `add_player_velocity(vel)`: **DEPRECATED**, use add_velocity(vel) instead. * `get_look_dir()`: get camera direction as a unit vector * `get_look_vertical()`: pitch in radians * Angle ranges between -pi/2 and pi/2, which are straight up and down @@ -6207,15 +6386,23 @@ object you are working with still exists. * Only affects formspecs shown after this is called. * `get_formspec_prepend(formspec)`: returns a formspec string. * `get_player_control()`: returns table with player pressed keys - * The table consists of fields with boolean value representing the pressed - keys, the fields are jump, right, left, LMB, RMB, sneak, aux1, down, up, zoom. - * example: `{jump=false, right=true, left=false, LMB=false, RMB=false, - sneak=true, aux1=false, down=false, up=false, zoom=false}` - * The `zoom` field is available since 5.3 + * The table consists of fields with the following boolean values + representing the pressed keys: `up`, `down`, `left`, `right`, `jump`, + `aux1`, `sneak`, `dig`, `place`, `LMB`, `RMB`, and `zoom`. + * The fields `LMB` and `RMB` are equal to `dig` and `place` respectively, + and exist only to preserve backwards compatibility. * `get_player_control_bits()`: returns integer with bit packed player pressed - keys. - * bit nr/meaning: 0/up, 1/down, 2/left, 3/right, 4/jump, 5/aux1, 6/sneak, - 7/LMB, 8/RMB, 9/zoom (zoom available since 5.3) + keys. Bits: + * 0 - up + * 1 - down + * 2 - left + * 3 - right + * 4 - jump + * 5 - aux1 + * 6 - sneak + * 7 - dig + * 8 - place + * 9 - zoom * `set_physics_override(override_table)` * `override_table` is a table with the following fields: * `speed`: multiplier to default walking speed value (default: `1`) @@ -6260,8 +6447,29 @@ object you are working with still exists. * `hud_set_hotbar_selected_image(texturename)` * sets image for selected item of hotbar * `hud_get_hotbar_selected_image`: returns texturename -* `set_sky(parameters)` - * `parameters` is a table with the following optional fields: +* `set_minimap_modes({mode, mode, ...}, selected_mode)` + * Overrides the available minimap modes (and toggle order), and changes the + selected mode. + * `mode` is a table consisting of up to four fields: + * `type`: Available type: + * `off`: Minimap off + * `surface`: Minimap in surface mode + * `radar`: Minimap in radar mode + * `texture`: Texture to be displayed instead of terrain map + (texture is centered around 0,0 and can be scaled). + Texture size is limited to 512 x 512 pixel. + * `label`: Optional label to display on minimap mode toggle + The translation must be handled within the mod. + * `size`: Sidelength or diameter, in number of nodes, of the terrain + displayed in minimap + * `texture`: Only for texture type, name of the texture to display + * `scale`: Only for texture type, scale of the texture map in nodes per + pixel (for example a `scale` of 2 means each pixel represents a 2x2 + nodes square) + * `selected_mode` is the mode index to be selected after modes have been changed + (0 is the first mode). +* `set_sky(sky_parameters)` + * `sky_parameters` is a table with the following optional fields: * `base_color`: ColorSpec, changes fog in "skybox" and "plain". * `type`: Available types: * `"regular"`: Uses 0 textures, `base_color` ignored @@ -6305,8 +6513,8 @@ object you are working with still exists. * `get_sky()`: returns base_color, type, table of textures, clouds. * `get_sky_color()`: returns a table with the `sky_color` parameters as in `set_sky`. -* `set_sun(parameters)`: - * `parameters` is a table with the following optional fields: +* `set_sun(sun_parameters)`: + * `sun_parameters` is a table with the following optional fields: * `visible`: Boolean for whether the sun is visible. (default: `true`) * `texture`: A regular texture for the sun. Setting to `""` @@ -6320,8 +6528,8 @@ object you are working with still exists. * `scale`: Float controlling the overall size of the sun. (default: `1`) * `get_sun()`: returns a table with the current sun parameters as in `set_sun`. -* `set_moon(parameters)`: - * `parameters` is a table with the following optional fields: +* `set_moon(moon_parameters)`: + * `moon_parameters` is a table with the following optional fields: * `visible`: Boolean for whether the moon is visible. (default: `true`) * `texture`: A regular texture for the moon. Setting to `""` @@ -6331,8 +6539,8 @@ object you are working with still exists. * `scale`: Float controlling the overall size of the moon (default: `1`) * `get_moon()`: returns a table with the current moon parameters as in `set_moon`. -* `set_stars(parameters)`: - * `parameters` is a table with the following optional fields: +* `set_stars(star_parameters)`: + * `star_parameters` is a table with the following optional fields: * `visible`: Boolean for whether the stars are visible. (default: `true`) * `count`: Integer number to set the number of stars in @@ -6344,8 +6552,8 @@ object you are working with still exists. * `scale`: Float controlling the overall size of the stars (default: `1`) * `get_stars()`: returns a table with the current stars parameters as in `set_stars`. -* `set_clouds(parameters)`: set cloud parameters - * `parameters` is a table with the following optional fields: +* `set_clouds(cloud_parameters)`: set cloud parameters + * `cloud_parameters` is a table with the following optional fields: * `density`: from `0` (no clouds) to `1` (full clouds) (default `0.4`) * `color`: basic cloud color with alpha channel, ColorSpec (default `#fff0f0e5`). @@ -6363,21 +6571,17 @@ object you are working with still exists. amount. * `nil`: Disables override, defaulting to sunlight based on day-night cycle * `get_day_night_ratio()`: returns the ratio or nil if it isn't overridden -* `set_local_animation(stand/idle, walk, dig, walk+dig, frame_speed=frame_speed)`: - set animation for player model in third person view - - set_local_animation({x=0, y=79}, -- stand/idle animation key frames - {x=168, y=187}, -- walk animation key frames - {x=189, y=198}, -- dig animation key frames - {x=200, y=219}, -- walk+dig animation key frames - frame_speed=30) -- animation frame speed -* `get_local_animation()`: returns stand, walk, dig, dig+walk tables and +* `set_local_animation(idle, walk, dig, walk_while_dig, frame_speed)`: + set animation for player model in third person view. + * Every animation equals to a `{x=starting frame, y=ending frame}` table. + * `frame_speed` sets the animations frame speed. Default is 30. +* `get_local_animation()`: returns idle, walk, dig, walk_while_dig tables and `frame_speed`. -* `set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0})`: defines offset value for - camera per player. +* `set_eye_offset(firstperson, thirdperson)`: defines offset vectors for camera + per player. * in first person view * in third person view (max. values `{x=-10/10,y=-10,15,z=-5/5}`) -* `get_eye_offset()`: returns `offset_first` and `offset_third` +* `get_eye_offset()`: returns first and third person offsets. * `send_mapblock(blockpos)`: * Sends a server-side loaded mapblock to the player. * Returns `false` if failed. @@ -6749,6 +6953,10 @@ Player properties need to be saved manually. shaded = true, -- Setting this to 'false' disables diffuse lighting of entity + + show_on_minimap = false, + -- Defaults to true for players, false for other entities. + -- If set to true the entity will show as a marker on the minimap. } Entity definition @@ -6883,13 +7091,8 @@ Tile definition * `"image.png"` * `{name="image.png", animation={Tile Animation definition}}` -* `{name="image.png", backface_culling=bool, tileable_vertical=bool, - tileable_horizontal=bool, align_style="node"/"world"/"user", scale=int}` +* `{name="image.png", backface_culling=bool, align_style="node"/"world"/"user", scale=int}` * backface culling enabled by default for most nodes - * tileable flags are info for shaders, how they should treat texture - when displacement mapping is used. - Directions are from the point of view of the tile texture, - not the node it's on. * align style determines whether the texture will be rotated with the node or kept aligned with its surroundings. "user" means that client setting will be used, similar to `glasslike_framed_optional`. @@ -6942,6 +7145,13 @@ Used by `minetest.register_node`, `minetest.register_craftitem`, and { description = "Steel Axe", + -- Can contain new lines. "\n" has to be used as new line character. + -- See also: `get_description` in [`ItemStack`] + + short_description = "Steel Axe", + -- Must not contain new lines. + -- Defaults to the first line of description. + -- See also: `get_short_description` in [`ItemStack`] groups = {}, -- key = name, value = rating; rating = 1..3. @@ -6980,6 +7190,13 @@ Used by `minetest.register_node`, `minetest.register_craftitem`, and liquids_pointable = false, + light_source = 0, + -- When used for nodes: Defines amount of light emitted by node. + -- Otherwise: Defines texture glow when viewed as a dropped item + -- To set the maximum (14), use the value 'minetest.LIGHT_MAX'. + -- A value outside the range 0 to minetest.LIGHT_MAX causes undefined + -- behavior. + -- See "Tools" section for an example including explanation tool_capabilities = { full_punch_interval = 1.0, @@ -7109,6 +7326,8 @@ Used by `minetest.register_node`. use_texture_alpha = false, -- Use texture's alpha channel + -- If this is set to false, the node will be rendered fully opaque + -- regardless of any texture transparency. palette = "palette.png", -- The node's `param2` is used to select a pixel from the image. @@ -7177,12 +7396,6 @@ Used by `minetest.register_node`. drowning = 0, -- Player will take this amount of damage if no bubbles are left - light_source = 0, - -- Amount of light emitted by node. - -- To set the maximum (14), use the value 'minetest.LIGHT_MAX'. - -- A value outside the range 0 to minetest.LIGHT_MAX causes undefined - -- behavior. - damage_per_second = 0, -- If player is inside node, this damage is caused @@ -7261,10 +7474,13 @@ Used by `minetest.register_node`. -- Node was placed. Also played after falling place_failed = , - -- When node placement failed + -- When node placement failed. + -- Note: This happens if the _built-in_ node placement failed. + -- This sound will still be played if the node is placed in the + -- `on_place` callback manually. fall = , - -- When node starts to fall + -- When node starts to fall or is detached }, drop = "", @@ -7851,6 +8067,8 @@ Used by `minetest.register_chatcommand`. func = function(name, param), -- Called when command is run. Returns boolean success and text output. + -- Special case: The help message is shown to the player if `func` + -- returns false without a text output. } Note that in params, use of symbols is as follows: @@ -7933,7 +8151,8 @@ Used by `Player:hud_add`. Returned by `Player:hud_get`. { hud_elem_type = "image", -- See HUD element types - -- Type of element, can be "image", "text", "statbar", or "inventory" + -- Type of element, can be "image", "text", "statbar", "inventory", + -- "compass" or "minimap" position = {x=0.5, y=0.5}, -- Left corner position of element @@ -8101,6 +8320,66 @@ Used by `minetest.add_particlespawner`. -- Otherwise, the default behavior is used. (currently: any random tile) } +`HTTPRequest` definition +------------------------ + +Used by `HTTPApiTable.fetch` and `HTTPApiTable.fetch_async`. + + { + url = "http://example.org", + + timeout = 10, + -- Timeout for connection in seconds. Default is 3 seconds. + + method = "GET", "POST", "PUT" or "DELETE" + -- The http method to use. Defaults to "GET". + + data = "Raw request data string" OR {field1 = "data1", field2 = "data2"}, + -- Data for the POST, PUT or DELETE request. + -- Accepts both a string and a table. If a table is specified, encodes + -- table as x-www-form-urlencoded key-value pairs. + + user_agent = "ExampleUserAgent", + -- Optional, if specified replaces the default minetest user agent with + -- given string + + extra_headers = { "Accept-Language: en-us", "Accept-Charset: utf-8" }, + -- Optional, if specified adds additional headers to the HTTP request. + -- You must make sure that the header strings follow HTTP specification + -- ("Key: Value"). + + multipart = boolean + -- Optional, if true performs a multipart HTTP request. + -- Default is false. + -- Post only, data must be array + + post_data = "Raw POST request data string" OR {field1 = "data1", field2 = "data2"}, + -- Deprecated, use `data` instead. Forces `method = "POST"`. + } + +`HTTPRequestResult` definition +------------------------------ + +Passed to `HTTPApiTable.fetch` callback. Returned by +`HTTPApiTable.fetch_async_get`. + + { + completed = true, + -- If true, the request has finished (either succeeded, failed or timed + -- out) + + succeeded = true, + -- If true, the request was successful + + timeout = false, + -- If true, the request timed out + + code = 200, + -- HTTP status code + + data = "response" + } + Authentication handler definition --------------------------------- diff --git a/doc/texture_packs.txt b/doc/texture_packs.txt index e7a7dfd3c..8af2cbad6 100644 --- a/doc/texture_packs.txt +++ b/doc/texture_packs.txt @@ -90,6 +90,7 @@ by texture packs. All existing fallback textures can be found in the directory * `minimap_mask_square.png`: mask used for the square minimap * `minimap_overlay_round.png`: overlay texture for the round minimap * `minimap_overlay_square.png`: overlay texture for the square minimap +* `no_texture_airlike.png`: fallback inventory image for airlike nodes * `object_marker_red.png`: texture for players on the minimap * `player_marker.png`: texture for the own player on the square minimap @@ -194,11 +195,27 @@ Here are targets you can choose from: | bottom | y- face | | sides | x-, x+, z-, z+ faces | | all | All faces. You can also use '*' instead of 'all'. | +| special1 | The first entry in the special_tiles list | +| special2 | The second entry in the special_tiles list | +| special3 | The third entry in the special_tiles list | +| special4 | The fourth entry in the special_tiles list | +| special5 | The fifth entry in the special_tiles list | +| special6 | The sixth entry in the special_tiles list | | inventory | The inventory texture | | wield | The texture used when held by the player | Nodes support all targets, but other items only support 'inventory' -and 'wield' +and 'wield'. + +### Using the special targets + +The special* targets only apply to specific drawtypes: + +* `flowingliquid`: special1 sets the top texture, special2 sets the side texture +* `allfaces_optional`: special1 is used by simple mode, see below +* `glasslike_framed`: When containing a liquid, special1 sets the liquid texture +* `glasslike_framed_optional`: Same as `glasslike_framed` +* `plantlike_rooted`: special1 sets the plant's texture Designing leaves textures for the leaves rendering options ---------------------------------------------------------- diff --git a/fonts/Arimo-Regular-old.ttf b/fonts/Arimo-Regular-old.ttf deleted file mode 100644 index 9be443c7d..000000000 Binary files a/fonts/Arimo-Regular-old.ttf and /dev/null differ diff --git a/fonts/DroidSansFallbackFull-LICENSE.txt b/fonts/DroidSansFallbackFull-LICENSE.txt index d3a2eaa8c..a7bf409b7 100644 --- a/fonts/DroidSansFallbackFull-LICENSE.txt +++ b/fonts/DroidSansFallbackFull-LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (C) 2008 The Android Open Source Project +Copyright (C) 2012 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/fonts/DroidSansFallbackFull.ttf b/fonts/DroidSansFallbackFull.ttf index a9df00585..0cacabedc 100644 Binary files a/fonts/DroidSansFallbackFull.ttf and b/fonts/DroidSansFallbackFull.ttf differ diff --git a/games/devtest/README.md b/games/devtest/README.md index a7e93cf11..77e722af7 100644 --- a/games/devtest/README.md +++ b/games/devtest/README.md @@ -23,9 +23,8 @@ Basically, just create a world and start. A few important things to note: * Use the `/infplace` command to toggle infinite node placement in-game * Use the Param2 Tool to change the param2 of nodes; it's useful to experiment with the various drawtype test nodes * Check out the game settings and server commands for additional tests and features -* Creative Mode does nothing (apart from default engine behavior) -Confused by a certain node or item? Check out for inline code comments. +Confused by a certain node or item? Check out for inline code comments. The usages of most tools are explained in their tooltips. ### Example tests diff --git a/games/devtest/mods/basenodes/init.lua b/games/devtest/mods/basenodes/init.lua index 8156c4bec..0cb85d808 100644 --- a/games/devtest/mods/basenodes/init.lua +++ b/games/devtest/mods/basenodes/init.lua @@ -124,8 +124,10 @@ minetest.register_node("basenodes:pine_needles", { }) minetest.register_node("basenodes:water_source", { - description = "Water Source", + description = "Water Source".."\n".. + "Drowning damage: 1", drawtype = "liquid", + waving = 3, tiles = {"default_water.png"}, special_tiles = { {name = "default_water.png", backface_culling = false}, @@ -148,8 +150,10 @@ minetest.register_node("basenodes:water_source", { }) minetest.register_node("basenodes:water_flowing", { - description = "Flowing Water", + description = "Flowing Water".."\n".. + "Drowning damage: 1", drawtype = "flowingliquid", + waving = 3, tiles = {"default_water_flowing.png"}, special_tiles = { {name = "default_water_flowing.png", backface_culling = false}, @@ -173,8 +177,10 @@ minetest.register_node("basenodes:water_flowing", { }) minetest.register_node("basenodes:river_water_source", { - description = "River Water Source", + description = "River Water Source".."\n".. + "Drowning damage: 1", drawtype = "liquid", + waving = 3, tiles = { "default_river_water.png" }, special_tiles = { {name = "default_river_water.png", backface_culling = false}, @@ -199,8 +205,10 @@ minetest.register_node("basenodes:river_water_source", { }) minetest.register_node("basenodes:river_water_flowing", { - description = "Flowing River Water", + description = "Flowing River Water".."\n".. + "Drowning damage: 1", drawtype = "flowingliquid", + waving = 3, tiles = {"default_river_water_flowing.png"}, special_tiles = { {name = "default_river_water_flowing.png", backface_culling = false}, @@ -226,7 +234,9 @@ minetest.register_node("basenodes:river_water_flowing", { }) minetest.register_node("basenodes:lava_flowing", { - description = "Flowing Lava", + description = "Flowing Lava".."\n".. + "4 damage per second".."\n".. + "Drowning damage: 1", drawtype = "flowingliquid", tiles = {"default_lava_flowing.png"}, special_tiles = { @@ -251,7 +261,9 @@ minetest.register_node("basenodes:lava_flowing", { }) minetest.register_node("basenodes:lava_source", { - description = "Lava Source", + description = "Lava Source".."\n".. + "4 damage per second".."\n".. + "Drowning damage: 1", drawtype = "liquid", tiles = { "default_lava.png" }, special_tiles = { @@ -290,7 +302,8 @@ minetest.register_node("basenodes:mossycobble", { }) minetest.register_node("basenodes:apple", { - description = "Apple", + description = "Apple".."\n".. + "Food (+2)", drawtype = "plantlike", tiles ={"default_apple.png"}, inventory_image = "default_apple.png", diff --git a/games/devtest/mods/basenodes/textures/default_grass_side.png b/games/devtest/mods/basenodes/textures/default_grass_side.png deleted file mode 100644 index 04770b6f6..000000000 Binary files a/games/devtest/mods/basenodes/textures/default_grass_side.png and /dev/null differ diff --git a/games/devtest/mods/basetools/init.lua b/games/devtest/mods/basetools/init.lua index c5b4cd76c..bd7480030 100644 --- a/games/devtest/mods/basetools/init.lua +++ b/games/devtest/mods/basetools/init.lua @@ -6,7 +6,7 @@ Tool types: -* Hand: basic tool/weapon (just for convenience, not optimized for testing) +* Hand: basic tool/weapon (special capabilities in creative mode) * Pickaxe: dig cracky * Axe: dig choppy * Shovel: dig crumbly @@ -24,25 +24,54 @@ Tool materials: ]] -- The hand -minetest.register_item(":", { - type = "none", - wield_image = "wieldhand.png", - wield_scale = {x=1,y=1,z=2.5}, - tool_capabilities = { - full_punch_interval = 1.0, - max_drop_level = 0, - groupcaps = { - crumbly = {times={[3]=1.50}, uses=0, maxlevel=0}, - snappy = {times={[3]=1.50}, uses=0, maxlevel=0}, - oddly_breakable_by_hand = {times={[1]=7.00,[2]=4.00,[3]=2.00}, uses=0, maxlevel=0}, - }, - damage_groups = {fleshy=1}, - } -}) +if minetest.settings:get_bool("creative_mode") then + local digtime = 42 + local caps = {times = {digtime, digtime, digtime}, uses = 0, maxlevel = 256} + + minetest.register_item(":", { + type = "none", + wield_image = "wieldhand.png", + wield_scale = {x = 1, y = 1, z = 2.5}, + range = 10, + tool_capabilities = { + full_punch_interval = 0.5, + max_drop_level = 3, + groupcaps = { + crumbly = caps, + cracky = caps, + snappy = caps, + choppy = caps, + oddly_breakable_by_hand = caps, + -- dig_immediate group doesn't use value 1. Value 3 is instant dig + dig_immediate = + {times = {[2] = digtime, [3] = 0}, uses = 0, maxlevel = 256}, + }, + damage_groups = {fleshy = 10}, + } + }) +else + minetest.register_item(":", { + type = "none", + wield_image = "wieldhand.png", + wield_scale = {x = 1, y = 1, z = 2.5}, + tool_capabilities = { + full_punch_interval = 0.9, + max_drop_level = 0, + groupcaps = { + crumbly = {times = {[2] = 3.00, [3] = 0.70}, uses = 0, maxlevel = 1}, + snappy = {times = {[3] = 0.40}, uses = 0, maxlevel = 1}, + oddly_breakable_by_hand = + {times = {[1] = 3.50, [2] = 2.00, [3] = 0.70}, uses = 0} + }, + damage_groups = {fleshy = 1}, + } + }) +end -- Mese Pickaxe: special tool that digs "everything" instantly minetest.register_tool("basetools:pick_mese", { - description = "Mese Pickaxe", + description = "Mese Pickaxe".."\n".. + "Digs diggable nodes instantly", inventory_image = "basetools_mesepick.png", tool_capabilities = { full_punch_interval = 1.0, @@ -65,7 +94,9 @@ minetest.register_tool("basetools:pick_mese", { -- This should break after only 1 use minetest.register_tool("basetools:pick_dirt", { - description = "Dirt Pickaxe", + description = "Dirt Pickaxe".."\n".. + "Digs cracky=3".."\n".. + "1 use only", inventory_image = "basetools_dirtpick.png", tool_capabilities = { max_drop_level=0, @@ -76,7 +107,8 @@ minetest.register_tool("basetools:pick_dirt", { }) minetest.register_tool("basetools:pick_wood", { - description = "Wooden Pickaxe", + description = "Wooden Pickaxe".."\n".. + "Digs cracky=3", inventory_image = "basetools_woodpick.png", tool_capabilities = { max_drop_level=0, @@ -86,7 +118,8 @@ minetest.register_tool("basetools:pick_wood", { }, }) minetest.register_tool("basetools:pick_stone", { - description = "Stone Pickaxe", + description = "Stone Pickaxe".."\n".. + "Digs cracky=2..3", inventory_image = "basetools_stonepick.png", tool_capabilities = { max_drop_level=0, @@ -96,7 +129,8 @@ minetest.register_tool("basetools:pick_stone", { }, }) minetest.register_tool("basetools:pick_steel", { - description = "Steel Pickaxe", + description = "Steel Pickaxe".."\n".. + "Digs cracky=1..3", inventory_image = "basetools_steelpick.png", tool_capabilities = { max_drop_level=1, @@ -106,7 +140,9 @@ minetest.register_tool("basetools:pick_steel", { }, }) minetest.register_tool("basetools:pick_steel_l1", { - description = "Steel Pickaxe Level 1", + description = "Steel Pickaxe Level 1".."\n".. + "Digs cracky=1..3".."\n".. + "maxlevel=1", inventory_image = "basetools_steelpick_l1.png", tool_capabilities = { max_drop_level=1, @@ -116,7 +152,9 @@ minetest.register_tool("basetools:pick_steel_l1", { }, }) minetest.register_tool("basetools:pick_steel_l2", { - description = "Steel Pickaxe Level 2", + description = "Steel Pickaxe Level 2".."\n".. + "Digs cracky=1..3".."\n".. + "maxlevel=2", inventory_image = "basetools_steelpick_l2.png", tool_capabilities = { max_drop_level=1, @@ -131,7 +169,8 @@ minetest.register_tool("basetools:pick_steel_l2", { -- minetest.register_tool("basetools:shovel_wood", { - description = "Wooden Shovel", + description = "Wooden Shovel".."\n".. + "Digs crumbly=3", inventory_image = "basetools_woodshovel.png", tool_capabilities = { max_drop_level=0, @@ -141,7 +180,8 @@ minetest.register_tool("basetools:shovel_wood", { }, }) minetest.register_tool("basetools:shovel_stone", { - description = "Stone Shovel", + description = "Stone Shovel".."\n".. + "Digs crumbly=2..3", inventory_image = "basetools_stoneshovel.png", tool_capabilities = { max_drop_level=0, @@ -151,7 +191,8 @@ minetest.register_tool("basetools:shovel_stone", { }, }) minetest.register_tool("basetools:shovel_steel", { - description = "Steel Shovel", + description = "Steel Shovel".."\n".. + "Digs crumbly=1..3", inventory_image = "basetools_steelshovel.png", tool_capabilities = { max_drop_level=1, @@ -166,7 +207,8 @@ minetest.register_tool("basetools:shovel_steel", { -- minetest.register_tool("basetools:axe_wood", { - description = "Wooden Axe", + description = "Wooden Axe".."\n".. + "Digs choppy=3", inventory_image = "basetools_woodaxe.png", tool_capabilities = { max_drop_level=0, @@ -176,7 +218,8 @@ minetest.register_tool("basetools:axe_wood", { }, }) minetest.register_tool("basetools:axe_stone", { - description = "Stone Axe", + description = "Stone Axe".."\n".. + "Digs choppy=2..3", inventory_image = "basetools_stoneaxe.png", tool_capabilities = { max_drop_level=0, @@ -186,7 +229,8 @@ minetest.register_tool("basetools:axe_stone", { }, }) minetest.register_tool("basetools:axe_steel", { - description = "Steel Axe", + description = "Steel Axe".."\n".. + "Digs choppy=1..3", inventory_image = "basetools_steelaxe.png", tool_capabilities = { max_drop_level=1, @@ -201,7 +245,8 @@ minetest.register_tool("basetools:axe_steel", { -- minetest.register_tool("basetools:shears_wood", { - description = "Wooden Shears", + description = "Wooden Shears".."\n".. + "Digs snappy=3", inventory_image = "basetools_woodshears.png", tool_capabilities = { max_drop_level=0, @@ -211,7 +256,8 @@ minetest.register_tool("basetools:shears_wood", { }, }) minetest.register_tool("basetools:shears_stone", { - description = "Stone Shears", + description = "Stone Shears".."\n".. + "Digs snappy=2..3", inventory_image = "basetools_stoneshears.png", tool_capabilities = { max_drop_level=0, @@ -221,7 +267,8 @@ minetest.register_tool("basetools:shears_stone", { }, }) minetest.register_tool("basetools:shears_steel", { - description = "Steel Shears", + description = "Steel Shears".."\n".. + "Digs snappy=1..3", inventory_image = "basetools_steelshears.png", tool_capabilities = { max_drop_level=1, @@ -236,7 +283,8 @@ minetest.register_tool("basetools:shears_steel", { -- minetest.register_tool("basetools:sword_wood", { - description = "Wooden Sword", + description = "Wooden Sword".."\n".. + "Damage: fleshy=2", inventory_image = "basetools_woodsword.png", tool_capabilities = { full_punch_interval = 1.0, @@ -244,7 +292,8 @@ minetest.register_tool("basetools:sword_wood", { } }) minetest.register_tool("basetools:sword_stone", { - description = "Stone Sword", + description = "Stone Sword".."\n".. + "Damage: fleshy=4", inventory_image = "basetools_stonesword.png", tool_capabilities = { full_punch_interval = 1.0, @@ -253,7 +302,8 @@ minetest.register_tool("basetools:sword_stone", { } }) minetest.register_tool("basetools:sword_steel", { - description = "Steel Sword", + description = "Steel Sword".."\n".. + "Damage: fleshy=6", inventory_image = "basetools_steelsword.png", tool_capabilities = { full_punch_interval = 1.0, @@ -264,7 +314,8 @@ minetest.register_tool("basetools:sword_steel", { -- Fire/Ice sword: Deal damage to non-fleshy damage groups minetest.register_tool("basetools:sword_fire", { - description = "Fire Sword", + description = "Fire Sword".."\n".. + "Damage: icy=6", inventory_image = "basetools_firesword.png", tool_capabilities = { full_punch_interval = 1.0, @@ -273,12 +324,13 @@ minetest.register_tool("basetools:sword_fire", { } }) minetest.register_tool("basetools:sword_ice", { - description = "Ice Sword", + description = "Ice Sword".."\n".. + "Damage: fiery=6", inventory_image = "basetools_icesword.png", tool_capabilities = { full_punch_interval = 1.0, max_drop_level=0, - damage_groups = {firy=6}, + damage_groups = {fiery=6}, } }) @@ -286,7 +338,9 @@ minetest.register_tool("basetools:sword_ice", { -- Dagger: Low damage, fast punch interval -- minetest.register_tool("basetools:dagger_steel", { - description = "Steel Dagger", + description = "Steel Dagger".."\n".. + "Damage: fleshy=2".."\n".. + "Full Punch Interval: 0.5s", inventory_image = "basetools_steeldagger.png", tool_capabilities = { full_punch_interval = 0.5, diff --git a/games/devtest/mods/bucket/init.lua b/games/devtest/mods/bucket/init.lua index 3189d4aa6..ff58b0669 100644 --- a/games/devtest/mods/bucket/init.lua +++ b/games/devtest/mods/bucket/init.lua @@ -1,7 +1,8 @@ -- Bucket: Punch liquid source or flowing liquid to collect it minetest.register_tool("bucket:bucket", { - description = "Bucket", + description = "Bucket".."\n".. + "Picks up liquid nodes", inventory_image = "bucket.png", stack_max = 1, liquids_pointable = true, diff --git a/games/devtest/mods/chest/init.lua b/games/devtest/mods/chest/init.lua index c44522cb9..fc92bfdd1 100644 --- a/games/devtest/mods/chest/init.lua +++ b/games/devtest/mods/chest/init.lua @@ -1,5 +1,6 @@ minetest.register_node("chest:chest", { - description = "Chest", + description = "Chest" .. "\n" .. + "32 inventory slots", tiles ={"chest_chest.png^[sheet:2x2:0,0", "chest_chest.png^[sheet:2x2:0,0", "chest_chest.png^[sheet:2x2:1,0", "chest_chest.png^[sheet:2x2:1,0", "chest_chest.png^[sheet:2x2:1,0", "chest_chest.png^[sheet:2x2:0,1"}, diff --git a/games/devtest/mods/chest_of_everything/init.lua b/games/devtest/mods/chest_of_everything/init.lua index 7d61abebf..3e9d2678a 100644 --- a/games/devtest/mods/chest_of_everything/init.lua +++ b/games/devtest/mods/chest_of_everything/init.lua @@ -43,7 +43,8 @@ local function get_chest_formspec(page) end minetest.register_node("chest_of_everything:chest", { - description = "Chest of Everything", + description = "Chest of Everything" .. "\n" .. + "Grants access to all items", tiles ={"chest_of_everything_chest.png^[sheet:2x2:0,0", "chest_of_everything_chest.png^[sheet:2x2:0,0", "chest_of_everything_chest.png^[sheet:2x2:1,0", "chest_of_everything_chest.png^[sheet:2x2:1,0", "chest_of_everything_chest.png^[sheet:2x2:1,0", "chest_of_everything_chest.png^[sheet:2x2:0,1"}, diff --git a/games/devtest/mods/experimental/commands.lua b/games/devtest/mods/experimental/commands.lua index 158e5039d..132b08b0b 100644 --- a/games/devtest/mods/experimental/commands.lua +++ b/games/devtest/mods/experimental/commands.lua @@ -214,3 +214,6 @@ minetest.register_chatcommand("test_place_nodes", { end, }) +core.register_on_chatcommand(function(name, command, params) + minetest.log("caught command '"..command.."', issued by '"..name.."'. Parameters: '"..params.."'") +end) diff --git a/games/devtest/mods/experimental/items.lua b/games/devtest/mods/experimental/items.lua index 51b063ba2..94be71cf7 100644 --- a/games/devtest/mods/experimental/items.lua +++ b/games/devtest/mods/experimental/items.lua @@ -44,7 +44,8 @@ minetest.register_node("experimental:callback_node", { }) minetest.register_tool("experimental:privatizer", { - description = "Node Meta Privatizer", + description = "Node Meta Privatizer".."\n".. + "Punch: Marks 'infotext' and 'formspec' meta fields of chest as private", inventory_image = "experimental_tester_tool_1.png", groups = { testtool = 1, disable_repair = 1 }, on_use = function(itemstack, user, pointed_thing) @@ -67,7 +68,8 @@ minetest.register_tool("experimental:privatizer", { }) minetest.register_tool("experimental:particle_spawner", { - description = "Particle Spawner", + description = "Particle Spawner".."\n".. + "Punch: Spawn random test particle", inventory_image = "experimental_tester_tool_1.png^[invert:g", groups = { testtool = 1, disable_repair = 1 }, on_use = function(itemstack, user, pointed_thing) diff --git a/games/devtest/mods/soundstuff/init.lua b/games/devtest/mods/soundstuff/init.lua index 22012ba14..40ad8f562 100644 --- a/games/devtest/mods/soundstuff/init.lua +++ b/games/devtest/mods/soundstuff/init.lua @@ -107,7 +107,8 @@ minetest.register_node("soundstuff:footstep_climbable", { minetest.register_craftitem("soundstuff:eat", { - description = "Eat Sound Item", + description = "Eat Sound Item".."\n".. + "Makes a sound when 'eaten' (with punch key)", inventory_image = "soundstuff_eat.png", on_use = minetest.item_eat(0), sound = { @@ -116,7 +117,9 @@ minetest.register_craftitem("soundstuff:eat", { }) minetest.register_tool("soundstuff:breaks", { - description = "Break Sound Tool", + description = "Break Sound Tool".."\n".. + "Digs cracky=3 and more".."\n".. + "Makes a sound when it breaks", inventory_image = "soundstuff_node_dug.png", sound = { breaks = { name = "soundstuff_mono", gain = 1.0 }, diff --git a/games/devtest/mods/testentities/armor.lua b/games/devtest/mods/testentities/armor.lua index 4c30cec8d..306953d50 100644 --- a/games/devtest/mods/testentities/armor.lua +++ b/games/devtest/mods/testentities/armor.lua @@ -3,7 +3,7 @@ local phasearmor = { [0]={icy=100}, - [1]={firy=100}, + [1]={fiery=100}, [2]={fleshy=100}, [3]={immortal=1}, [4]={punch_operable=1}, diff --git a/games/devtest/mods/testentities/visuals.lua b/games/devtest/mods/testentities/visuals.lua index 83f361f16..8848ba49f 100644 --- a/games/devtest/mods/testentities/visuals.lua +++ b/games/devtest/mods/testentities/visuals.lua @@ -68,7 +68,7 @@ minetest.register_entity("testentities:mesh_unshaded", { -- Advanced visual tests --- A test entity for testing animated and yaw-modulated sprites +-- An entity for testing animated and yaw-modulated sprites minetest.register_entity("testentities:yawsprite", { initial_properties = { selectionbox = {-0.3, -0.5, -0.3, 0.3, 0.3, 0.3}, @@ -79,6 +79,18 @@ minetest.register_entity("testentities:yawsprite", { initial_sprite_basepos = {x=0, y=0}, }, on_activate = function(self, staticdata) - self.object:set_sprite({x=0, y=0}, 1, 0, true) + self.object:set_sprite({x=0, y=0}, 3, 0.5, true) + end, +}) + +-- An entity for testing animated upright sprites +minetest.register_entity("testentities:upright_animated", { + initial_properties = { + visual = "upright_sprite", + textures = {"testnodes_anim.png"}, + spritediv = {x = 1, y = 4}, + }, + on_activate = function(self) + self.object:set_sprite({x=0, y=0}, 4, 1.0, false) end, }) diff --git a/games/devtest/mods/testformspec/LICENSE.txt b/games/devtest/mods/testformspec/LICENSE.txt new file mode 100644 index 000000000..07696cc30 --- /dev/null +++ b/games/devtest/mods/testformspec/LICENSE.txt @@ -0,0 +1,14 @@ +License of media files +---------------------- +Content imported from minetest_game. + + +BlockMen (CC BY-SA 3.0) + default_chest_front.png + default_chest_lock.png + default_chest_side.png + default_chest_top.png + +stujones11 (CC BY-SA 3.0) +An0n3m0us (CC BY-SA 3.0) + testformspec_character.b3d diff --git a/games/devtest/mods/testformspec/formspec.lua b/games/devtest/mods/testformspec/formspec.lua index 08c1b6dc0..5495896ce 100644 --- a/games/devtest/mods/testformspec/formspec.lua +++ b/games/devtest/mods/testformspec/formspec.lua @@ -164,7 +164,7 @@ local style_fs = [[ style[one_btn14:hovered+pressed;textcolor=purple] image_button[0,9.6;1,1;testformspec_button_image.png;one_btn14;Bg] - style[one_btn15;border=false;bgimg=testformspec_bg.png;bgimg_hovered=testformspec_bg_hovered.png;bgimg_pressed=testformspec_bg_pressed.png] + style[one_btn15;border=false;bgcolor=#1cc;bgimg=testformspec_bg.png;bgimg_hovered=testformspec_bg_hovered.png;bgimg_pressed=testformspec_bg_pressed.png] item_image_button[1.25,9.6;1,1;testformspec:item;one_btn15;Bg] style[one_btn16;border=false;bgimg=testformspec_bg_9slice.png;bgimg_hovered=testformspec_bg_9slice_hovered.png;bgimg_pressed=testformspec_bg_9slice_pressed.png;bgimg_middle=4,6] @@ -327,6 +327,10 @@ Number] animated_image[3,4.25;1,1;;testformspec_animation.png;4;0;3] animated_image[5.5,0.5;5,2;;testformspec_animation.png;4;100] animated_image[5.5,2.75;5,2;;testformspec_animation.jpg;4;100] + + style[m1;bgcolor=black] + model[0.5,6;4,4;m1;testformspec_character.b3d;testformspec_character.png] + model[5,6;4,4;m2;testformspec_chest.obj;default_chest_top.png,default_chest_top.png,default_chest_side.png,default_chest_side.png,default_chest_front.png,default_chest_inside.png;30,1;true;true] ]], -- Scroll containers diff --git a/games/devtest/mods/testformspec/models/testformspec_character.b3d b/games/devtest/mods/testformspec/models/testformspec_character.b3d new file mode 100644 index 000000000..8edbaf637 Binary files /dev/null and b/games/devtest/mods/testformspec/models/testformspec_character.b3d differ diff --git a/games/devtest/mods/testformspec/models/testformspec_chest.obj b/games/devtest/mods/testformspec/models/testformspec_chest.obj new file mode 100644 index 000000000..72ba175a0 --- /dev/null +++ b/games/devtest/mods/testformspec/models/testformspec_chest.obj @@ -0,0 +1,79 @@ +# Blender v2.78 (sub 0) OBJ File: 'chest-open.blend' +# www.blender.org +o Top_Cube.002_None_Top_Cube.002_None_bottom +v -0.500000 0.408471 0.720970 +v -0.500000 1.115578 0.013863 +v -0.500000 0.894607 -0.207108 +v -0.500000 0.187501 0.499999 +v 0.500000 1.115578 0.013863 +v 0.500000 0.408471 0.720970 +v 0.500000 0.187501 0.499999 +v 0.500000 0.894607 -0.207108 +v -0.500000 0.187500 -0.500000 +v -0.500000 -0.500000 -0.500000 +v -0.500000 -0.500000 0.500000 +v 0.500000 0.187500 -0.500000 +v 0.500000 -0.500000 0.500000 +v 0.500000 -0.500000 -0.500000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 0.0000 1.0000 +vt 1.0000 1.0000 +vt 1.0000 0.6875 +vt 0.0000 0.6875 +vt 1.0000 1.0000 +vt 0.0000 0.6875 +vt 1.0000 0.6875 +vt 1.0000 0.6875 +vt 1.0000 0.0000 +vt 0.0000 0.0000 +vt 1.0000 0.6875 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 1.0000 0.6875 +vt 1.0000 0.0000 +vt 0.0000 1.0000 +vt 0.0000 0.6875 +vt 0.0000 0.6875 +vt 0.0000 0.0000 +vt 1.0000 0.5000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.5000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vn 0.0000 0.7071 0.7071 +vn -0.0000 -1.0000 -0.0000 +vn -1.0000 0.0000 0.0000 +vn 1.0000 0.0000 -0.0000 +vn 0.0000 -0.7071 0.7071 +vn 0.0000 0.0000 1.0000 +vn -0.0000 0.7071 -0.7071 +vn -0.0000 0.0000 -1.0000 +vn -0.0000 -0.7071 -0.7071 +vn -0.0000 1.0000 -0.0000 +g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Top +s off +f 6/1/1 5/2/1 2/3/1 1/4/1 +g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Bottom +f 11/5/2 10/6/2 14/7/2 13/8/2 +g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Right-Left +f 1/9/3 2/10/3 3/11/3 4/12/3 +f 5/13/4 6/1/4 7/14/4 8/15/4 +f 4/12/3 9/16/3 10/17/3 11/18/3 +f 12/19/4 7/14/4 13/8/4 14/20/4 +g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Back +f 6/21/5 1/9/5 4/12/5 7/22/5 +f 7/22/6 4/12/6 11/18/6 13/23/6 +g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Front +f 2/10/7 5/24/7 8/25/7 3/11/7 +f 9/16/8 12/26/8 14/27/8 10/17/8 +g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Inside +f 4/28/9 3/29/9 8/30/9 7/31/9 +f 7/31/10 12/32/10 9/33/10 4/28/10 diff --git a/games/devtest/mods/testformspec/textures/default_chest_front.png b/games/devtest/mods/testformspec/textures/default_chest_front.png new file mode 100644 index 000000000..85227d8fd Binary files /dev/null and b/games/devtest/mods/testformspec/textures/default_chest_front.png differ diff --git a/games/devtest/mods/testformspec/textures/default_chest_inside.png b/games/devtest/mods/testformspec/textures/default_chest_inside.png new file mode 100644 index 000000000..5f7b6b132 Binary files /dev/null and b/games/devtest/mods/testformspec/textures/default_chest_inside.png differ diff --git a/games/devtest/mods/testformspec/textures/default_chest_side.png b/games/devtest/mods/testformspec/textures/default_chest_side.png new file mode 100644 index 000000000..44a65a43d Binary files /dev/null and b/games/devtest/mods/testformspec/textures/default_chest_side.png differ diff --git a/games/devtest/mods/testformspec/textures/default_chest_top.png b/games/devtest/mods/testformspec/textures/default_chest_top.png new file mode 100644 index 000000000..f4a92ee07 Binary files /dev/null and b/games/devtest/mods/testformspec/textures/default_chest_top.png differ diff --git a/games/devtest/mods/testformspec/textures/testformspec_character.png b/games/devtest/mods/testformspec/textures/testformspec_character.png new file mode 100644 index 000000000..05021781e Binary files /dev/null and b/games/devtest/mods/testformspec/textures/testformspec_character.png differ diff --git a/games/devtest/mods/testnodes/drawtypes.lua b/games/devtest/mods/testnodes/drawtypes.lua index 6bf57fa37..b3ab2b322 100644 --- a/games/devtest/mods/testnodes/drawtypes.lua +++ b/games/devtest/mods/testnodes/drawtypes.lua @@ -145,6 +145,23 @@ minetest.register_node("testnodes:fencelike", { }) minetest.register_node("testnodes:torchlike", { + description = S("Torchlike Drawtype Test Node"), + drawtype = "torchlike", + paramtype = "light", + tiles = { + "testnodes_torchlike_floor.png", + "testnodes_torchlike_ceiling.png", + "testnodes_torchlike_wall.png", + }, + + + walkable = false, + sunlight_propagates = true, + groups = { dig_immediate = 3 }, + inventory_image = fallback_image("testnodes_torchlike_floor.png"), +}) + +minetest.register_node("testnodes:torchlike_wallmounted", { description = S("Wallmounted Torchlike Drawtype Test Node"), drawtype = "torchlike", paramtype = "light", @@ -162,6 +179,8 @@ minetest.register_node("testnodes:torchlike", { inventory_image = fallback_image("testnodes_torchlike_floor.png"), }) + + minetest.register_node("testnodes:signlike", { description = S("Wallmounted Signlike Drawtype Test Node"), drawtype = "signlike", @@ -514,10 +533,19 @@ local scale = function(subname, desc_double, desc_half) minetest.register_node("testnodes:"..subname.."_half", def) end +scale("allfaces", + S("Double-sized Allfaces Drawtype Test Node"), + S("Half-sized Allfaces Drawtype Test Node")) +scale("allfaces_optional", + S("Double-sized Allfaces Optional Drawtype Test Node"), + S("Half-sized Allfaces Optional Drawtype Test Node")) +scale("allfaces_optional_waving", + S("Double-sized Waving Allfaces Optional Drawtype Test Node"), + S("Half-sized Waving Allfaces Optional Drawtype Test Node")) scale("plantlike", S("Double-sized Plantlike Drawtype Test Node"), S("Half-sized Plantlike Drawtype Test Node")) -scale("torchlike", +scale("torchlike_wallmounted", S("Double-sized Wallmounted Torchlike Drawtype Test Node"), S("Half-sized Wallmounted Torchlike Drawtype Test Node")) scale("signlike", diff --git a/games/devtest/mods/testnodes/light.lua b/games/devtest/mods/testnodes/light.lua index 94409e83f..8ab4416d9 100644 --- a/games/devtest/mods/testnodes/light.lua +++ b/games/devtest/mods/testnodes/light.lua @@ -22,7 +22,8 @@ end -- Lets light through, but not sunlight, leading to a -- reduction in light level when light passes through minetest.register_node("testnodes:sunlight_filter", { - description = S("Sunlight Filter"), + description = S("Sunlight Filter") .."\n".. + S("Lets light through, but weakens sunlight"), paramtype = "light", @@ -35,7 +36,8 @@ minetest.register_node("testnodes:sunlight_filter", { -- Lets light and sunlight through without obstruction minetest.register_node("testnodes:sunlight_propagator", { - description = S("Sunlight Propagator"), + description = S("Sunlight Propagator") .."\n".. + S("Lets all light through"), paramtype = "light", sunlight_propagates = true, diff --git a/games/devtest/mods/testnodes/mod.conf b/games/devtest/mods/testnodes/mod.conf index 4824c6ed0..d894c3452 100644 --- a/games/devtest/mods/testnodes/mod.conf +++ b/games/devtest/mods/testnodes/mod.conf @@ -1,2 +1,3 @@ name = testnodes description = Contains a bunch of basic example nodes for demonstrative purposes, development and testing +depends = stairs diff --git a/games/devtest/mods/testnodes/textures.lua b/games/devtest/mods/testnodes/textures.lua index 6ffef8fe9..e0724c229 100644 --- a/games/devtest/mods/testnodes/textures.lua +++ b/games/devtest/mods/testnodes/textures.lua @@ -71,3 +71,52 @@ for a=1,#alphas do groups = { dig_immediate = 3 }, }) end + + +-- Bumpmapping and Parallax Occlusion + +-- This node has a normal map which corresponds to a pyramid with sides tilted +-- by an angle of 45°, i.e. the normal map contains four vectors which point +-- diagonally away from the surface (e.g. (0.7, 0.7, 0)), +-- and the heights in the height map linearly increase towards the centre, +-- so that the surface corresponds to a simple pyramid. +-- The node can help to determine if e.g. tangent space transformations work +-- correctly. +-- If, for example, the light comes from above, then the (tilted) pyramids +-- should look like they're lit from this light direction on all node faces. +-- The white albedo texture has small black indicators which can be used to see +-- how it is transformed ingame (and thus see if there's rotation around the +-- normal vector). +minetest.register_node("testnodes:height_pyramid", { + description = "Bumpmapping and Parallax Occlusion Tester (height pyramid)", + tiles = {"testnodes_height_pyramid.png"}, + groups = {dig_immediate = 3}, +}) + +-- The stairs nodes should help to validate if shading works correctly for +-- rotated nodes (which have rotated textures). +stairs.register_stair_and_slab("height_pyramid", "experimantal:height_pyramid", + {dig_immediate = 3}, + {"testnodes_height_pyramid.png"}, + "Bumpmapping and Parallax Occlusion Tester Stair (height pyramid)", + "Bumpmapping and Parallax Occlusion Tester Slab (height pyramid)") + +-- This node has a simple heightmap for parallax occlusion testing and flat +-- normalmap. +-- When parallax occlusion is enabled, the yellow scrawl should stick out of +-- the texture when viewed at an angle. +minetest.register_node("testnodes:parallax_extruded", { + description = "Parallax Occlusion Tester", + tiles = {"testnodes_parallax_extruded.png"}, + groups = {dig_immediate = 3}, +}) + +-- Analogously to the height pyramid stairs nodes, +-- these nodes should help to validate if parallax occlusion works correctly for +-- rotated nodes (which have rotated textures). +stairs.register_stair_and_slab("parallax_extruded", + "experimantal:parallax_extruded", + {dig_immediate = 3}, + {"testnodes_parallax_extruded.png"}, + "Parallax Occlusion Tester Stair", + "Parallax Occlusion Tester Slab") diff --git a/games/devtest/mods/testpathfinder/init.lua b/games/devtest/mods/testpathfinder/init.lua index f94848236..67748afca 100644 --- a/games/devtest/mods/testpathfinder/init.lua +++ b/games/devtest/mods/testpathfinder/init.lua @@ -121,7 +121,11 @@ end -- Sneak+punch: Select pathfinding algorithm -- Place: Select destination node minetest.register_tool("testpathfinder:testpathfinder", { - description = S("Pathfinder Tester"), + description = S("Pathfinder Tester") .."\n".. + S("Finds path between 2 points") .."\n".. + S("Place on node: Select destination") .."\n".. + S("Punch: Find path from here") .."\n".. + S("Sneak+Punch: Change algorithm"), inventory_image = "testpathfinder_testpathfinder.png", groups = { testtool = 1, disable_repair = 1 }, on_use = find_path_or_set_algorithm, diff --git a/games/devtest/mods/testtools/init.lua b/games/devtest/mods/testtools/init.lua index d68a086b9..d578b264a 100644 --- a/games/devtest/mods/testtools/init.lua +++ b/games/devtest/mods/testtools/init.lua @@ -1,15 +1,17 @@ local S = minetest.get_translator("testtools") local F = minetest.formspec_escape +dofile(minetest.get_modpath("testtools") .. "/light.lua") + -- TODO: Add a Node Metadata tool --- Param 2 Tool: Set param2 value of tools --- Punch: +1 --- Punch+Shift: +8 --- Place: -1 --- Place+Shift: -8 minetest.register_tool("testtools:param2tool", { - description = S("Param2 Tool"), + description = S("Param2 Tool") .."\n".. + S("Modify param2 value of nodes") .."\n".. + S("Punch: +1") .."\n".. + S("Sneak+Punch: +8") .."\n".. + S("Place: -1") .."\n".. + S("Sneak+Place: -8"), inventory_image = "testtools_param2tool.png", groups = { testtool = 1, disable_repair = 1 }, on_use = function(itemstack, user, pointed_thing) @@ -47,7 +49,11 @@ minetest.register_tool("testtools:param2tool", { }) minetest.register_tool("testtools:node_setter", { - description = S("Node Setter"), + description = S("Node Setter") .."\n".. + S("Replace pointed node with something else") .."\n".. + S("Punch: Select pointed node") .."\n".. + S("Place on node: Replace node with selected node") .."\n".. + S("Place in air: Manually select a node"), inventory_image = "testtools_node_setter.png", groups = { testtool = 1, disable_repair = 1 }, on_use = function(itemstack, user, pointed_thing) @@ -125,7 +131,8 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end) minetest.register_tool("testtools:remover", { - description = S("Remover"), + description = S("Remover") .."\n".. + S("Punch: Remove pointed node or object"), inventory_image = "testtools_remover.png", groups = { testtool = 1, disable_repair = 1 }, on_use = function(itemstack, user, pointed_thing) @@ -136,13 +143,17 @@ minetest.register_tool("testtools:remover", { local obj = pointed_thing.ref if not obj:is_player() then obj:remove() + else + minetest.chat_send_player(user:get_player_name(), S("Can't remove players!")) end end end, }) minetest.register_tool("testtools:falling_node_tool", { - description = S("Falling Node Tool"), + description = S("Falling Node Tool") .."\n".. + S("Punch: Make pointed node fall") .."\n".. + S("Place: Move pointed node 2 units upwards, then make it fall"), inventory_image = "testtools_falling_node_tool.png", groups = { testtool = 1, disable_repair = 1 }, on_place = function(itemstack, user, pointed_thing) @@ -191,7 +202,11 @@ minetest.register_tool("testtools:falling_node_tool", { }) minetest.register_tool("testtools:rotator", { - description = S("Entity Rotator"), + description = S("Entity Rotator") .. "\n" .. + S("Rotate pointed entity") .."\n".. + S("Punch: Yaw") .."\n".. + S("Sneak+Punch: Pitch") .."\n".. + S("Aux1+Punch: Roll"), inventory_image = "testtools_entity_rotator.png", groups = { testtool = 1, disable_repair = 1 }, on_use = function(itemstack, user, pointed_thing) @@ -244,7 +259,12 @@ local mover_config = function(itemstack, user, pointed_thing) end minetest.register_tool("testtools:object_mover", { - description = S("Object Mover"), + description = S("Object Mover") .."\n".. + S("Move pointed object towards or away from you") .."\n".. + S("Punch: Move by distance").."\n".. + S("Sneak+Punch: Move by negative distance").."\n".. + S("Place: Increase distance").."\n".. + S("Sneak+Place: Decrease distance"), inventory_image = "testtools_object_mover.png", groups = { testtool = 1, disable_repair = 1 }, on_place = mover_config, @@ -287,7 +307,10 @@ minetest.register_tool("testtools:object_mover", { minetest.register_tool("testtools:entity_scaler", { - description = S("Entity Visual Scaler"), + description = S("Entity Visual Scaler") .."\n".. + S("Scale visual size of entities") .."\n".. + S("Punch: Increase size") .."\n".. + S("Sneak+Punch: Decrease scale"), inventory_image = "testtools_entity_scaler.png", groups = { testtool = 1, disable_repair = 1 }, on_use = function(itemstack, user, pointed_thing) @@ -342,14 +365,21 @@ local function get_entity_list() return entity_list end minetest.register_tool("testtools:entity_spawner", { - description = S("Entity Spawner"), + description = S("Entity Spawner") .."\n".. + S("Spawns entities") .."\n".. + S("Punch: Select entity to spawn") .."\n".. + S("Place: Spawn selected entity"), inventory_image = "testtools_entity_spawner.png", groups = { testtool = 1, disable_repair = 1 }, on_place = function(itemstack, user, pointed_thing) local name = user:get_player_name() - if selections[name] and pointed_thing.type == "node" then - local pos = pointed_thing.above - minetest.add_entity(pos, get_entity_list()[selections[name]]) + if pointed_thing.type == "node" then + if selections[name] then + local pos = pointed_thing.above + minetest.add_entity(pos, get_entity_list()[selections[name]]) + else + minetest.chat_send_player(name, S("Select an entity first (with punch key)!")) + end end end, on_use = function(itemstack, user, pointed_thing) @@ -435,7 +465,10 @@ local editor_formspec = function(playername, obj, value, sel) end minetest.register_tool("testtools:object_editor", { - description = S("Object Property Editor"), + description = S("Object Property Editor") .."\n".. + S("Edit properties of objects") .."\n".. + S("Punch object: Edit object") .."\n".. + S("Punch air: Edit yourself"), inventory_image = "testtools_object_editor.png", groups = { testtool = 1, disable_repair = 1 }, on_use = function(itemstack, user, pointed_thing) @@ -515,7 +548,14 @@ local attacher_config = function(itemstack, user, pointed_thing) end minetest.register_tool("testtools:object_attacher", { - description = S("Object Attacher"), + description = S("Object Attacher") .."\n".. + S("Attach object to another") .."\n".. + S("Punch objects to first select parent object, then the child object to attach") .."\n".. + S("Punch air to select yourself") .."\n".. + S("Place: Incease attachment Y offset") .."\n".. + S("Sneak+Place: Decease attachment Y offset") .."\n".. + S("Aux1+Place: Incease attachment rotation") .."\n".. + S("Aux1+Sneak+Place: Decrease attachment rotation"), inventory_image = "testtools_object_attacher.png", groups = { testtool = 1, disable_repair = 1 }, on_place = attacher_config, diff --git a/games/devtest/mods/unittests/init.lua b/games/devtest/mods/unittests/init.lua index 6c1728420..12c67f78b 100644 --- a/games/devtest/mods/unittests/init.lua +++ b/games/devtest/mods/unittests/init.lua @@ -5,10 +5,12 @@ dofile(modpath .. "/random.lua") dofile(modpath .. "/player.lua") dofile(modpath .. "/crafting_prepare.lua") dofile(modpath .. "/crafting.lua") +dofile(modpath .. "/itemdescription.lua") if minetest.settings:get_bool("devtest_unittests_autostart", false) then unittests.test_random() unittests.test_crafting() + unittests.test_short_desc() minetest.register_on_joinplayer(function(player) unittests.test_player(player) end) diff --git a/games/devtest/mods/util_commands/init.lua b/games/devtest/mods/util_commands/init.lua index 3a0e91a41..ca5dca2d9 100644 --- a/games/devtest/mods/util_commands/init.lua +++ b/games/devtest/mods/util_commands/init.lua @@ -36,8 +36,12 @@ minetest.register_chatcommand("hp", { end, }) -minetest.register_chatcommand("zoom", { - params = "[]", +minetest.register_on_joinplayer(function(player) + player:set_properties({zoom_fov = 15}) +end) + +minetest.register_chatcommand("zoomfov", { + params = "[]", description = "Set or display your zoom_fov", func = function(name, param) local player = minetest.get_player_by_name(name) @@ -58,8 +62,6 @@ minetest.register_chatcommand("zoom", { end, }) - - local s_infplace = minetest.settings:get("devtest_infplace") if s_infplace == "true" then infplace = true @@ -112,6 +114,62 @@ minetest.register_chatcommand("detach", { end, }) +-- Use this to test waypoint capabilities +minetest.register_chatcommand("test_waypoints", { + params = "[change_immediate]", + description = "tests waypoint capabilities", + func = function(name, params) + local player = minetest.get_player_by_name(name) + local regular = player:hud_add { + hud_elem_type = "waypoint", + name = "regular waypoint", + text = "m", + number = 0xFF0000, + world_pos = vector.add(player:get_pos(), {x = 0, y = 1.5, z = 0}) + } + local reduced_precision = player:hud_add { + hud_elem_type = "waypoint", + name = "better waypoint", + text = "m (0.5 steps, precision = 2)", + precision = 10, + number = 0xFFFF00, + world_pos = vector.add(player:get_pos(), {x = 0, y = 1, z = 0}) + } + local function change() + if regular then + player:hud_change(regular, "world_pos", vector.add(player:get_pos(), {x = 0, y = 3, z = 0})) + end + if reduced_precision then + player:hud_change(reduced_precision, "precision", 2) + end + end + if params ~= "" then + -- change immediate + change() + else + minetest.after(0.5, change) + end + regular = regular or "error" + reduced_precision = reduced_precision or "error" + local hidden_distance = player:hud_add { + hud_elem_type = "waypoint", + name = "waypoint with hidden distance", + text = "this text is hidden as well (precision = 0)", + precision = 0, + number = 0x0000FF, + world_pos = vector.add(player:get_pos(), {x = 0, y = 0.5, z = 0}) + } or "error" + local image_waypoint = player:hud_add { + hud_elem_type = "image_waypoint", + text = "wieldhand.png", + world_pos = player:get_pos(), + scale = {x = 10, y = 10}, + offset = {x = 0, y = -32} + } or "error" + minetest.chat_send_player(name, "Waypoint IDs: regular: " .. regular .. ", reduced precision: " .. reduced_precision .. + ", hidden distance: " .. hidden_distance .. ", image waypoint: " .. image_waypoint) + end +}) -- Unlimited node placement minetest.register_on_placenode(function(pos, newnode, placer, oldnode, itemstack) diff --git a/lib/jsoncpp/json/UPDATING b/lib/jsoncpp/json/UPDATING index f0226e8ee..0893244cf 100644 --- a/lib/jsoncpp/json/UPDATING +++ b/lib/jsoncpp/json/UPDATING @@ -1,6 +1,6 @@ #!/bin/sh cd .. -git clone https://github.com/open-source-parsers/jsoncpp -b 1.8.3 --depth 1 +git clone https://github.com/open-source-parsers/jsoncpp -b 1.9.4 --depth 1 cd jsoncpp python amalgamate.py cp -R dist/json ../json diff --git a/lib/jsoncpp/json/json-forwards.h b/lib/jsoncpp/json/json-forwards.h index de2e4bd79..d3260c57c 100644 --- a/lib/jsoncpp/json/json-forwards.h +++ b/lib/jsoncpp/json/json-forwards.h @@ -79,6 +79,151 @@ license you like. /// to prevent private header inclusion. #define JSON_IS_AMALGAMATION +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/version.h +// ////////////////////////////////////////////////////////////////////// + +#ifndef JSON_VERSION_H_INCLUDED +#define JSON_VERSION_H_INCLUDED + +// Note: version must be updated in three places when doing a release. This +// annoying process ensures that amalgamate, CMake, and meson all report the +// correct version. +// 1. /meson.build +// 2. /include/json/version.h +// 3. /CMakeLists.txt +// IMPORTANT: also update the SOVERSION!! + +#define JSONCPP_VERSION_STRING "1.9.4" +#define JSONCPP_VERSION_MAJOR 1 +#define JSONCPP_VERSION_MINOR 9 +#define JSONCPP_VERSION_PATCH 3 +#define JSONCPP_VERSION_QUALIFIER +#define JSONCPP_VERSION_HEXA \ + ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | \ + (JSONCPP_VERSION_PATCH << 8)) + +#ifdef JSONCPP_USING_SECURE_MEMORY +#undef JSONCPP_USING_SECURE_MEMORY +#endif +#define JSONCPP_USING_SECURE_MEMORY 0 +// If non-zero, the library zeroes any memory that it has allocated before +// it frees its memory. + +#endif // JSON_VERSION_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/version.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/allocator.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_ALLOCATOR_H_INCLUDED +#define JSON_ALLOCATOR_H_INCLUDED + +#include +#include + +#pragma pack(push, 8) + +namespace Json { +template class SecureAllocator { +public: + // Type definitions + using value_type = T; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + /** + * Allocate memory for N items using the standard allocator. + */ + pointer allocate(size_type n) { + // allocate using "global operator new" + return static_cast(::operator new(n * sizeof(T))); + } + + /** + * Release memory which was allocated for N items at pointer P. + * + * The memory block is filled with zeroes before being released. + * The pointer argument is tagged as "volatile" to prevent the + * compiler optimizing out this critical step. + */ + void deallocate(volatile pointer p, size_type n) { + std::memset(p, 0, n * sizeof(T)); + // free using "global operator delete" + ::operator delete(p); + } + + /** + * Construct an item in-place at pointer P. + */ + template void construct(pointer p, Args&&... args) { + // construct using "placement new" and "perfect forwarding" + ::new (static_cast(p)) T(std::forward(args)...); + } + + size_type max_size() const { return size_t(-1) / sizeof(T); } + + pointer address(reference x) const { return std::addressof(x); } + + const_pointer address(const_reference x) const { return std::addressof(x); } + + /** + * Destroy an item in-place at pointer P. + */ + void destroy(pointer p) { + // destroy using "explicit destructor" + p->~T(); + } + + // Boilerplate + SecureAllocator() {} + template SecureAllocator(const SecureAllocator&) {} + template struct rebind { using other = SecureAllocator; }; +}; + +template +bool operator==(const SecureAllocator&, const SecureAllocator&) { + return true; +} + +template +bool operator!=(const SecureAllocator&, const SecureAllocator&) { + return false; +} + +} // namespace Json + +#pragma pack(pop) + +#endif // JSON_ALLOCATOR_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/allocator.h +// ////////////////////////////////////////////////////////////////////// + + + + + + // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: include/json/config.h // ////////////////////////////////////////////////////////////////////// @@ -90,19 +235,14 @@ license you like. #ifndef JSON_CONFIG_H_INCLUDED #define JSON_CONFIG_H_INCLUDED -#include -#include //typedef String -#include //typedef int64_t, uint64_t - -/// If defined, indicates that json library is embedded in CppTL library. -//# define JSON_IN_CPPTL 1 - -/// If defined, indicates that json may leverage CppTL library -//# define JSON_USE_CPPTL 1 -/// If defined, indicates that cpptl vector based map should be used instead of -/// std::map -/// as Value container. -//# define JSON_USE_CPPTL_SMALLMAP 1 +#include +#include +#include +#include +#include +#include +#include +#include // If non-zero, the library uses exceptions to report bad input instead of C // assertion macros. The default is to use exceptions. @@ -110,164 +250,132 @@ license you like. #define JSON_USE_EXCEPTION 1 #endif +// Temporary, tracked for removal with issue #982. +#ifndef JSON_USE_NULLREF +#define JSON_USE_NULLREF 1 +#endif + /// If defined, indicates that the source file is amalgamated /// to prevent private header inclusion. /// Remarks: it is automatically defined in the generated amalgamated header. // #define JSON_IS_AMALGAMATION -#ifdef JSON_IN_CPPTL -#include -#ifndef JSON_USE_CPPTL -#define JSON_USE_CPPTL 1 -#endif -#endif - -#ifdef JSON_IN_CPPTL -#define JSON_API CPPTL_API -#elif defined(JSON_DLL_BUILD) +// Export macros for DLL visibility +#if defined(JSON_DLL_BUILD) #if defined(_MSC_VER) || defined(__MINGW32__) #define JSON_API __declspec(dllexport) #define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#elif defined(__GNUC__) || defined(__clang__) +#define JSON_API __attribute__((visibility("default"))) #endif // if defined(_MSC_VER) + #elif defined(JSON_DLL) #if defined(_MSC_VER) || defined(__MINGW32__) #define JSON_API __declspec(dllimport) #define JSONCPP_DISABLE_DLL_INTERFACE_WARNING #endif // if defined(_MSC_VER) -#endif // ifdef JSON_IN_CPPTL +#endif // ifdef JSON_DLL_BUILD + #if !defined(JSON_API) #define JSON_API #endif +#if defined(_MSC_VER) && _MSC_VER < 1800 +#error \ + "ERROR: Visual Studio 12 (2013) with _MSC_VER=1800 is the oldest supported compiler with sufficient C++11 capabilities" +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1900 +// As recommended at +// https://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 +extern JSON_API int msvc_pre1900_c99_snprintf(char* outBuf, size_t size, + const char* format, ...); +#define jsoncpp_snprintf msvc_pre1900_c99_snprintf +#else +#define jsoncpp_snprintf std::snprintf +#endif + // If JSON_NO_INT64 is defined, then Json only support C++ "int" type for // integer // Storages, and 64 bits integer support is disabled. // #define JSON_NO_INT64 1 -#if defined(_MSC_VER) // MSVC -# if _MSC_VER <= 1200 // MSVC 6 - // Microsoft Visual Studio 6 only support conversion from __int64 to double - // (no conversion from unsigned __int64). -# define JSON_USE_INT64_DOUBLE_CONVERSION 1 - // Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255' - // characters in the debug information) - // All projects I've ever seen with VS6 were using this globally (not bothering - // with pragma push/pop). -# pragma warning(disable : 4786) -# endif // MSVC 6 - -# if _MSC_VER >= 1500 // MSVC 2008 - /// Indicates that the following function is deprecated. -# define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) -# endif - -#endif // defined(_MSC_VER) - -// In c++11 the override keyword allows you to explicitly define that a function -// is intended to override the base-class version. This makes the code more -// managable and fixes a set of common hard-to-find bugs. -#if __cplusplus >= 201103L -# define JSONCPP_OVERRIDE override -# define JSONCPP_NOEXCEPT noexcept -#elif defined(_MSC_VER) && _MSC_VER > 1600 && _MSC_VER < 1900 -# define JSONCPP_OVERRIDE override -# define JSONCPP_NOEXCEPT throw() -#elif defined(_MSC_VER) && _MSC_VER >= 1900 -# define JSONCPP_OVERRIDE override -# define JSONCPP_NOEXCEPT noexcept -#else -# define JSONCPP_OVERRIDE -# define JSONCPP_NOEXCEPT throw() -#endif - -#ifndef JSON_HAS_RVALUE_REFERENCES - -#if defined(_MSC_VER) && _MSC_VER >= 1600 // MSVC >= 2010 -#define JSON_HAS_RVALUE_REFERENCES 1 -#endif // MSVC >= 2010 +// JSONCPP_OVERRIDE is maintained for backwards compatibility of external tools. +// C++11 should be used directly in JSONCPP. +#define JSONCPP_OVERRIDE override #ifdef __clang__ -#if __has_feature(cxx_rvalue_references) -#define JSON_HAS_RVALUE_REFERENCES 1 -#endif // has_feature - -#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) -#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L) -#define JSON_HAS_RVALUE_REFERENCES 1 -#endif // GXX_EXPERIMENTAL - -#endif // __clang__ || __GNUC__ - -#endif // not defined JSON_HAS_RVALUE_REFERENCES - -#ifndef JSON_HAS_RVALUE_REFERENCES -#define JSON_HAS_RVALUE_REFERENCES 0 +#if __has_extension(attribute_deprecated_with_message) +#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message))) #endif - -#ifdef __clang__ -# if __has_extension(attribute_deprecated_with_message) -# define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) -# endif -#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) -# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) -# define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) -# elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) -# define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) -# endif // GNUC version -#endif // __clang__ || __GNUC__ +#elif defined(__GNUC__) // not clang (gcc comes later since clang emulates gcc) +#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message))) +#elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) +#endif // GNUC version +#elif defined(_MSC_VER) // MSVC (after clang because clang on Windows emulates + // MSVC) +#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) +#endif // __clang__ || __GNUC__ || _MSC_VER #if !defined(JSONCPP_DEPRECATED) #define JSONCPP_DEPRECATED(message) #endif // if !defined(JSONCPP_DEPRECATED) -#if __GNUC__ >= 6 -# define JSON_USE_INT64_DOUBLE_CONVERSION 1 +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 6)) +#define JSON_USE_INT64_DOUBLE_CONVERSION 1 #endif #if !defined(JSON_IS_AMALGAMATION) -# include "version.h" - -# if JSONCPP_USING_SECURE_MEMORY -# include "allocator.h" //typedef Allocator -# endif +#include "allocator.h" +#include "version.h" #endif // if !defined(JSON_IS_AMALGAMATION) namespace Json { -typedef int Int; -typedef unsigned int UInt; +using Int = int; +using UInt = unsigned int; #if defined(JSON_NO_INT64) -typedef int LargestInt; -typedef unsigned int LargestUInt; +using LargestInt = int; +using LargestUInt = unsigned int; #undef JSON_HAS_INT64 #else // if defined(JSON_NO_INT64) // For Microsoft Visual use specific types as long long is not supported #if defined(_MSC_VER) // Microsoft Visual Studio -typedef __int64 Int64; -typedef unsigned __int64 UInt64; +using Int64 = __int64; +using UInt64 = unsigned __int64; #else // if defined(_MSC_VER) // Other platforms, use long long -typedef int64_t Int64; -typedef uint64_t UInt64; -#endif // if defined(_MSC_VER) -typedef Int64 LargestInt; -typedef UInt64 LargestUInt; +using Int64 = int64_t; +using UInt64 = uint64_t; +#endif // if defined(_MSC_VER) +using LargestInt = Int64; +using LargestUInt = UInt64; #define JSON_HAS_INT64 #endif // if defined(JSON_NO_INT64) -#if JSONCPP_USING_SECURE_MEMORY -#define JSONCPP_STRING std::basic_string, Json::SecureAllocator > -#define JSONCPP_OSTRINGSTREAM std::basic_ostringstream, Json::SecureAllocator > -#define JSONCPP_OSTREAM std::basic_ostream> -#define JSONCPP_ISTRINGSTREAM std::basic_istringstream, Json::SecureAllocator > -#define JSONCPP_ISTREAM std::istream -#else -#define JSONCPP_STRING std::string -#define JSONCPP_OSTRINGSTREAM std::ostringstream -#define JSONCPP_OSTREAM std::ostream -#define JSONCPP_ISTRINGSTREAM std::istringstream -#define JSONCPP_ISTREAM std::istream -#endif // if JSONCPP_USING_SECURE_MEMORY -} // end namespace Json + +template +using Allocator = + typename std::conditional, + std::allocator>::type; +using String = std::basic_string, Allocator>; +using IStringStream = + std::basic_istringstream; +using OStringStream = + std::basic_ostringstream; +using IStream = std::istream; +using OStream = std::ostream; +} // namespace Json + +// Legacy names (formerly macros). +using JSONCPP_STRING = Json::String; +using JSONCPP_ISTRINGSTREAM = Json::IStringStream; +using JSONCPP_OSTRINGSTREAM = Json::OStringStream; +using JSONCPP_ISTREAM = Json::IStream; +using JSONCPP_OSTREAM = Json::OStream; #endif // JSON_CONFIG_H_INCLUDED @@ -299,17 +407,23 @@ typedef UInt64 LargestUInt; namespace Json { // writer.h +class StreamWriter; +class StreamWriterBuilder; +class Writer; class FastWriter; class StyledWriter; +class StyledStreamWriter; // reader.h class Reader; +class CharReader; +class CharReaderBuilder; -// features.h +// json_features.h class Features; // value.h -typedef unsigned int ArrayIndex; +using ArrayIndex = unsigned int; class StaticString; class Path; class PathArgument; diff --git a/lib/jsoncpp/json/json.h b/lib/jsoncpp/json/json.h index 625ba02e9..3f4813a95 100644 --- a/lib/jsoncpp/json/json.h +++ b/lib/jsoncpp/json/json.h @@ -82,17 +82,25 @@ license you like. // Beginning of content of file: include/json/version.h // ////////////////////////////////////////////////////////////////////// -// DO NOT EDIT. This file (and "version") is generated by CMake. -// Run CMake configure step to update it. #ifndef JSON_VERSION_H_INCLUDED -# define JSON_VERSION_H_INCLUDED +#define JSON_VERSION_H_INCLUDED -# define JSONCPP_VERSION_STRING "1.8.4" -# define JSONCPP_VERSION_MAJOR 1 -# define JSONCPP_VERSION_MINOR 8 -# define JSONCPP_VERSION_PATCH 4 -# define JSONCPP_VERSION_QUALIFIER -# define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8)) +// Note: version must be updated in three places when doing a release. This +// annoying process ensures that amalgamate, CMake, and meson all report the +// correct version. +// 1. /meson.build +// 2. /include/json/version.h +// 3. /CMakeLists.txt +// IMPORTANT: also update the SOVERSION!! + +#define JSONCPP_VERSION_STRING "1.9.4" +#define JSONCPP_VERSION_MAJOR 1 +#define JSONCPP_VERSION_MINOR 9 +#define JSONCPP_VERSION_PATCH 3 +#define JSONCPP_VERSION_QUALIFIER +#define JSONCPP_VERSION_HEXA \ + ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | \ + (JSONCPP_VERSION_PATCH << 8)) #ifdef JSONCPP_USING_SECURE_MEMORY #undef JSONCPP_USING_SECURE_MEMORY @@ -112,6 +120,109 @@ license you like. +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/allocator.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_ALLOCATOR_H_INCLUDED +#define JSON_ALLOCATOR_H_INCLUDED + +#include +#include + +#pragma pack(push, 8) + +namespace Json { +template class SecureAllocator { +public: + // Type definitions + using value_type = T; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + /** + * Allocate memory for N items using the standard allocator. + */ + pointer allocate(size_type n) { + // allocate using "global operator new" + return static_cast(::operator new(n * sizeof(T))); + } + + /** + * Release memory which was allocated for N items at pointer P. + * + * The memory block is filled with zeroes before being released. + * The pointer argument is tagged as "volatile" to prevent the + * compiler optimizing out this critical step. + */ + void deallocate(volatile pointer p, size_type n) { + std::memset(p, 0, n * sizeof(T)); + // free using "global operator delete" + ::operator delete(p); + } + + /** + * Construct an item in-place at pointer P. + */ + template void construct(pointer p, Args&&... args) { + // construct using "placement new" and "perfect forwarding" + ::new (static_cast(p)) T(std::forward(args)...); + } + + size_type max_size() const { return size_t(-1) / sizeof(T); } + + pointer address(reference x) const { return std::addressof(x); } + + const_pointer address(const_reference x) const { return std::addressof(x); } + + /** + * Destroy an item in-place at pointer P. + */ + void destroy(pointer p) { + // destroy using "explicit destructor" + p->~T(); + } + + // Boilerplate + SecureAllocator() {} + template SecureAllocator(const SecureAllocator&) {} + template struct rebind { using other = SecureAllocator; }; +}; + +template +bool operator==(const SecureAllocator&, const SecureAllocator&) { + return true; +} + +template +bool operator!=(const SecureAllocator&, const SecureAllocator&) { + return false; +} + +} // namespace Json + +#pragma pack(pop) + +#endif // JSON_ALLOCATOR_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/allocator.h +// ////////////////////////////////////////////////////////////////////// + + + + + + // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: include/json/config.h // ////////////////////////////////////////////////////////////////////// @@ -123,19 +234,14 @@ license you like. #ifndef JSON_CONFIG_H_INCLUDED #define JSON_CONFIG_H_INCLUDED -#include -#include //typedef String -#include //typedef int64_t, uint64_t - -/// If defined, indicates that json library is embedded in CppTL library. -//# define JSON_IN_CPPTL 1 - -/// If defined, indicates that json may leverage CppTL library -//# define JSON_USE_CPPTL 1 -/// If defined, indicates that cpptl vector based map should be used instead of -/// std::map -/// as Value container. -//# define JSON_USE_CPPTL_SMALLMAP 1 +#include +#include +#include +#include +#include +#include +#include +#include // If non-zero, the library uses exceptions to report bad input instead of C // assertion macros. The default is to use exceptions. @@ -143,164 +249,132 @@ license you like. #define JSON_USE_EXCEPTION 1 #endif +// Temporary, tracked for removal with issue #982. +#ifndef JSON_USE_NULLREF +#define JSON_USE_NULLREF 1 +#endif + /// If defined, indicates that the source file is amalgamated /// to prevent private header inclusion. /// Remarks: it is automatically defined in the generated amalgamated header. // #define JSON_IS_AMALGAMATION -#ifdef JSON_IN_CPPTL -#include -#ifndef JSON_USE_CPPTL -#define JSON_USE_CPPTL 1 -#endif -#endif - -#ifdef JSON_IN_CPPTL -#define JSON_API CPPTL_API -#elif defined(JSON_DLL_BUILD) +// Export macros for DLL visibility +#if defined(JSON_DLL_BUILD) #if defined(_MSC_VER) || defined(__MINGW32__) #define JSON_API __declspec(dllexport) #define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#elif defined(__GNUC__) || defined(__clang__) +#define JSON_API __attribute__((visibility("default"))) #endif // if defined(_MSC_VER) + #elif defined(JSON_DLL) #if defined(_MSC_VER) || defined(__MINGW32__) #define JSON_API __declspec(dllimport) #define JSONCPP_DISABLE_DLL_INTERFACE_WARNING #endif // if defined(_MSC_VER) -#endif // ifdef JSON_IN_CPPTL +#endif // ifdef JSON_DLL_BUILD + #if !defined(JSON_API) #define JSON_API #endif +#if defined(_MSC_VER) && _MSC_VER < 1800 +#error \ + "ERROR: Visual Studio 12 (2013) with _MSC_VER=1800 is the oldest supported compiler with sufficient C++11 capabilities" +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1900 +// As recommended at +// https://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 +extern JSON_API int msvc_pre1900_c99_snprintf(char* outBuf, size_t size, + const char* format, ...); +#define jsoncpp_snprintf msvc_pre1900_c99_snprintf +#else +#define jsoncpp_snprintf std::snprintf +#endif + // If JSON_NO_INT64 is defined, then Json only support C++ "int" type for // integer // Storages, and 64 bits integer support is disabled. // #define JSON_NO_INT64 1 -#if defined(_MSC_VER) // MSVC -# if _MSC_VER <= 1200 // MSVC 6 - // Microsoft Visual Studio 6 only support conversion from __int64 to double - // (no conversion from unsigned __int64). -# define JSON_USE_INT64_DOUBLE_CONVERSION 1 - // Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255' - // characters in the debug information) - // All projects I've ever seen with VS6 were using this globally (not bothering - // with pragma push/pop). -# pragma warning(disable : 4786) -# endif // MSVC 6 - -# if _MSC_VER >= 1500 // MSVC 2008 - /// Indicates that the following function is deprecated. -# define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) -# endif - -#endif // defined(_MSC_VER) - -// In c++11 the override keyword allows you to explicitly define that a function -// is intended to override the base-class version. This makes the code more -// managable and fixes a set of common hard-to-find bugs. -#if __cplusplus >= 201103L -# define JSONCPP_OVERRIDE override -# define JSONCPP_NOEXCEPT noexcept -#elif defined(_MSC_VER) && _MSC_VER > 1600 && _MSC_VER < 1900 -# define JSONCPP_OVERRIDE override -# define JSONCPP_NOEXCEPT throw() -#elif defined(_MSC_VER) && _MSC_VER >= 1900 -# define JSONCPP_OVERRIDE override -# define JSONCPP_NOEXCEPT noexcept -#else -# define JSONCPP_OVERRIDE -# define JSONCPP_NOEXCEPT throw() -#endif - -#ifndef JSON_HAS_RVALUE_REFERENCES - -#if defined(_MSC_VER) && _MSC_VER >= 1600 // MSVC >= 2010 -#define JSON_HAS_RVALUE_REFERENCES 1 -#endif // MSVC >= 2010 +// JSONCPP_OVERRIDE is maintained for backwards compatibility of external tools. +// C++11 should be used directly in JSONCPP. +#define JSONCPP_OVERRIDE override #ifdef __clang__ -#if __has_feature(cxx_rvalue_references) -#define JSON_HAS_RVALUE_REFERENCES 1 -#endif // has_feature - -#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) -#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L) -#define JSON_HAS_RVALUE_REFERENCES 1 -#endif // GXX_EXPERIMENTAL - -#endif // __clang__ || __GNUC__ - -#endif // not defined JSON_HAS_RVALUE_REFERENCES - -#ifndef JSON_HAS_RVALUE_REFERENCES -#define JSON_HAS_RVALUE_REFERENCES 0 +#if __has_extension(attribute_deprecated_with_message) +#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message))) #endif - -#ifdef __clang__ -# if __has_extension(attribute_deprecated_with_message) -# define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) -# endif -#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) -# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) -# define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) -# elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) -# define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) -# endif // GNUC version -#endif // __clang__ || __GNUC__ +#elif defined(__GNUC__) // not clang (gcc comes later since clang emulates gcc) +#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message))) +#elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) +#endif // GNUC version +#elif defined(_MSC_VER) // MSVC (after clang because clang on Windows emulates + // MSVC) +#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) +#endif // __clang__ || __GNUC__ || _MSC_VER #if !defined(JSONCPP_DEPRECATED) #define JSONCPP_DEPRECATED(message) #endif // if !defined(JSONCPP_DEPRECATED) -#if __GNUC__ >= 6 -# define JSON_USE_INT64_DOUBLE_CONVERSION 1 +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 6)) +#define JSON_USE_INT64_DOUBLE_CONVERSION 1 #endif #if !defined(JSON_IS_AMALGAMATION) -# include "version.h" - -# if JSONCPP_USING_SECURE_MEMORY -# include "allocator.h" //typedef Allocator -# endif +#include "allocator.h" +#include "version.h" #endif // if !defined(JSON_IS_AMALGAMATION) namespace Json { -typedef int Int; -typedef unsigned int UInt; +using Int = int; +using UInt = unsigned int; #if defined(JSON_NO_INT64) -typedef int LargestInt; -typedef unsigned int LargestUInt; +using LargestInt = int; +using LargestUInt = unsigned int; #undef JSON_HAS_INT64 #else // if defined(JSON_NO_INT64) // For Microsoft Visual use specific types as long long is not supported #if defined(_MSC_VER) // Microsoft Visual Studio -typedef __int64 Int64; -typedef unsigned __int64 UInt64; +using Int64 = __int64; +using UInt64 = unsigned __int64; #else // if defined(_MSC_VER) // Other platforms, use long long -typedef int64_t Int64; -typedef uint64_t UInt64; -#endif // if defined(_MSC_VER) -typedef Int64 LargestInt; -typedef UInt64 LargestUInt; +using Int64 = int64_t; +using UInt64 = uint64_t; +#endif // if defined(_MSC_VER) +using LargestInt = Int64; +using LargestUInt = UInt64; #define JSON_HAS_INT64 #endif // if defined(JSON_NO_INT64) -#if JSONCPP_USING_SECURE_MEMORY -#define JSONCPP_STRING std::basic_string, Json::SecureAllocator > -#define JSONCPP_OSTRINGSTREAM std::basic_ostringstream, Json::SecureAllocator > -#define JSONCPP_OSTREAM std::basic_ostream> -#define JSONCPP_ISTRINGSTREAM std::basic_istringstream, Json::SecureAllocator > -#define JSONCPP_ISTREAM std::istream -#else -#define JSONCPP_STRING std::string -#define JSONCPP_OSTRINGSTREAM std::ostringstream -#define JSONCPP_OSTREAM std::ostream -#define JSONCPP_ISTRINGSTREAM std::istringstream -#define JSONCPP_ISTREAM std::istream -#endif // if JSONCPP_USING_SECURE_MEMORY -} // end namespace Json + +template +using Allocator = + typename std::conditional, + std::allocator>::type; +using String = std::basic_string, Allocator>; +using IStringStream = + std::basic_istringstream; +using OStringStream = + std::basic_ostringstream; +using IStream = std::istream; +using OStream = std::ostream; +} // namespace Json + +// Legacy names (formerly macros). +using JSONCPP_STRING = Json::String; +using JSONCPP_ISTRINGSTREAM = Json::IStringStream; +using JSONCPP_OSTRINGSTREAM = Json::OStringStream; +using JSONCPP_ISTREAM = Json::IStream; +using JSONCPP_OSTREAM = Json::OStream; #endif // JSON_CONFIG_H_INCLUDED @@ -332,17 +406,23 @@ typedef UInt64 LargestUInt; namespace Json { // writer.h +class StreamWriter; +class StreamWriterBuilder; +class Writer; class FastWriter; class StyledWriter; +class StyledStreamWriter; // reader.h class Reader; +class CharReader; +class CharReaderBuilder; -// features.h +// json_features.h class Features; // value.h -typedef unsigned int ArrayIndex; +using ArrayIndex = unsigned int; class StaticString; class Path; class PathArgument; @@ -365,7 +445,7 @@ class ValueConstIterator; // ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: include/json/features.h +// Beginning of content of file: include/json/json_features.h // ////////////////////////////////////////////////////////////////////// // Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors @@ -373,8 +453,8 @@ class ValueConstIterator; // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE -#ifndef CPPTL_JSON_FEATURES_H_INCLUDED -#define CPPTL_JSON_FEATURES_H_INCLUDED +#ifndef JSON_FEATURES_H_INCLUDED +#define JSON_FEATURES_H_INCLUDED #if !defined(JSON_IS_AMALGAMATION) #include "forwards.h" @@ -411,27 +491,27 @@ public: Features(); /// \c true if comments are allowed. Default: \c true. - bool allowComments_; + bool allowComments_{true}; /// \c true if root must be either an array or an object value. Default: \c /// false. - bool strictRoot_; + bool strictRoot_{false}; /// \c true if dropped null placeholders are allowed. Default: \c false. - bool allowDroppedNullPlaceholders_; + bool allowDroppedNullPlaceholders_{false}; /// \c true if numeric object key are allowed. Default: \c false. - bool allowNumericKeys_; + bool allowNumericKeys_{false}; }; } // namespace Json #pragma pack(pop) -#endif // CPPTL_JSON_FEATURES_H_INCLUDED +#endif // JSON_FEATURES_H_INCLUDED // ////////////////////////////////////////////////////////////////////// -// End of content of file: include/json/features.h +// End of content of file: include/json/json_features.h // ////////////////////////////////////////////////////////////////////// @@ -448,37 +528,48 @@ public: // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE -#ifndef CPPTL_JSON_H_INCLUDED -#define CPPTL_JSON_H_INCLUDED +#ifndef JSON_H_INCLUDED +#define JSON_H_INCLUDED #if !defined(JSON_IS_AMALGAMATION) #include "forwards.h" #endif // if !defined(JSON_IS_AMALGAMATION) -#include -#include -#include -#ifndef JSON_USE_CPPTL_SMALLMAP -#include -#else -#include -#endif -#ifdef JSON_USE_CPPTL -#include -#endif - -//Conditional NORETURN attribute on the throw functions would: +// Conditional NORETURN attribute on the throw functions would: // a) suppress false positives from static code analysis // b) possibly improve optimization opportunities. #if !defined(JSONCPP_NORETURN) -# if defined(_MSC_VER) -# define JSONCPP_NORETURN __declspec(noreturn) -# elif defined(__GNUC__) -# define JSONCPP_NORETURN __attribute__ ((__noreturn__)) -# else -# define JSONCPP_NORETURN -# endif +#if defined(_MSC_VER) && _MSC_VER == 1800 +#define JSONCPP_NORETURN __declspec(noreturn) +#else +#define JSONCPP_NORETURN [[noreturn]] #endif +#endif + +// Support for '= delete' with template declarations was a late addition +// to the c++11 standard and is rejected by clang 3.8 and Apple clang 8.2 +// even though these declare themselves to be c++11 compilers. +#if !defined(JSONCPP_TEMPLATE_DELETE) +#if defined(__clang__) && defined(__apple_build_version__) +#if __apple_build_version__ <= 8000042 +#define JSONCPP_TEMPLATE_DELETE +#endif +#elif defined(__clang__) +#if __clang_major__ == 3 && __clang_minor__ <= 8 +#define JSONCPP_TEMPLATE_DELETE +#endif +#endif +#if !defined(JSONCPP_TEMPLATE_DELETE) +#define JSONCPP_TEMPLATE_DELETE = delete +#endif +#endif + +#include +#include +#include +#include +#include +#include // Disable warning C4251: : needs to have dll-interface to // be used by... @@ -493,17 +584,19 @@ public: */ namespace Json { +#if JSON_USE_EXCEPTION /** Base class for all exceptions we throw. * * We use nothing but these internally. Of course, STL can throw others. */ class JSON_API Exception : public std::exception { public: - Exception(JSONCPP_STRING const& msg); - ~Exception() JSONCPP_NOEXCEPT JSONCPP_OVERRIDE; - char const* what() const JSONCPP_NOEXCEPT JSONCPP_OVERRIDE; + Exception(String msg); + ~Exception() noexcept override; + char const* what() const noexcept override; + protected: - JSONCPP_STRING msg_; + String msg_; }; /** Exceptions which the user cannot easily avoid. @@ -514,7 +607,7 @@ protected: */ class JSON_API RuntimeError : public Exception { public: - RuntimeError(JSONCPP_STRING const& msg); + RuntimeError(String const& msg); }; /** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros. @@ -525,13 +618,14 @@ public: */ class JSON_API LogicError : public Exception { public: - LogicError(JSONCPP_STRING const& msg); + LogicError(String const& msg); }; +#endif /// used internally -JSONCPP_NORETURN void throwRuntimeError(JSONCPP_STRING const& msg); +JSONCPP_NORETURN void throwRuntimeError(String const& msg); /// used internally -JSONCPP_NORETURN void throwLogicError(JSONCPP_STRING const& msg); +JSONCPP_NORETURN void throwLogicError(String const& msg); /** \brief Type of the value held by a Value object. */ @@ -554,10 +648,12 @@ enum CommentPlacement { numberOfCommentPlacement }; -//# ifdef JSON_USE_CPPTL -// typedef CppTL::AnyEnumerator EnumMemberNames; -// typedef CppTL::AnyEnumerator EnumValues; -//# endif +/** \brief Type of precision for formatting of real values. + */ +enum PrecisionType { + significantDigits = 0, ///< we set max number of significant digits in string + decimalPlaces ///< we set max number of digits after "." in string +}; /** \brief Lightweight wrapper to tag static string. * @@ -610,7 +706,7 @@ private: * The get() methods can be used to obtain default value in the case the * required element does not exist. * - * It is possible to iterate over the list of a #objectValue values using + * It is possible to iterate over the list of member keys of an object using * the getMemberNames() method. * * \note #Value string-length fit in size_t, but keys must be < 2^30. @@ -621,76 +717,86 @@ private: */ class JSON_API Value { friend class ValueIteratorBase; + public: - typedef std::vector Members; - typedef ValueIterator iterator; - typedef ValueConstIterator const_iterator; - typedef Json::UInt UInt; - typedef Json::Int Int; + using Members = std::vector; + using iterator = ValueIterator; + using const_iterator = ValueConstIterator; + using UInt = Json::UInt; + using Int = Json::Int; #if defined(JSON_HAS_INT64) - typedef Json::UInt64 UInt64; - typedef Json::Int64 Int64; + using UInt64 = Json::UInt64; + using Int64 = Json::Int64; #endif // defined(JSON_HAS_INT64) - typedef Json::LargestInt LargestInt; - typedef Json::LargestUInt LargestUInt; - typedef Json::ArrayIndex ArrayIndex; + using LargestInt = Json::LargestInt; + using LargestUInt = Json::LargestUInt; + using ArrayIndex = Json::ArrayIndex; // Required for boost integration, e. g. BOOST_TEST - typedef std::string value_type; + using value_type = std::string; - static const Value& null; ///< We regret this reference to a global instance; prefer the simpler Value(). - static const Value& nullRef; ///< just a kludge for binary-compatibility; same as null - static Value const& nullSingleton(); ///< Prefer this to null or nullRef. +#if JSON_USE_NULLREF + // Binary compatibility kludges, do not use. + static const Value& null; + static const Value& nullRef; +#endif + + // null and nullRef are deprecated, use this instead. + static Value const& nullSingleton(); /// Minimum signed integer value that can be stored in a Json::Value. - static const LargestInt minLargestInt; + static constexpr LargestInt minLargestInt = + LargestInt(~(LargestUInt(-1) / 2)); /// Maximum signed integer value that can be stored in a Json::Value. - static const LargestInt maxLargestInt; + static constexpr LargestInt maxLargestInt = LargestInt(LargestUInt(-1) / 2); /// Maximum unsigned integer value that can be stored in a Json::Value. - static const LargestUInt maxLargestUInt; + static constexpr LargestUInt maxLargestUInt = LargestUInt(-1); /// Minimum signed int value that can be stored in a Json::Value. - static const Int minInt; + static constexpr Int minInt = Int(~(UInt(-1) / 2)); /// Maximum signed int value that can be stored in a Json::Value. - static const Int maxInt; + static constexpr Int maxInt = Int(UInt(-1) / 2); /// Maximum unsigned int value that can be stored in a Json::Value. - static const UInt maxUInt; + static constexpr UInt maxUInt = UInt(-1); #if defined(JSON_HAS_INT64) /// Minimum signed 64 bits int value that can be stored in a Json::Value. - static const Int64 minInt64; + static constexpr Int64 minInt64 = Int64(~(UInt64(-1) / 2)); /// Maximum signed 64 bits int value that can be stored in a Json::Value. - static const Int64 maxInt64; + static constexpr Int64 maxInt64 = Int64(UInt64(-1) / 2); /// Maximum unsigned 64 bits int value that can be stored in a Json::Value. - static const UInt64 maxUInt64; + static constexpr UInt64 maxUInt64 = UInt64(-1); #endif // defined(JSON_HAS_INT64) - + /// Default precision for real value for string representation. + static constexpr UInt defaultRealPrecision = 17; + // The constant is hard-coded because some compiler have trouble + // converting Value::maxUInt64 to a double correctly (AIX/xlC). + // Assumes that UInt64 is a 64 bits integer. + static constexpr double maxUInt64AsDouble = 18446744073709551615.0; +// Workaround for bug in the NVIDIAs CUDA 9.1 nvcc compiler +// when using gcc and clang backend compilers. CZString +// cannot be defined as private. See issue #486 +#ifdef __NVCC__ +public: +#else private: +#endif #ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION class CZString { public: - enum DuplicationPolicy { - noDuplication = 0, - duplicate, - duplicateOnCopy - }; + enum DuplicationPolicy { noDuplication = 0, duplicate, duplicateOnCopy }; CZString(ArrayIndex index); CZString(char const* str, unsigned length, DuplicationPolicy allocate); CZString(CZString const& other); -#if JSON_HAS_RVALUE_REFERENCES CZString(CZString&& other); -#endif ~CZString(); CZString& operator=(const CZString& other); - -#if JSON_HAS_RVALUE_REFERENCES CZString& operator=(CZString&& other); -#endif bool operator<(CZString const& other) const; bool operator==(CZString const& other) const; ArrayIndex index() const; - //const char* c_str() const; ///< \deprecated + // const char* c_str() const; ///< \deprecated char const* data() const; unsigned length() const; bool isStaticString() const; @@ -699,11 +805,11 @@ private: void swap(CZString& other); struct StringStorage { - unsigned policy_: 2; - unsigned length_: 30; // 1GB max + unsigned policy_ : 2; + unsigned length_ : 30; // 1GB max }; - char const* cstr_; // actually, a prefixed string, unless policy is noDup + char const* cstr_; // actually, a prefixed string, unless policy is noDup union { ArrayIndex index_; StringStorage storage_; @@ -711,29 +817,26 @@ private: }; public: -#ifndef JSON_USE_CPPTL_SMALLMAP typedef std::map ObjectValues; -#else - typedef CppTL::SmallMap ObjectValues; -#endif // ifndef JSON_USE_CPPTL_SMALLMAP #endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION public: - /** \brief Create a default Value of the given type. - - This is a very useful constructor. - To create an empty array, pass arrayValue. - To create an empty object, pass objectValue. - Another Value can then be set to this one by assignment. -This is useful since clear() and resize() will not alter types. - - Examples: -\code -Json::Value null_value; // null -Json::Value arr_value(Json::arrayValue); // [] -Json::Value obj_value(Json::objectValue); // {} -\endcode - */ + /** + * \brief Create a default Value of the given type. + * + * This is a very useful constructor. + * To create an empty array, pass arrayValue. + * To create an empty object, pass objectValue. + * Another Value can then be set to this one by assignment. + * This is useful since clear() and resize() will not alter types. + * + * Examples: + * \code + * Json::Value null_value; // null + * Json::Value arr_value(Json::arrayValue); // [] + * Json::Value obj_value(Json::objectValue); // {} + * \endcode + */ Value(ValueType type = nullValue); Value(Int value); Value(UInt value); @@ -744,38 +847,35 @@ Json::Value obj_value(Json::objectValue); // {} Value(double value); Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.) Value(const char* begin, const char* end); ///< Copy all, incl zeroes. - /** \brief Constructs a value from a static string. - + /** + * \brief Constructs a value from a static string. + * * Like other value string constructor but do not duplicate the string for - * internal storage. The given string must remain alive after the call to this - * constructor. + * internal storage. The given string must remain alive after the call to + * this constructor. + * * \note This works only for null-terminated strings. (We cannot change the - * size of this class, so we have nowhere to store the length, - * which might be computed later for various operations.) + * size of this class, so we have nowhere to store the length, which might be + * computed later for various operations.) * * Example of usage: - * \code - * static StaticString foo("some text"); - * Json::Value aValue(foo); - * \endcode + * \code + * static StaticString foo("some text"); + * Json::Value aValue(foo); + * \endcode */ Value(const StaticString& value); - Value(const JSONCPP_STRING& value); ///< Copy data() til size(). Embedded zeroes too. -#ifdef JSON_USE_CPPTL - Value(const CppTL::ConstString& value); -#endif + Value(const String& value); Value(bool value); - /// Deep copy. + Value(std::nullptr_t ptr) = delete; Value(const Value& other); -#if JSON_HAS_RVALUE_REFERENCES - /// Move constructor Value(Value&& other); -#endif ~Value(); - /// Deep copy, then swap(other). - /// \note Over-write existing comments. To preserve comments, use #swapPayload(). - Value& operator=(Value other); + /// \note Overwrite existing comments. To preserve comments, use + /// #swapPayload(). + Value& operator=(const Value& other); + Value& operator=(Value&& other); /// Swap everything. void swap(Value& other); @@ -800,17 +900,14 @@ Json::Value obj_value(Json::objectValue); // {} const char* asCString() const; ///< Embedded zeroes could cause you trouble! #if JSONCPP_USING_SECURE_MEMORY - unsigned getCStringLength() const; //Allows you to understand the length of the CString + unsigned getCStringLength() const; // Allows you to understand the length of + // the CString #endif - JSONCPP_STRING asString() const; ///< Embedded zeroes are possible. + String asString() const; ///< Embedded zeroes are possible. /** Get raw char* of string-value. * \return false if !string. (Seg-fault if str or end are NULL.) */ - bool getString( - char const** begin, char const** end) const; -#ifdef JSON_USE_CPPTL - CppTL::ConstString asConstString() const; -#endif + bool getString(char const** begin, char const** end) const; Int asInt() const; UInt asUInt() const; #if defined(JSON_HAS_INT64) @@ -836,6 +933,10 @@ Json::Value obj_value(Json::objectValue); // {} bool isArray() const; bool isObject() const; + /// The `as` and `is` member function templates and specializations. + template T as() const JSONCPP_TEMPLATE_DELETE; + template bool is() const JSONCPP_TEMPLATE_DELETE; + bool isConvertibleTo(ValueType other) const; /// Number of values in array or object @@ -853,42 +954,33 @@ Json::Value obj_value(Json::objectValue); // {} /// \post type() is unchanged void clear(); - /// Resize the array to size elements. + /// Resize the array to newSize elements. /// New elements are initialized to null. /// May only be called on nullValue or arrayValue. /// \pre type() is arrayValue or nullValue /// \post type() is arrayValue - void resize(ArrayIndex size); + void resize(ArrayIndex newSize); - /// Access an array element (zero based index ). - /// If the array contains less than index element, then null value are - /// inserted - /// in the array so that its size is index+1. + //@{ + /// Access an array element (zero based index). If the array contains less + /// than index element, then null value are inserted in the array so that + /// its size is index+1. /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) + /// this from the operator[] which takes a string.) Value& operator[](ArrayIndex index); - - /// Access an array element (zero based index ). - /// If the array contains less than index element, then null value are - /// inserted - /// in the array so that its size is index+1. - /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) Value& operator[](int index); + //@} - /// Access an array element (zero based index ) + //@{ + /// Access an array element (zero based index). /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) + /// this from the operator[] which takes a string.) const Value& operator[](ArrayIndex index) const; - - /// Access an array element (zero based index ) - /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) const Value& operator[](int index) const; + //@} /// If the array contains at least index+1 elements, returns the element - /// value, - /// otherwise returns defaultValue. + /// value, otherwise returns defaultValue. Value get(ArrayIndex index, const Value& defaultValue) const; /// Return true if index < size(). bool isValidIndex(ArrayIndex index) const; @@ -896,61 +988,51 @@ Json::Value obj_value(Json::objectValue); // {} /// /// Equivalent to jsonvalue[jsonvalue.size()] = value; Value& append(const Value& value); - -#if JSON_HAS_RVALUE_REFERENCES Value& append(Value&& value); -#endif + + /// \brief Insert value in array at specific index + bool insert(ArrayIndex index, const Value& newValue); + bool insert(ArrayIndex index, Value&& newValue); /// Access an object value by name, create a null member if it does not exist. /// \note Because of our implementation, keys are limited to 2^30 -1 chars. - /// Exceeding that will cause an exception. + /// Exceeding that will cause an exception. Value& operator[](const char* key); /// Access an object value by name, returns null if there is no member with /// that name. const Value& operator[](const char* key) const; /// Access an object value by name, create a null member if it does not exist. /// \param key may contain embedded nulls. - Value& operator[](const JSONCPP_STRING& key); + Value& operator[](const String& key); /// Access an object value by name, returns null if there is no member with /// that name. /// \param key may contain embedded nulls. - const Value& operator[](const JSONCPP_STRING& key) const; + const Value& operator[](const String& key) const; /** \brief Access an object value by name, create a null member if it does not - exist. - - * If the object has no entry for that name, then the member name used to store - * the new entry is not duplicated. + * exist. + * + * If the object has no entry for that name, then the member name used to + * store the new entry is not duplicated. * Example of use: - * \code - * Json::Value object; - * static const StaticString code("code"); - * object[code] = 1234; - * \endcode + * \code + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode */ Value& operator[](const StaticString& key); -#ifdef JSON_USE_CPPTL - /// Access an object value by name, create a null member if it does not exist. - Value& operator[](const CppTL::ConstString& key); - /// Access an object value by name, returns null if there is no member with - /// that name. - const Value& operator[](const CppTL::ConstString& key) const; -#endif /// Return the member named key if it exist, defaultValue otherwise. /// \note deep copy Value get(const char* key, const Value& defaultValue) const; /// Return the member named key if it exist, defaultValue otherwise. /// \note deep copy /// \note key may contain embedded nulls. - Value get(const char* begin, const char* end, const Value& defaultValue) const; + Value get(const char* begin, const char* end, + const Value& defaultValue) const; /// Return the member named key if it exist, defaultValue otherwise. /// \note deep copy /// \param key may contain embedded nulls. - Value get(const JSONCPP_STRING& key, const Value& defaultValue) const; -#ifdef JSON_USE_CPPTL - /// Return the member named key if it exist, defaultValue otherwise. - /// \note deep copy - Value get(const CppTL::ConstString& key, const Value& defaultValue) const; -#endif + Value get(const String& key, const Value& defaultValue) const; /// Most general and efficient version of isMember()const, get()const, /// and operator[]const /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 @@ -958,51 +1040,44 @@ Json::Value obj_value(Json::objectValue); // {} /// Most general and efficient version of object-mutators. /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 /// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue. - Value const* demand(char const* begin, char const* end); + Value* demand(char const* begin, char const* end); /// \brief Remove and return the named member. /// /// Do nothing if it did not exist. - /// \return the removed Value, or null. /// \pre type() is objectValue or nullValue /// \post type() is unchanged - /// \deprecated void removeMember(const char* key); /// Same as removeMember(const char*) /// \param key may contain embedded nulls. - /// \deprecated - void removeMember(const JSONCPP_STRING& key); + void removeMember(const String& key); /// Same as removeMember(const char* begin, const char* end, Value* removed), /// but 'key' is null-terminated. bool removeMember(const char* key, Value* removed); /** \brief Remove the named map member. - - Update 'removed' iff removed. - \param key may contain embedded nulls. - \return true iff removed (no exceptions) - */ - bool removeMember(JSONCPP_STRING const& key, Value* removed); - /// Same as removeMember(JSONCPP_STRING const& key, Value* removed) + * + * Update 'removed' iff removed. + * \param key may contain embedded nulls. + * \return true iff removed (no exceptions) + */ + bool removeMember(String const& key, Value* removed); + /// Same as removeMember(String const& key, Value* removed) bool removeMember(const char* begin, const char* end, Value* removed); /** \brief Remove the indexed array element. - - O(n) expensive operations. - Update 'removed' iff removed. - \return true iff removed (no exceptions) - */ - bool removeIndex(ArrayIndex i, Value* removed); + * + * O(n) expensive operations. + * Update 'removed' iff removed. + * \return true if removed (no exceptions) + */ + bool removeIndex(ArrayIndex index, Value* removed); /// Return true if the object has a member named key. /// \note 'key' must be null-terminated. bool isMember(const char* key) const; /// Return true if the object has a member named key. /// \param key may contain embedded nulls. - bool isMember(const JSONCPP_STRING& key) const; - /// Same as isMember(JSONCPP_STRING const& key)const + bool isMember(const String& key) const; + /// Same as isMember(String const& key)const bool isMember(const char* begin, const char* end) const; -#ifdef JSON_USE_CPPTL - /// Return true if the object has a member named key. - bool isMember(const CppTL::ConstString& key) const; -#endif /// \brief Return a list of the member names. /// @@ -1011,23 +1086,22 @@ Json::Value obj_value(Json::objectValue); // {} /// \post if type() was nullValue, it remains nullValue Members getMemberNames() const; - //# ifdef JSON_USE_CPPTL - // EnumMemberNames enumMemberNames() const; - // EnumValues enumValues() const; - //# endif - /// \deprecated Always pass len. - JSONCPP_DEPRECATED("Use setComment(JSONCPP_STRING const&) instead.") - void setComment(const char* comment, CommentPlacement placement); + JSONCPP_DEPRECATED("Use setComment(String const&) instead.") + void setComment(const char* comment, CommentPlacement placement) { + setComment(String(comment, strlen(comment)), placement); + } /// Comments must be //... or /* ... */ - void setComment(const char* comment, size_t len, CommentPlacement placement); + void setComment(const char* comment, size_t len, CommentPlacement placement) { + setComment(String(comment, len), placement); + } /// Comments must be //... or /* ... */ - void setComment(const JSONCPP_STRING& comment, CommentPlacement placement); + void setComment(String comment, CommentPlacement placement); bool hasComment(CommentPlacement placement) const; /// Include delimiters and embedded newlines. - JSONCPP_STRING getComment(CommentPlacement placement) const; + String getComment(CommentPlacement placement) const; - JSONCPP_STRING toStyledString() const; + String toStyledString() const; const_iterator begin() const; const_iterator end() const; @@ -1043,20 +1117,20 @@ Json::Value obj_value(Json::objectValue); // {} ptrdiff_t getOffsetLimit() const; private: + void setType(ValueType v) { + bits_.value_type_ = static_cast(v); + } + bool isAllocated() const { return bits_.allocated_; } + void setIsAllocated(bool v) { bits_.allocated_ = v; } + void initBasic(ValueType type, bool allocated = false); + void dupPayload(const Value& other); + void releasePayload(); + void dupMeta(const Value& other); Value& resolveReference(const char* key); Value& resolveReference(const char* key, const char* end); - struct CommentInfo { - CommentInfo(); - ~CommentInfo(); - - void setComment(const char* text, size_t len); - - char* comment_; - }; - // struct MemberNamesTransform //{ // typedef const char *result_type; @@ -1071,13 +1145,33 @@ private: LargestUInt uint_; double real_; bool bool_; - char* string_; // actually ptr to unsigned, followed by str, unless !allocated_ + char* string_; // if allocated_, ptr to { unsigned, char[] }. ObjectValues* map_; } value_; - ValueType type_ : 8; - unsigned int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. - // If not allocated_, string_ must be null-terminated. - CommentInfo* comments_; + + struct { + // Really a ValueType, but types should agree for bitfield packing. + unsigned int value_type_ : 8; + // Unless allocated_, string_ must be null-terminated. + unsigned int allocated_ : 1; + } bits_; + + class Comments { + public: + Comments() = default; + Comments(const Comments& that); + Comments(Comments&& that); + Comments& operator=(const Comments& that); + Comments& operator=(Comments&& that); + bool has(CommentPlacement slot) const; + String get(CommentPlacement slot) const; + void set(CommentPlacement slot, String comment); + + private: + using Array = std::array; + std::unique_ptr ptr_; + }; + Comments comments_; // [start, limit) byte offsets in the source JSON text from which this Value // was extracted. @@ -1085,6 +1179,36 @@ private: ptrdiff_t limit_; }; +template <> inline bool Value::as() const { return asBool(); } +template <> inline bool Value::is() const { return isBool(); } + +template <> inline Int Value::as() const { return asInt(); } +template <> inline bool Value::is() const { return isInt(); } + +template <> inline UInt Value::as() const { return asUInt(); } +template <> inline bool Value::is() const { return isUInt(); } + +#if defined(JSON_HAS_INT64) +template <> inline Int64 Value::as() const { return asInt64(); } +template <> inline bool Value::is() const { return isInt64(); } + +template <> inline UInt64 Value::as() const { return asUInt64(); } +template <> inline bool Value::is() const { return isUInt64(); } +#endif + +template <> inline double Value::as() const { return asDouble(); } +template <> inline bool Value::is() const { return isDouble(); } + +template <> inline String Value::as() const { return asString(); } +template <> inline bool Value::is() const { return isString(); } + +/// These `as` specializations are type conversions, and do not have a +/// corresponding `is`. +template <> inline float Value::as() const { return asFloat(); } +template <> inline const char* Value::as() const { + return asCString(); +} + /** \brief Experimental and untested: represents an element of the "path" to * access a node. */ @@ -1095,17 +1219,13 @@ public: PathArgument(); PathArgument(ArrayIndex index); PathArgument(const char* key); - PathArgument(const JSONCPP_STRING& key); + PathArgument(String key); private: - enum Kind { - kindNone = 0, - kindIndex, - kindKey - }; - JSONCPP_STRING key_; - ArrayIndex index_; - Kind kind_; + enum Kind { kindNone = 0, kindIndex, kindKey }; + String key_; + ArrayIndex index_{}; + Kind kind_{kindNone}; }; /** \brief Experimental and untested: represents a "path" to access a node. @@ -1117,12 +1237,11 @@ private: * - ".name1.name2.name3" * - ".[0][1][2].name1[3]" * - ".%" => member name is provided as parameter - * - ".[%]" => index is provied as parameter + * - ".[%]" => index is provided as parameter */ class JSON_API Path { public: - Path(const JSONCPP_STRING& path, - const PathArgument& a1 = PathArgument(), + Path(const String& path, const PathArgument& a1 = PathArgument(), const PathArgument& a2 = PathArgument(), const PathArgument& a3 = PathArgument(), const PathArgument& a4 = PathArgument(), @@ -1135,15 +1254,13 @@ public: Value& make(Value& root) const; private: - typedef std::vector InArgs; - typedef std::vector Args; + using InArgs = std::vector; + using Args = std::vector; - void makePath(const JSONCPP_STRING& path, const InArgs& in); - void addPathInArg(const JSONCPP_STRING& path, - const InArgs& in, - InArgs::const_iterator& itInArg, - PathArgument::Kind kind); - void invalidPath(const JSONCPP_STRING& path, int location); + void makePath(const String& path, const InArgs& in); + void addPathInArg(const String& path, const InArgs& in, + InArgs::const_iterator& itInArg, PathArgument::Kind kind); + static void invalidPath(const String& path, int location); Args args_; }; @@ -1153,10 +1270,10 @@ private: */ class JSON_API ValueIteratorBase { public: - typedef std::bidirectional_iterator_tag iterator_category; - typedef unsigned int size_t; - typedef int difference_type; - typedef ValueIteratorBase SelfType; + using iterator_category = std::bidirectional_iterator_tag; + using size_t = unsigned int; + using difference_type = int; + using SelfType = ValueIteratorBase; bool operator==(const SelfType& other) const { return isEqual(other); } @@ -1170,17 +1287,19 @@ public: /// Value. Value key() const; - /// Return the index of the referenced Value, or -1 if it is not an arrayValue. + /// Return the index of the referenced Value, or -1 if it is not an + /// arrayValue. UInt index() const; /// Return the member name of the referenced Value, or "" if it is not an /// objectValue. /// \note Avoid `c_str()` on result, as embedded zeroes are possible. - JSONCPP_STRING name() const; + String name() const; /// Return the member name of the referenced Value. "" if it is not an /// objectValue. - /// \deprecated This cannot be used for UTF-8 strings, since there can be embedded nulls. + /// \deprecated This cannot be used for UTF-8 strings, since there can be + /// embedded nulls. JSONCPP_DEPRECATED("Use `key = name();` instead.") char const* memberName() const; /// Return the member name of the referenced Value, or NULL if it is not an @@ -1189,7 +1308,14 @@ public: char const* memberName(char const** end) const; protected: - Value& deref() const; + /*! Internal utility functions to assist with implementing + * other iterator functions. The const and non-const versions + * of the "deref" protected methods expose the protected + * current_ member variable in a way that can often be + * optimized away by the compiler. + */ + const Value& deref() const; + Value& deref(); void increment(); @@ -1204,7 +1330,7 @@ protected: private: Value::ObjectValues::iterator current_; // Indicates that iterator is for a null value. - bool isNull_; + bool isNull_{true}; public: // For some reason, BORLAND needs these at the end, rather @@ -1220,20 +1346,21 @@ class JSON_API ValueConstIterator : public ValueIteratorBase { friend class Value; public: - typedef const Value value_type; - //typedef unsigned int size_t; - //typedef int difference_type; - typedef const Value& reference; - typedef const Value* pointer; - typedef ValueConstIterator SelfType; + using value_type = const Value; + // typedef unsigned int size_t; + // typedef int difference_type; + using reference = const Value&; + using pointer = const Value*; + using SelfType = ValueConstIterator; ValueConstIterator(); ValueConstIterator(ValueIterator const& other); private: -/*! \internal Use by Value to create an iterator. - */ + /*! \internal Use by Value to create an iterator. + */ explicit ValueConstIterator(const Value::ObjectValues::iterator& current); + public: SelfType& operator=(const ValueIteratorBase& other); @@ -1270,21 +1397,22 @@ class JSON_API ValueIterator : public ValueIteratorBase { friend class Value; public: - typedef Value value_type; - typedef unsigned int size_t; - typedef int difference_type; - typedef Value& reference; - typedef Value* pointer; - typedef ValueIterator SelfType; + using value_type = Value; + using size_t = unsigned int; + using difference_type = int; + using reference = Value&; + using pointer = Value*; + using SelfType = ValueIterator; ValueIterator(); explicit ValueIterator(const ValueConstIterator& other); ValueIterator(const ValueIterator& other); private: -/*! \internal Use by Value to create an iterator. - */ + /*! \internal Use by Value to create an iterator. + */ explicit ValueIterator(const Value::ObjectValues::iterator& current); + public: SelfType& operator=(const SelfType& other); @@ -1310,27 +1438,26 @@ public: return *this; } - reference operator*() const { return deref(); } - - pointer operator->() const { return &deref(); } + /*! The return value of non-const iterators can be + * changed, so the these functions are not const + * because the returned references/pointers can be used + * to change state of the base class. + */ + reference operator*() { return deref(); } + pointer operator->() { return &deref(); } }; +inline void swap(Value& a, Value& b) { a.swap(b); } + } // namespace Json - -namespace std { -/// Specialize std::swap() for Json::Value. -template<> -inline void swap(Json::Value& a, Json::Value& b) { a.swap(b); } -} - #pragma pack(pop) #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #pragma warning(pop) #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) -#endif // CPPTL_JSON_H_INCLUDED +#endif // JSON_H_INCLUDED // ////////////////////////////////////////////////////////////////////// // End of content of file: include/json/value.h @@ -1350,18 +1477,18 @@ inline void swap(Json::Value& a, Json::Value& b) { a.swap(b); } // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE -#ifndef CPPTL_JSON_READER_H_INCLUDED -#define CPPTL_JSON_READER_H_INCLUDED +#ifndef JSON_READER_H_INCLUDED +#define JSON_READER_H_INCLUDED #if !defined(JSON_IS_AMALGAMATION) -#include "features.h" +#include "json_features.h" #include "value.h" #endif // if !defined(JSON_IS_AMALGAMATION) #include #include +#include #include #include -#include // Disable warning C4251: : needs to have dll-interface to // be used by... @@ -1375,132 +1502,130 @@ inline void swap(Json::Value& a, Json::Value& b) { a.swap(b); } namespace Json { /** \brief Unserialize a JSON document into a - *Value. + * Value. * * \deprecated Use CharReader and CharReaderBuilder. */ -class JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead") JSON_API Reader { + +class JSONCPP_DEPRECATED( + "Use CharReader and CharReaderBuilder instead.") JSON_API Reader { public: - typedef char Char; - typedef const Char* Location; + using Char = char; + using Location = const Char*; /** \brief An error tagged with where in the JSON text it was encountered. * * The offsets give the [start, limit) range of bytes within the text. Note * that this is bytes, not codepoints. - * */ struct StructuredError { ptrdiff_t offset_start; ptrdiff_t offset_limit; - JSONCPP_STRING message; + String message; }; - /** \brief Constructs a Reader allowing all features - * for parsing. + /** \brief Constructs a Reader allowing all features for parsing. */ + JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead") Reader(); - /** \brief Constructs a Reader allowing the specified feature set - * for parsing. + /** \brief Constructs a Reader allowing the specified feature set for parsing. */ + JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead") Reader(const Features& features); /** \brief Read a Value from a JSON * document. - * \param document UTF-8 encoded string containing the document to read. - * \param root [out] Contains the root value of the document if it was - * successfully parsed. - * \param collectComments \c true to collect comment and allow writing them - * back during - * serialization, \c false to discard comments. - * This parameter is ignored if - * Features::allowComments_ - * is \c false. + * + * \param document UTF-8 encoded string containing the document + * to read. + * \param[out] root Contains the root value of the document if it + * was successfully parsed. + * \param collectComments \c true to collect comment and allow writing + * them back during serialization, \c false to + * discard comments. This parameter is ignored + * if Features::allowComments_ is \c false. * \return \c true if the document was successfully parsed, \c false if an * error occurred. */ - bool - parse(const std::string& document, Value& root, bool collectComments = true); + bool parse(const std::string& document, Value& root, + bool collectComments = true); /** \brief Read a Value from a JSON - document. - * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the - document to read. - * \param endDoc Pointer on the end of the UTF-8 encoded string of the - document to read. - * Must be >= beginDoc. - * \param root [out] Contains the root value of the document if it was - * successfully parsed. - * \param collectComments \c true to collect comment and allow writing them - back during - * serialization, \c false to discard comments. - * This parameter is ignored if - Features::allowComments_ - * is \c false. + * document. + * + * \param beginDoc Pointer on the beginning of the UTF-8 encoded + * string of the document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string + * of the document to read. Must be >= beginDoc. + * \param[out] root Contains the root value of the document if it + * was successfully parsed. + * \param collectComments \c true to collect comment and allow writing + * them back during serialization, \c false to + * discard comments. This parameter is ignored + * if Features::allowComments_ is \c false. * \return \c true if the document was successfully parsed, \c false if an - error occurred. + * error occurred. */ - bool parse(const char* beginDoc, - const char* endDoc, - Value& root, + bool parse(const char* beginDoc, const char* endDoc, Value& root, bool collectComments = true); /// \brief Parse from input stream. /// \see Json::operator>>(std::istream&, Json::Value&). - bool parse(JSONCPP_ISTREAM& is, Value& root, bool collectComments = true); + bool parse(IStream& is, Value& root, bool collectComments = true); /** \brief Returns a user friendly string that list errors in the parsed * document. - * \return Formatted error message with the list of errors with their location - * in - * the parsed document. An empty string is returned if no error - * occurred - * during parsing. + * + * \return Formatted error message with the list of errors with their + * location in the parsed document. An empty string is returned if no error + * occurred during parsing. * \deprecated Use getFormattedErrorMessages() instead (typo fix). */ JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.") - JSONCPP_STRING getFormatedErrorMessages() const; + String getFormatedErrorMessages() const; /** \brief Returns a user friendly string that list errors in the parsed * document. - * \return Formatted error message with the list of errors with their location - * in - * the parsed document. An empty string is returned if no error - * occurred - * during parsing. + * + * \return Formatted error message with the list of errors with their + * location in the parsed document. An empty string is returned if no error + * occurred during parsing. */ - JSONCPP_STRING getFormattedErrorMessages() const; + String getFormattedErrorMessages() const; - /** \brief Returns a vector of structured erros encounted while parsing. + /** \brief Returns a vector of structured errors encountered while parsing. + * * \return A (possibly empty) vector of StructuredError objects. Currently - * only one error can be returned, but the caller should tolerate - * multiple - * errors. This can occur if the parser recovers from a non-fatal - * parse error and then encounters additional errors. + * only one error can be returned, but the caller should tolerate multiple + * errors. This can occur if the parser recovers from a non-fatal parse + * error and then encounters additional errors. */ std::vector getStructuredErrors() const; /** \brief Add a semantic error message. - * \param value JSON Value location associated with the error + * + * \param value JSON Value location associated with the error * \param message The error message. - * \return \c true if the error was successfully added, \c false if the - * Value offset exceeds the document size. + * \return \c true if the error was successfully added, \c false if the Value + * offset exceeds the document size. */ - bool pushError(const Value& value, const JSONCPP_STRING& message); + bool pushError(const Value& value, const String& message); /** \brief Add a semantic error message with extra context. - * \param value JSON Value location associated with the error + * + * \param value JSON Value location associated with the error * \param message The error message. - * \param extra Additional JSON Value location to contextualize the error + * \param extra Additional JSON Value location to contextualize the error * \return \c true if the error was successfully added, \c false if either * Value offset exceeds the document size. */ - bool pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra); + bool pushError(const Value& value, const String& message, const Value& extra); /** \brief Return whether there are any errors. - * \return \c true if there are no errors to report \c false if - * errors have occurred. + * + * \return \c true if there are no errors to report \c false if errors have + * occurred. */ bool good() const; @@ -1532,15 +1657,15 @@ private: class ErrorInfo { public: Token token_; - JSONCPP_STRING message_; + String message_; Location extra_; }; - typedef std::deque Errors; + using Errors = std::deque; bool readToken(Token& token); void skipSpaces(); - bool match(Location pattern, int patternLength); + bool match(const Char* pattern, int patternLength); bool readComment(); bool readCStyleComment(); bool readCppStyleComment(); @@ -1552,142 +1677,138 @@ private: bool decodeNumber(Token& token); bool decodeNumber(Token& token, Value& decoded); bool decodeString(Token& token); - bool decodeString(Token& token, JSONCPP_STRING& decoded); + bool decodeString(Token& token, String& decoded); bool decodeDouble(Token& token); bool decodeDouble(Token& token, Value& decoded); - bool decodeUnicodeCodePoint(Token& token, - Location& current, - Location end, + bool decodeUnicodeCodePoint(Token& token, Location& current, Location end, unsigned int& unicode); - bool decodeUnicodeEscapeSequence(Token& token, - Location& current, - Location end, - unsigned int& unicode); - bool addError(const JSONCPP_STRING& message, Token& token, Location extra = 0); + bool decodeUnicodeEscapeSequence(Token& token, Location& current, + Location end, unsigned int& unicode); + bool addError(const String& message, Token& token, Location extra = nullptr); bool recoverFromError(TokenType skipUntilToken); - bool addErrorAndRecover(const JSONCPP_STRING& message, - Token& token, + bool addErrorAndRecover(const String& message, Token& token, TokenType skipUntilToken); void skipUntilSpace(); Value& currentValue(); Char getNextChar(); - void - getLocationLineAndColumn(Location location, int& line, int& column) const; - JSONCPP_STRING getLocationLineAndColumn(Location location) const; + void getLocationLineAndColumn(Location location, int& line, + int& column) const; + String getLocationLineAndColumn(Location location) const; void addComment(Location begin, Location end, CommentPlacement placement); void skipCommentTokens(Token& token); static bool containsNewLine(Location begin, Location end); - static JSONCPP_STRING normalizeEOL(Location begin, Location end); + static String normalizeEOL(Location begin, Location end); - typedef std::stack Nodes; + using Nodes = std::stack; Nodes nodes_; Errors errors_; - JSONCPP_STRING document_; - Location begin_; - Location end_; - Location current_; - Location lastValueEnd_; - Value* lastValue_; - JSONCPP_STRING commentsBefore_; + String document_; + Location begin_{}; + Location end_{}; + Location current_{}; + Location lastValueEnd_{}; + Value* lastValue_{}; + String commentsBefore_; Features features_; - bool collectComments_; -}; // Reader + bool collectComments_{}; +}; // Reader /** Interface for reading JSON from a char array. */ class JSON_API CharReader { public: - virtual ~CharReader() {} + virtual ~CharReader() = default; /** \brief Read a Value from a JSON - document. - * The document must be a UTF-8 encoded string containing the document to read. + * document. The document must be a UTF-8 encoded string containing the + * document to read. * - * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the - document to read. - * \param endDoc Pointer on the end of the UTF-8 encoded string of the - document to read. - * Must be >= beginDoc. - * \param root [out] Contains the root value of the document if it was - * successfully parsed. - * \param errs [out] Formatted error messages (if not NULL) - * a user friendly string that lists errors in the parsed - * document. + * \param beginDoc Pointer on the beginning of the UTF-8 encoded string + * of the document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string of the + * document to read. Must be >= beginDoc. + * \param[out] root Contains the root value of the document if it was + * successfully parsed. + * \param[out] errs Formatted error messages (if not NULL) a user + * friendly string that lists errors in the parsed + * document. * \return \c true if the document was successfully parsed, \c false if an - error occurred. + * error occurred. */ - virtual bool parse( - char const* beginDoc, char const* endDoc, - Value* root, JSONCPP_STRING* errs) = 0; + virtual bool parse(char const* beginDoc, char const* endDoc, Value* root, + String* errs) = 0; class JSON_API Factory { public: - virtual ~Factory() {} + virtual ~Factory() = default; /** \brief Allocate a CharReader via operator new(). * \throw std::exception if something goes wrong (e.g. invalid settings) */ virtual CharReader* newCharReader() const = 0; - }; // Factory -}; // CharReader + }; // Factory +}; // CharReader /** \brief Build a CharReader implementation. - -Usage: -\code - using namespace Json; - CharReaderBuilder builder; - builder["collectComments"] = false; - Value value; - JSONCPP_STRING errs; - bool ok = parseFromStream(builder, std::cin, &value, &errs); -\endcode -*/ + * + * Usage: + * \code + * using namespace Json; + * CharReaderBuilder builder; + * builder["collectComments"] = false; + * Value value; + * String errs; + * bool ok = parseFromStream(builder, std::cin, &value, &errs); + * \endcode + */ class JSON_API CharReaderBuilder : public CharReader::Factory { public: // Note: We use a Json::Value so that we can add data-members to this class // without a major version bump. /** Configuration of this builder. - These are case-sensitive. - Available settings (case-sensitive): - - `"collectComments": false or true` - - true to collect comment and allow writing them - back during serialization, false to discard comments. - This parameter is ignored if allowComments is false. - - `"allowComments": false or true` - - true if comments are allowed. - - `"strictRoot": false or true` - - true if root must be either an array or an object value - - `"allowDroppedNullPlaceholders": false or true` - - true if dropped null placeholders are allowed. (See StreamWriterBuilder.) - - `"allowNumericKeys": false or true` - - true if numeric object keys are allowed. - - `"allowSingleQuotes": false or true` - - true if '' are allowed for strings (both keys and values) - - `"stackLimit": integer` - - Exceeding stackLimit (recursive depth of `readValue()`) will - cause an exception. - - This is a security issue (seg-faults caused by deeply nested JSON), - so the default is low. - - `"failIfExtra": false or true` - - If true, `parse()` returns false when extra non-whitespace trails - the JSON value in the input string. - - `"rejectDupKeys": false or true` - - If true, `parse()` returns false when a key is duplicated within an object. - - `"allowSpecialFloats": false or true` - - If true, special float values (NaNs and infinities) are allowed - and their values are lossfree restorable. - - You can examine 'settings_` yourself - to see the defaults. You can also write and read them just like any - JSON Value. - \sa setDefaults() - */ + * These are case-sensitive. + * Available settings (case-sensitive): + * - `"collectComments": false or true` + * - true to collect comment and allow writing them back during + * serialization, false to discard comments. This parameter is ignored + * if allowComments is false. + * - `"allowComments": false or true` + * - true if comments are allowed. + * - `"allowTrailingCommas": false or true` + * - true if trailing commas in objects and arrays are allowed. + * - `"strictRoot": false or true` + * - true if root must be either an array or an object value + * - `"allowDroppedNullPlaceholders": false or true` + * - true if dropped null placeholders are allowed. (See + * StreamWriterBuilder.) + * - `"allowNumericKeys": false or true` + * - true if numeric object keys are allowed. + * - `"allowSingleQuotes": false or true` + * - true if '' are allowed for strings (both keys and values) + * - `"stackLimit": integer` + * - Exceeding stackLimit (recursive depth of `readValue()`) will cause an + * exception. + * - This is a security issue (seg-faults caused by deeply nested JSON), so + * the default is low. + * - `"failIfExtra": false or true` + * - If true, `parse()` returns false when extra non-whitespace trails the + * JSON value in the input string. + * - `"rejectDupKeys": false or true` + * - If true, `parse()` returns false when a key is duplicated within an + * object. + * - `"allowSpecialFloats": false or true` + * - If true, special float values (NaNs and infinities) are allowed and + * their values are lossfree restorable. + * + * You can examine 'settings_` yourself to see the defaults. You can also + * write and read them just like any JSON Value. + * \sa setDefaults() + */ Json::Value settings_; CharReaderBuilder(); - ~CharReaderBuilder() JSONCPP_OVERRIDE; + ~CharReaderBuilder() override; - CharReader* newCharReader() const JSONCPP_OVERRIDE; + CharReader* newCharReader() const override; /** \return true if 'settings' are legal and consistent; * otherwise, indicate bad settings via 'invalid'. @@ -1696,7 +1817,7 @@ public: /** A simple way to update a specific setting. */ - Value& operator[](JSONCPP_STRING key); + Value& operator[](const String& key); /** Called by ctor, but you can use this to reset settings_. * \pre 'settings' != NULL (but Json::null is fine) @@ -1713,39 +1834,37 @@ public: }; /** Consume entire stream and use its begin/end. - * Someday we might have a real StreamReader, but for now this - * is convenient. - */ -bool JSON_API parseFromStream( - CharReader::Factory const&, - JSONCPP_ISTREAM&, - Value* root, std::string* errs); + * Someday we might have a real StreamReader, but for now this + * is convenient. + */ +bool JSON_API parseFromStream(CharReader::Factory const&, IStream&, Value* root, + String* errs); /** \brief Read from 'sin' into 'root'. - - Always keep comments from the input JSON. - - This can be used to read a file into a particular sub-object. - For example: - \code - Json::Value root; - cin >> root["dir"]["file"]; - cout << root; - \endcode - Result: - \verbatim - { - "dir": { - "file": { - // The input stream JSON would be nested here. - } - } - } - \endverbatim - \throw std::exception on parse error. - \see Json::operator<<() -*/ -JSON_API JSONCPP_ISTREAM& operator>>(JSONCPP_ISTREAM&, Value&); + * + * Always keep comments from the input JSON. + * + * This can be used to read a file into a particular sub-object. + * For example: + * \code + * Json::Value root; + * cin >> root["dir"]["file"]; + * cout << root; + * \endcode + * Result: + * \verbatim + * { + * "dir": { + * "file": { + * // The input stream JSON would be nested here. + * } + * } + * } + * \endverbatim + * \throw std::exception on parse error. + * \see Json::operator<<() + */ +JSON_API IStream& operator>>(IStream&, Value&); } // namespace Json @@ -1755,7 +1874,7 @@ JSON_API JSONCPP_ISTREAM& operator>>(JSONCPP_ISTREAM&, Value&); #pragma warning(pop) #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) -#endif // CPPTL_JSON_READER_H_INCLUDED +#endif // JSON_READER_H_INCLUDED // ////////////////////////////////////////////////////////////////////// // End of content of file: include/json/reader.h @@ -1781,9 +1900,9 @@ JSON_API JSONCPP_ISTREAM& operator>>(JSONCPP_ISTREAM&, Value&); #if !defined(JSON_IS_AMALGAMATION) #include "value.h" #endif // if !defined(JSON_IS_AMALGAMATION) -#include -#include #include +#include +#include // Disable warning C4251: : needs to have dll-interface to // be used by... @@ -1799,31 +1918,31 @@ namespace Json { class Value; /** - -Usage: -\code - using namespace Json; - void writeToStdout(StreamWriter::Factory const& factory, Value const& value) { - std::unique_ptr const writer( - factory.newStreamWriter()); - writer->write(value, &std::cout); - std::cout << std::endl; // add lf and flush - } -\endcode -*/ + * + * Usage: + * \code + * using namespace Json; + * void writeToStdout(StreamWriter::Factory const& factory, Value const& value) + * { std::unique_ptr const writer( factory.newStreamWriter()); + * writer->write(value, &std::cout); + * std::cout << std::endl; // add lf and flush + * } + * \endcode + */ class JSON_API StreamWriter { protected: - JSONCPP_OSTREAM* sout_; // not owned; will not delete + OStream* sout_; // not owned; will not delete public: StreamWriter(); virtual ~StreamWriter(); /** Write Value into document as configured in sub-class. - Do not take ownership of sout, but maintain a reference during function. - \pre sout != NULL - \return zero on success (For now, we always return zero, so check the stream instead.) - \throw std::exception possibly, depending on configuration + * Do not take ownership of sout, but maintain a reference during function. + * \pre sout != NULL + * \return zero on success (For now, we always return zero, so check the + * stream instead.) \throw std::exception possibly, depending on + * configuration */ - virtual int write(Value const& root, JSONCPP_OSTREAM* sout) = 0; + virtual int write(Value const& root, OStream* sout) = 0; /** \brief A simple abstract factory. */ @@ -1834,64 +1953,69 @@ public: * \throw std::exception if something goes wrong (e.g. invalid settings) */ virtual StreamWriter* newStreamWriter() const = 0; - }; // Factory -}; // StreamWriter + }; // Factory +}; // StreamWriter /** \brief Write into stringstream, then return string, for convenience. * A StreamWriter will be created from the factory, used, and then deleted. */ -JSONCPP_STRING JSON_API writeString(StreamWriter::Factory const& factory, Value const& root); - +String JSON_API writeString(StreamWriter::Factory const& factory, + Value const& root); /** \brief Build a StreamWriter implementation. -Usage: -\code - using namespace Json; - Value value = ...; - StreamWriterBuilder builder; - builder["commentStyle"] = "None"; - builder["indentation"] = " "; // or whatever you like - std::unique_ptr writer( - builder.newStreamWriter()); - writer->write(value, &std::cout); - std::cout << std::endl; // add lf and flush -\endcode +* Usage: +* \code +* using namespace Json; +* Value value = ...; +* StreamWriterBuilder builder; +* builder["commentStyle"] = "None"; +* builder["indentation"] = " "; // or whatever you like +* std::unique_ptr writer( +* builder.newStreamWriter()); +* writer->write(value, &std::cout); +* std::cout << std::endl; // add lf and flush +* \endcode */ class JSON_API StreamWriterBuilder : public StreamWriter::Factory { public: // Note: We use a Json::Value so that we can add data-members to this class // without a major version bump. /** Configuration of this builder. - Available settings (case-sensitive): - - "commentStyle": "None" or "All" - - "indentation": "" - - "enableYAMLCompatibility": false or true - - slightly change the whitespace around colons - - "dropNullPlaceholders": false or true - - Drop the "null" string from the writer's output for nullValues. - Strictly speaking, this is not valid JSON. But when the output is being - fed to a browser's JavaScript, it makes for smaller output and the - browser can handle the output just fine. - - "useSpecialFloats": false or true - - If true, outputs non-finite floating point values in the following way: - NaN values as "NaN", positive infinity as "Infinity", and negative infinity - as "-Infinity". + * Available settings (case-sensitive): + * - "commentStyle": "None" or "All" + * - "indentation": "". + * - Setting this to an empty string also omits newline characters. + * - "enableYAMLCompatibility": false or true + * - slightly change the whitespace around colons + * - "dropNullPlaceholders": false or true + * - Drop the "null" string from the writer's output for nullValues. + * Strictly speaking, this is not valid JSON. But when the output is being + * fed to a browser's JavaScript, it makes for smaller output and the + * browser can handle the output just fine. + * - "useSpecialFloats": false or true + * - If true, outputs non-finite floating point values in the following way: + * NaN values as "NaN", positive infinity as "Infinity", and negative + * infinity as "-Infinity". + * - "precision": int + * - Number of precision digits for formatting of real values. + * - "precisionType": "significant"(default) or "decimal" + * - Type of precision for formatting of real values. - You can examine 'settings_` yourself - to see the defaults. You can also write and read them just like any - JSON Value. - \sa setDefaults() - */ + * You can examine 'settings_` yourself + * to see the defaults. You can also write and read them just like any + * JSON Value. + * \sa setDefaults() + */ Json::Value settings_; StreamWriterBuilder(); - ~StreamWriterBuilder() JSONCPP_OVERRIDE; + ~StreamWriterBuilder() override; /** * \throw std::exception if something goes wrong (e.g. invalid settings) */ - StreamWriter* newStreamWriter() const JSONCPP_OVERRIDE; + StreamWriter* newStreamWriter() const override; /** \return true if 'settings' are legal and consistent; * otherwise, indicate bad settings via 'invalid'. @@ -1899,7 +2023,7 @@ public: bool validate(Json::Value* invalid) const; /** A simple way to update a specific setting. */ - Value& operator[](JSONCPP_STRING key); + Value& operator[](const String& key); /** Called by ctor, but you can use this to reset settings_. * \pre 'settings' != NULL (but Json::null is fine) @@ -1916,7 +2040,7 @@ class JSONCPP_DEPRECATED("Use StreamWriter instead") JSON_API Writer { public: virtual ~Writer(); - virtual JSONCPP_STRING write(const Value& root) = 0; + virtual String write(const Value& root) = 0; }; /** \brief Outputs a Value in JSON format @@ -1924,18 +2048,19 @@ public: * * The JSON document is written in a single line. It is not intended for 'human' *consumption, - * but may be usefull to support feature such as RPC where bandwith is limited. + * but may be useful to support feature such as RPC where bandwidth is limited. * \sa Reader, Value * \deprecated Use StreamWriterBuilder. */ #if defined(_MSC_VER) #pragma warning(push) -#pragma warning(disable:4996) // Deriving from deprecated class +#pragma warning(disable : 4996) // Deriving from deprecated class #endif -class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API FastWriter : public Writer { +class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API FastWriter + : public Writer { public: FastWriter(); - ~FastWriter() JSONCPP_OVERRIDE {} + ~FastWriter() override = default; void enableYAMLCompatibility(); @@ -1949,15 +2074,15 @@ public: void omitEndingLineFeed(); public: // overridden from Writer - JSONCPP_STRING write(const Value& root) JSONCPP_OVERRIDE; + String write(const Value& root) override; private: void writeValue(const Value& value); - JSONCPP_STRING document_; - bool yamlCompatibilityEnabled_; - bool dropNullPlaceholders_; - bool omitEndingLineFeed_; + String document_; + bool yamlCompatibilityEnabled_{false}; + bool dropNullPlaceholders_{false}; + bool omitEndingLineFeed_{false}; }; #if defined(_MSC_VER) #pragma warning(pop) @@ -1989,42 +2114,43 @@ private: */ #if defined(_MSC_VER) #pragma warning(push) -#pragma warning(disable:4996) // Deriving from deprecated class +#pragma warning(disable : 4996) // Deriving from deprecated class #endif -class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API StyledWriter : public Writer { +class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API + StyledWriter : public Writer { public: StyledWriter(); - ~StyledWriter() JSONCPP_OVERRIDE {} + ~StyledWriter() override = default; public: // overridden from Writer /** \brief Serialize a Value in JSON format. * \param root Value to serialize. * \return String containing the JSON document that represents the root value. */ - JSONCPP_STRING write(const Value& root) JSONCPP_OVERRIDE; + String write(const Value& root) override; private: void writeValue(const Value& value); void writeArrayValue(const Value& value); bool isMultilineArray(const Value& value); - void pushValue(const JSONCPP_STRING& value); + void pushValue(const String& value); void writeIndent(); - void writeWithIndent(const JSONCPP_STRING& value); + void writeWithIndent(const String& value); void indent(); void unindent(); void writeCommentBeforeValue(const Value& root); void writeCommentAfterValueOnSameLine(const Value& root); - bool hasCommentForValue(const Value& value); - static JSONCPP_STRING normalizeEOL(const JSONCPP_STRING& text); + static bool hasCommentForValue(const Value& value); + static String normalizeEOL(const String& text); - typedef std::vector ChildValues; + using ChildValues = std::vector; ChildValues childValues_; - JSONCPP_STRING document_; - JSONCPP_STRING indentString_; - unsigned int rightMargin_; - unsigned int indentSize_; - bool addChildValues_; + String document_; + String indentString_; + unsigned int rightMargin_{74}; + unsigned int indentSize_{3}; + bool addChildValues_{false}; }; #if defined(_MSC_VER) #pragma warning(pop) @@ -2057,15 +2183,16 @@ private: */ #if defined(_MSC_VER) #pragma warning(push) -#pragma warning(disable:4996) // Deriving from deprecated class +#pragma warning(disable : 4996) // Deriving from deprecated class #endif -class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API StyledStreamWriter { +class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API + StyledStreamWriter { public: -/** - * \param indentation Each level will be indented by this amount extra. - */ - StyledStreamWriter(JSONCPP_STRING indentation = "\t"); - ~StyledStreamWriter() {} + /** + * \param indentation Each level will be indented by this amount extra. + */ + StyledStreamWriter(String indentation = "\t"); + ~StyledStreamWriter() = default; public: /** \brief Serialize a Value in JSON format. @@ -2074,29 +2201,29 @@ public: * \note There is no point in deriving from Writer, since write() should not * return a value. */ - void write(JSONCPP_OSTREAM& out, const Value& root); + void write(OStream& out, const Value& root); private: void writeValue(const Value& value); void writeArrayValue(const Value& value); bool isMultilineArray(const Value& value); - void pushValue(const JSONCPP_STRING& value); + void pushValue(const String& value); void writeIndent(); - void writeWithIndent(const JSONCPP_STRING& value); + void writeWithIndent(const String& value); void indent(); void unindent(); void writeCommentBeforeValue(const Value& root); void writeCommentAfterValueOnSameLine(const Value& root); - bool hasCommentForValue(const Value& value); - static JSONCPP_STRING normalizeEOL(const JSONCPP_STRING& text); + static bool hasCommentForValue(const Value& value); + static String normalizeEOL(const String& text); - typedef std::vector ChildValues; + using ChildValues = std::vector; ChildValues childValues_; - JSONCPP_OSTREAM* document_; - JSONCPP_STRING indentString_; - unsigned int rightMargin_; - JSONCPP_STRING indentation_; + OStream* document_; + String indentString_; + unsigned int rightMargin_{74}; + String indentation_; bool addChildValues_ : 1; bool indented_ : 1; }; @@ -2105,18 +2232,20 @@ private: #endif #if defined(JSON_HAS_INT64) -JSONCPP_STRING JSON_API valueToString(Int value); -JSONCPP_STRING JSON_API valueToString(UInt value); +String JSON_API valueToString(Int value); +String JSON_API valueToString(UInt value); #endif // if defined(JSON_HAS_INT64) -JSONCPP_STRING JSON_API valueToString(LargestInt value); -JSONCPP_STRING JSON_API valueToString(LargestUInt value); -JSONCPP_STRING JSON_API valueToString(double value); -JSONCPP_STRING JSON_API valueToString(bool value); -JSONCPP_STRING JSON_API valueToQuotedString(const char* value); +String JSON_API valueToString(LargestInt value); +String JSON_API valueToString(LargestUInt value); +String JSON_API valueToString( + double value, unsigned int precision = Value::defaultRealPrecision, + PrecisionType precisionType = PrecisionType::significantDigits); +String JSON_API valueToString(bool value); +String JSON_API valueToQuotedString(const char* value); /// \brief Output using the StyledStreamWriter. /// \see Json::operator>>() -JSON_API JSONCPP_OSTREAM& operator<<(JSONCPP_OSTREAM&, const Value& root); +JSON_API OStream& operator<<(OStream&, const Value& root); } // namespace Json @@ -2146,10 +2275,10 @@ JSON_API JSONCPP_OSTREAM& operator<<(JSONCPP_OSTREAM&, const Value& root); // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE -#ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED -#define CPPTL_JSON_ASSERTIONS_H_INCLUDED +#ifndef JSON_ASSERTIONS_H_INCLUDED +#define JSON_ASSERTIONS_H_INCLUDED -#include +#include #include #if !defined(JSON_IS_AMALGAMATION) @@ -2163,38 +2292,45 @@ JSON_API JSONCPP_OSTREAM& operator<<(JSONCPP_OSTREAM&, const Value& root); #if JSON_USE_EXCEPTION // @todo <= add detail about condition in exception -# define JSON_ASSERT(condition) \ - {if (!(condition)) {Json::throwLogicError( "assert json failed" );}} +#define JSON_ASSERT(condition) \ + do { \ + if (!(condition)) { \ + Json::throwLogicError("assert json failed"); \ + } \ + } while (0) -# define JSON_FAIL_MESSAGE(message) \ - { \ - JSONCPP_OSTRINGSTREAM oss; oss << message; \ +#define JSON_FAIL_MESSAGE(message) \ + do { \ + OStringStream oss; \ + oss << message; \ Json::throwLogicError(oss.str()); \ abort(); \ - } + } while (0) #else // JSON_USE_EXCEPTION -# define JSON_ASSERT(condition) assert(condition) +#define JSON_ASSERT(condition) assert(condition) // The call to assert() will show the failure message in debug builds. In // release builds we abort, for a core-dump or debugger. -# define JSON_FAIL_MESSAGE(message) \ +#define JSON_FAIL_MESSAGE(message) \ { \ - JSONCPP_OSTRINGSTREAM oss; oss << message; \ + OStringStream oss; \ + oss << message; \ assert(false && oss.str().c_str()); \ abort(); \ } - #endif #define JSON_ASSERT_MESSAGE(condition, message) \ - if (!(condition)) { \ - JSON_FAIL_MESSAGE(message); \ - } + do { \ + if (!(condition)) { \ + JSON_FAIL_MESSAGE(message); \ + } \ + } while (0) -#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED +#endif // JSON_ASSERTIONS_H_INCLUDED // ////////////////////////////////////////////////////////////////////// // End of content of file: include/json/assertions.h diff --git a/lib/jsoncpp/jsoncpp.cpp b/lib/jsoncpp/jsoncpp.cpp index 507a1c6ad..30e0a69f7 100644 --- a/lib/jsoncpp/jsoncpp.cpp +++ b/lib/jsoncpp/jsoncpp.cpp @@ -92,6 +92,9 @@ license you like. #ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED #define LIB_JSONCPP_JSON_TOOL_H_INCLUDED +#if !defined(JSON_IS_AMALGAMATION) +#include +#endif // Also support old flag NO_LOCALE_SUPPORT #ifdef NO_LOCALE_SUPPORT @@ -109,7 +112,7 @@ license you like. */ namespace Json { -static char getDecimalPoint() { +static inline char getDecimalPoint() { #ifdef JSONCPP_NO_LOCALE_SUPPORT return '\0'; #else @@ -119,8 +122,8 @@ static char getDecimalPoint() { } /// Converts a unicode code-point to UTF-8. -static inline JSONCPP_STRING codePointToUTF8(unsigned int cp) { - JSONCPP_STRING result; +static inline String codePointToUTF8(unsigned int cp) { + String result; // based on description from http://en.wikipedia.org/wiki/UTF-8 @@ -154,7 +157,7 @@ enum { }; // Defines a char buffer for use with uintToString(). -typedef char UIntToStringBuffer[uintToStringBufferSize]; +using UIntToStringBuffer = char[uintToStringBufferSize]; /** Converts an unsigned integer to string. * @param value Unsigned integer to convert to string @@ -174,28 +177,45 @@ static inline void uintToString(LargestUInt value, char*& current) { * We had a sophisticated way, but it did not work in WinCE. * @see https://github.com/open-source-parsers/jsoncpp/pull/9 */ -static inline void fixNumericLocale(char* begin, char* end) { - while (begin < end) { +template Iter fixNumericLocale(Iter begin, Iter end) { + for (; begin != end; ++begin) { if (*begin == ',') { *begin = '.'; } - ++begin; } + return begin; } -static inline void fixNumericLocaleInput(char* begin, char* end) { +template void fixNumericLocaleInput(Iter begin, Iter end) { char decimalPoint = getDecimalPoint(); - if (decimalPoint != '\0' && decimalPoint != '.') { - while (begin < end) { - if (*begin == '.') { - *begin = decimalPoint; - } - ++begin; + if (decimalPoint == '\0' || decimalPoint == '.') { + return; + } + for (; begin != end; ++begin) { + if (*begin == '.') { + *begin = decimalPoint; } } } -} // namespace Json { +/** + * Return iterator that would be the new end of the range [begin,end), if we + * were to delete zeros in the end of string, but not the last zero before '.'. + */ +template Iter fixZerosInTheEnd(Iter begin, Iter end) { + for (; begin != end; --end) { + if (*(end - 1) != '0') { + return end; + } + // Don't delete the last zero before the decimal point. + if (begin != (end - 1) && *(end - 2) == '.') { + return end; + } + } + return end; +} + +} // namespace Json #endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED @@ -219,69 +239,65 @@ static inline void fixNumericLocaleInput(char* begin, char* end) { // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #if !defined(JSON_IS_AMALGAMATION) +#include "json_tool.h" #include #include #include -#include "json_tool.h" #endif // if !defined(JSON_IS_AMALGAMATION) -#include -#include +#include #include #include +#include #include -#include +#include #include #include -#include +#include +#include -#if defined(_MSC_VER) -#if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above -#define snprintf sprintf_s -#elif _MSC_VER >= 1900 // VC++ 14.0 and above -#define snprintf std::snprintf -#else -#define snprintf _snprintf -#endif -#elif defined(__ANDROID__) || defined(__QNXNTO__) -#define snprintf snprintf -#elif __cplusplus >= 201103L -#if !defined(__MINGW32__) && !defined(__CYGWIN__) -#define snprintf std::snprintf -#endif -#endif +#include +#if __cplusplus >= 201103L -#if defined(__QNXNTO__) +#if !defined(sscanf) #define sscanf std::sscanf #endif -#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 +#endif //__cplusplus + +#if defined(_MSC_VER) +#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES) +#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1 +#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES +#endif //_MSC_VER + +#if defined(_MSC_VER) // Disable warning about strdup being deprecated. #pragma warning(disable : 4996) #endif -// Define JSONCPP_DEPRECATED_STACK_LIMIT as an appropriate integer at compile time to change the stack limit +// Define JSONCPP_DEPRECATED_STACK_LIMIT as an appropriate integer at compile +// time to change the stack limit #if !defined(JSONCPP_DEPRECATED_STACK_LIMIT) #define JSONCPP_DEPRECATED_STACK_LIMIT 1000 #endif -static size_t const stackLimit_g = JSONCPP_DEPRECATED_STACK_LIMIT; // see readValue() +static size_t const stackLimit_g = + JSONCPP_DEPRECATED_STACK_LIMIT; // see readValue() namespace Json { #if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) -typedef std::unique_ptr CharReaderPtr; +using CharReaderPtr = std::unique_ptr; #else -typedef std::auto_ptr CharReaderPtr; +using CharReaderPtr = std::auto_ptr; #endif // Implementation of class Features // //////////////////////////////// -Features::Features() - : allowComments_(true), strictRoot_(false), - allowDroppedNullPlaceholders_(false), allowNumericKeys_(false) {} +Features::Features() = default; -Features Features::all() { return Features(); } +Features Features::all() { return {}; } Features Features::strictMode() { Features features; @@ -296,49 +312,38 @@ Features Features::strictMode() { // //////////////////////////////// bool Reader::containsNewLine(Reader::Location begin, Reader::Location end) { - for (; begin < end; ++begin) - if (*begin == '\n' || *begin == '\r') - return true; - return false; + return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; }); } // Class Reader // ////////////////////////////////////////////////////////////////// -Reader::Reader() - : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), - lastValue_(), commentsBefore_(), features_(Features::all()), - collectComments_() {} +Reader::Reader() : features_(Features::all()) {} -Reader::Reader(const Features& features) - : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), - lastValue_(), commentsBefore_(), features_(features), collectComments_() { -} +Reader::Reader(const Features& features) : features_(features) {} -bool -Reader::parse(const std::string& document, Value& root, bool collectComments) { +bool Reader::parse(const std::string& document, Value& root, + bool collectComments) { document_.assign(document.begin(), document.end()); const char* begin = document_.c_str(); const char* end = begin + document_.length(); return parse(begin, end, root, collectComments); } -bool Reader::parse(std::istream& sin, Value& root, bool collectComments) { - // std::istream_iterator begin(sin); +bool Reader::parse(std::istream& is, Value& root, bool collectComments) { + // std::istream_iterator begin(is); // std::istream_iterator end; // Those would allow streamed input from a file, if parse() were a // template function. - // Since JSONCPP_STRING is reference-counted, this at least does not + // Since String is reference-counted, this at least does not // create an extra copy. - JSONCPP_STRING doc; - std::getline(sin, doc, (char)EOF); + String doc; + std::getline(is, doc, static_cast EOF); return parse(doc.data(), doc.data() + doc.size(), root, collectComments); } -bool Reader::parse(const char* beginDoc, - const char* endDoc, - Value& root, +bool Reader::parse(const char* beginDoc, const char* endDoc, Value& root, bool collectComments) { if (!features_.allowComments_) { collectComments = false; @@ -348,8 +353,8 @@ bool Reader::parse(const char* beginDoc, end_ = endDoc; collectComments_ = collectComments; current_ = begin_; - lastValueEnd_ = 0; - lastValue_ = 0; + lastValueEnd_ = nullptr; + lastValue_ = nullptr; commentsBefore_.clear(); errors_.clear(); while (!nodes_.empty()) @@ -379,9 +384,11 @@ bool Reader::parse(const char* beginDoc, bool Reader::readValue() { // readValue() may call itself only if it calls readObject() or ReadArray(). - // These methods execute nodes_.push() just before and nodes_.pop)() just after calling readValue(). - // parse() executes one nodes_.push(), so > instead of >=. - if (nodes_.size() > stackLimit_g) throwRuntimeError("Exceeded stackLimit in readValue()."); + // These methods execute nodes_.push() just before and nodes_.pop)() just + // after calling readValue(). parse() executes one nodes_.push(), so > instead + // of >=. + if (nodes_.size() > stackLimit_g) + throwRuntimeError("Exceeded stackLimit in readValue()."); Token token; skipCommentTokens(token); @@ -407,30 +414,24 @@ bool Reader::readValue() { case tokenString: successful = decodeString(token); break; - case tokenTrue: - { + case tokenTrue: { Value v(true); currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenFalse: - { + } break; + case tokenFalse: { Value v(false); currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenNull: - { + } break; + case tokenNull: { Value v; currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); - } - break; + } break; case tokenArraySeparator: case tokenObjectEnd: case tokenArrayEnd: @@ -536,7 +537,7 @@ bool Reader::readToken(Token& token) { if (!ok) token.type_ = tokenError; token.end_ = current_; - return true; + return ok; } void Reader::skipSpaces() { @@ -549,7 +550,7 @@ void Reader::skipSpaces() { } } -bool Reader::match(Location pattern, int patternLength) { +bool Reader::match(const Char* pattern, int patternLength) { if (end_ - current_ < patternLength) return false; int index = patternLength; @@ -583,16 +584,16 @@ bool Reader::readComment() { return true; } -JSONCPP_STRING Reader::normalizeEOL(Reader::Location begin, Reader::Location end) { - JSONCPP_STRING normalized; +String Reader::normalizeEOL(Reader::Location begin, Reader::Location end) { + String normalized; normalized.reserve(static_cast(end - begin)); Reader::Location current = begin; while (current != end) { char c = *current++; if (c == '\r') { if (current != end && *current == '\n') - // convert dos EOL - ++current; + // convert dos EOL + ++current; // convert Mac EOL normalized += '\n'; } else { @@ -602,12 +603,12 @@ JSONCPP_STRING Reader::normalizeEOL(Reader::Location begin, Reader::Location end return normalized; } -void -Reader::addComment(Location begin, Location end, CommentPlacement placement) { +void Reader::addComment(Location begin, Location end, + CommentPlacement placement) { assert(collectComments_); - const JSONCPP_STRING& normalized = normalizeEOL(begin, end); + const String& normalized = normalizeEOL(begin, end); if (placement == commentAfterOnSameLine) { - assert(lastValue_ != 0); + assert(lastValue_ != nullptr); lastValue_->setComment(normalized, placement); } else { commentsBefore_ += normalized; @@ -640,7 +641,7 @@ bool Reader::readCppStyleComment() { } void Reader::readNumber() { - const char *p = current_; + Location p = current_; char c = '0'; // stopgap for already consumed character // integral part while (c >= '0' && c <= '9') @@ -673,12 +674,12 @@ bool Reader::readString() { return c == '"'; } -bool Reader::readObject(Token& tokenStart) { +bool Reader::readObject(Token& token) { Token tokenName; - JSONCPP_STRING name; + String name; Value init(objectValue); currentValue().swapPayload(init); - currentValue().setOffsetStart(tokenStart.start_ - begin_); + currentValue().setOffsetStart(token.start_ - begin_); while (readToken(tokenName)) { bool initialTokenOk = true; while (tokenName.type_ == tokenComment && initialTokenOk) @@ -695,15 +696,15 @@ bool Reader::readObject(Token& tokenStart) { Value numberName; if (!decodeNumber(tokenName, numberName)) return recoverFromError(tokenObjectEnd); - name = JSONCPP_STRING(numberName.asCString()); + name = numberName.asString(); } else { break; } Token colon; if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { - return addErrorAndRecover( - "Missing ':' after object member name", colon, tokenObjectEnd); + return addErrorAndRecover("Missing ':' after object member name", colon, + tokenObjectEnd); } Value& value = currentValue()[name]; nodes_.push(&value); @@ -716,8 +717,8 @@ bool Reader::readObject(Token& tokenStart) { if (!readToken(comma) || (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && comma.type_ != tokenComment)) { - return addErrorAndRecover( - "Missing ',' or '}' in object declaration", comma, tokenObjectEnd); + return addErrorAndRecover("Missing ',' or '}' in object declaration", + comma, tokenObjectEnd); } bool finalizeTokenOk = true; while (comma.type_ == tokenComment && finalizeTokenOk) @@ -725,14 +726,14 @@ bool Reader::readObject(Token& tokenStart) { if (comma.type_ == tokenObjectEnd) return true; } - return addErrorAndRecover( - "Missing '}' or object member name", tokenName, tokenObjectEnd); + return addErrorAndRecover("Missing '}' or object member name", tokenName, + tokenObjectEnd); } -bool Reader::readArray(Token& tokenStart) { +bool Reader::readArray(Token& token) { Value init(arrayValue); currentValue().swapPayload(init); - currentValue().setOffsetStart(tokenStart.start_ - begin_); + currentValue().setOffsetStart(token.start_ - begin_); skipSpaces(); if (current_ != end_ && *current_ == ']') // empty array { @@ -749,19 +750,19 @@ bool Reader::readArray(Token& tokenStart) { if (!ok) // error already set return recoverFromError(tokenArrayEnd); - Token token; + Token currentToken; // Accept Comment after last item in the array. - ok = readToken(token); - while (token.type_ == tokenComment && ok) { - ok = readToken(token); + ok = readToken(currentToken); + while (currentToken.type_ == tokenComment && ok) { + ok = readToken(currentToken); } - bool badTokenType = - (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd); + bool badTokenType = (currentToken.type_ != tokenArraySeparator && + currentToken.type_ != tokenArrayEnd); if (!ok || badTokenType) { - return addErrorAndRecover( - "Missing ',' or ']' in array declaration", token, tokenArrayEnd); + return addErrorAndRecover("Missing ',' or ']' in array declaration", + currentToken, tokenArrayEnd); } - if (token.type_ == tokenArrayEnd) + if (currentToken.type_ == tokenArrayEnd) break; } return true; @@ -785,7 +786,8 @@ bool Reader::decodeNumber(Token& token, Value& decoded) { bool isNegative = *current == '-'; if (isNegative) ++current; - // TODO: Help the compiler do the div and mod at compile time or get rid of them. + // TODO: Help the compiler do the div and mod at compile time or get rid of + // them. Value::LargestUInt maxIntegerValue = isNegative ? Value::LargestUInt(Value::maxLargestInt) + 1 : Value::maxLargestUInt; @@ -795,7 +797,7 @@ bool Reader::decodeNumber(Token& token, Value& decoded) { Char c = *current++; if (c < '0' || c > '9') return decodeDouble(token, decoded); - Value::UInt digit(static_cast(c - '0')); + auto digit(static_cast(c - '0')); if (value >= threshold) { // We've hit or exceeded the max value divided by 10 (rounded down). If // a) we've only just touched the limit, b) this is the last digit, and @@ -831,18 +833,17 @@ bool Reader::decodeDouble(Token& token) { bool Reader::decodeDouble(Token& token, Value& decoded) { double value = 0; - JSONCPP_STRING buffer(token.start_, token.end_); - JSONCPP_ISTRINGSTREAM is(buffer); + String buffer(token.start_, token.end_); + IStringStream is(buffer); if (!(is >> value)) - return addError("'" + JSONCPP_STRING(token.start_, token.end_) + - "' is not a number.", - token); + return addError( + "'" + String(token.start_, token.end_) + "' is not a number.", token); decoded = value; return true; } bool Reader::decodeString(Token& token) { - JSONCPP_STRING decoded_string; + String decoded_string; if (!decodeString(token, decoded_string)) return false; Value decoded(decoded_string); @@ -852,7 +853,7 @@ bool Reader::decodeString(Token& token) { return true; } -bool Reader::decodeString(Token& token, JSONCPP_STRING& decoded) { +bool Reader::decodeString(Token& token, String& decoded) { decoded.reserve(static_cast(token.end_ - token.start_ - 2)); Location current = token.start_ + 1; // skip '"' Location end = token.end_ - 1; // do not include '"' @@ -860,7 +861,7 @@ bool Reader::decodeString(Token& token, JSONCPP_STRING& decoded) { Char c = *current++; if (c == '"') break; - else if (c == '\\') { + if (c == '\\') { if (current == end) return addError("Empty escape sequence in string", token, current); Char escape = *current++; @@ -905,10 +906,8 @@ bool Reader::decodeString(Token& token, JSONCPP_STRING& decoded) { return true; } -bool Reader::decodeUnicodeCodePoint(Token& token, - Location& current, - Location end, - unsigned int& unicode) { +bool Reader::decodeUnicodeCodePoint(Token& token, Location& current, + Location end, unsigned int& unicode) { if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) return false; @@ -917,10 +916,9 @@ bool Reader::decodeUnicodeCodePoint(Token& token, if (end - current < 6) return addError( "additional six characters expected to parse unicode surrogate pair.", - token, - current); - unsigned int surrogatePair; + token, current); if (*(current++) == '\\' && *(current++) == 'u') { + unsigned int surrogatePair; if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); } else @@ -928,20 +926,17 @@ bool Reader::decodeUnicodeCodePoint(Token& token, } else return addError("expecting another \\u token to begin the second half of " "a unicode surrogate pair", - token, - current); + token, current); } return true; } -bool Reader::decodeUnicodeEscapeSequence(Token& token, - Location& current, +bool Reader::decodeUnicodeEscapeSequence(Token& token, Location& current, Location end, unsigned int& ret_unicode) { if (end - current < 4) return addError( - "Bad unicode escape sequence in string: four digits expected.", - token, + "Bad unicode escape sequence in string: four digits expected.", token, current); int unicode = 0; for (int index = 0; index < 4; ++index) { @@ -956,15 +951,13 @@ bool Reader::decodeUnicodeEscapeSequence(Token& token, else return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", - token, - current); + token, current); } ret_unicode = static_cast(unicode); return true; } -bool -Reader::addError(const JSONCPP_STRING& message, Token& token, Location extra) { +bool Reader::addError(const String& message, Token& token, Location extra) { ErrorInfo info; info.token_ = token; info.message_ = message; @@ -986,8 +979,7 @@ bool Reader::recoverFromError(TokenType skipUntilToken) { return false; } -bool Reader::addErrorAndRecover(const JSONCPP_STRING& message, - Token& token, +bool Reader::addErrorAndRecover(const String& message, Token& token, TokenType skipUntilToken) { addError(message, token); return recoverFromError(skipUntilToken); @@ -1001,8 +993,7 @@ Reader::Char Reader::getNextChar() { return *current_++; } -void Reader::getLocationLineAndColumn(Location location, - int& line, +void Reader::getLocationLineAndColumn(Location location, int& line, int& column) const { Location current = begin_; Location lastLineStart = current; @@ -1024,25 +1015,22 @@ void Reader::getLocationLineAndColumn(Location location, ++line; } -JSONCPP_STRING Reader::getLocationLineAndColumn(Location location) const { +String Reader::getLocationLineAndColumn(Location location) const { int line, column; getLocationLineAndColumn(location, line, column); char buffer[18 + 16 + 16 + 1]; - snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); + jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); return buffer; } // Deprecated. Preserved for backward compatibility -JSONCPP_STRING Reader::getFormatedErrorMessages() const { +String Reader::getFormatedErrorMessages() const { return getFormattedErrorMessages(); } -JSONCPP_STRING Reader::getFormattedErrorMessages() const { - JSONCPP_STRING formattedMessage; - for (Errors::const_iterator itError = errors_.begin(); - itError != errors_.end(); - ++itError) { - const ErrorInfo& error = *itError; +String Reader::getFormattedErrorMessages() const { + String formattedMessage; + for (const auto& error : errors_) { formattedMessage += "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; formattedMessage += " " + error.message_ + "\n"; @@ -1055,10 +1043,7 @@ JSONCPP_STRING Reader::getFormattedErrorMessages() const { std::vector Reader::getStructuredErrors() const { std::vector allErrors; - for (Errors::const_iterator itError = errors_.begin(); - itError != errors_.end(); - ++itError) { - const ErrorInfo& error = *itError; + for (const auto& error : errors_) { Reader::StructuredError structured; structured.offset_start = error.token_.start_ - begin_; structured.offset_limit = error.token_.end_ - begin_; @@ -1068,28 +1053,27 @@ std::vector Reader::getStructuredErrors() const { return allErrors; } -bool Reader::pushError(const Value& value, const JSONCPP_STRING& message) { +bool Reader::pushError(const Value& value, const String& message) { ptrdiff_t const length = end_ - begin_; - if(value.getOffsetStart() > length - || value.getOffsetLimit() > length) + if (value.getOffsetStart() > length || value.getOffsetLimit() > length) return false; Token token; token.type_ = tokenError; token.start_ = begin_ + value.getOffsetStart(); - token.end_ = end_ + value.getOffsetLimit(); + token.end_ = begin_ + value.getOffsetLimit(); ErrorInfo info; info.token_ = token; info.message_ = message; - info.extra_ = 0; + info.extra_ = nullptr; errors_.push_back(info); return true; } -bool Reader::pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra) { +bool Reader::pushError(const Value& value, const String& message, + const Value& extra) { ptrdiff_t const length = end_ - begin_; - if(value.getOffsetStart() > length - || value.getOffsetLimit() > length - || extra.getOffsetLimit() > length) + if (value.getOffsetStart() > length || value.getOffsetLimit() > length || + extra.getOffsetLimit() > length) return false; Token token; token.type_ = tokenError; @@ -1103,15 +1087,15 @@ bool Reader::pushError(const Value& value, const JSONCPP_STRING& message, const return true; } -bool Reader::good() const { - return !errors_.size(); -} +bool Reader::good() const { return errors_.empty(); } -// exact copy of Features +// Originally copied from the Features class (now deprecated), used internally +// for features implementation. class OurFeatures { public: static OurFeatures all(); bool allowComments_; + bool allowTrailingCommas_; bool strictRoot_; bool allowDroppedNullPlaceholders_; bool allowNumericKeys_; @@ -1119,42 +1103,36 @@ public: bool failIfExtra_; bool rejectDupKeys_; bool allowSpecialFloats_; - int stackLimit_; -}; // OurFeatures + bool skipBom_; + size_t stackLimit_; +}; // OurFeatures -// exact copy of Implementation of class Features -// //////////////////////////////// - -OurFeatures OurFeatures::all() { return OurFeatures(); } +OurFeatures OurFeatures::all() { return {}; } // Implementation of class Reader // //////////////////////////////// -// exact copy of Reader, renamed to OurReader +// Originally copied from the Reader class (now deprecated), used internally +// for implementing JSON reading. class OurReader { public: - typedef char Char; - typedef const Char* Location; + using Char = char; + using Location = const Char*; struct StructuredError { ptrdiff_t offset_start; ptrdiff_t offset_limit; - JSONCPP_STRING message; + String message; }; - OurReader(OurFeatures const& features); - bool parse(const char* beginDoc, - const char* endDoc, - Value& root, + explicit OurReader(OurFeatures const& features); + bool parse(const char* beginDoc, const char* endDoc, Value& root, bool collectComments = true); - JSONCPP_STRING getFormattedErrorMessages() const; + String getFormattedErrorMessages() const; std::vector getStructuredErrors() const; - bool pushError(const Value& value, const JSONCPP_STRING& message); - bool pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra); - bool good() const; private: - OurReader(OurReader const&); // no impl - void operator=(OurReader const&); // no impl + OurReader(OurReader const&); // no impl + void operator=(OurReader const&); // no impl enum TokenType { tokenEndOfStream = 0, @@ -1186,17 +1164,18 @@ private: class ErrorInfo { public: Token token_; - JSONCPP_STRING message_; + String message_; Location extra_; }; - typedef std::deque Errors; + using Errors = std::deque; bool readToken(Token& token); void skipSpaces(); - bool match(Location pattern, int patternLength); + void skipBom(bool skipBom); + bool match(const Char* pattern, int patternLength); bool readComment(); - bool readCStyleComment(); + bool readCStyleComment(bool* containsNewLineResult); bool readCppStyleComment(); bool readString(); bool readStringSingleQuote(); @@ -1207,68 +1186,57 @@ private: bool decodeNumber(Token& token); bool decodeNumber(Token& token, Value& decoded); bool decodeString(Token& token); - bool decodeString(Token& token, JSONCPP_STRING& decoded); + bool decodeString(Token& token, String& decoded); bool decodeDouble(Token& token); bool decodeDouble(Token& token, Value& decoded); - bool decodeUnicodeCodePoint(Token& token, - Location& current, - Location end, + bool decodeUnicodeCodePoint(Token& token, Location& current, Location end, unsigned int& unicode); - bool decodeUnicodeEscapeSequence(Token& token, - Location& current, - Location end, - unsigned int& unicode); - bool addError(const JSONCPP_STRING& message, Token& token, Location extra = 0); + bool decodeUnicodeEscapeSequence(Token& token, Location& current, + Location end, unsigned int& unicode); + bool addError(const String& message, Token& token, Location extra = nullptr); bool recoverFromError(TokenType skipUntilToken); - bool addErrorAndRecover(const JSONCPP_STRING& message, - Token& token, + bool addErrorAndRecover(const String& message, Token& token, TokenType skipUntilToken); void skipUntilSpace(); Value& currentValue(); Char getNextChar(); - void - getLocationLineAndColumn(Location location, int& line, int& column) const; - JSONCPP_STRING getLocationLineAndColumn(Location location) const; + void getLocationLineAndColumn(Location location, int& line, + int& column) const; + String getLocationLineAndColumn(Location location) const; void addComment(Location begin, Location end, CommentPlacement placement); void skipCommentTokens(Token& token); - static JSONCPP_STRING normalizeEOL(Location begin, Location end); + static String normalizeEOL(Location begin, Location end); static bool containsNewLine(Location begin, Location end); - typedef std::stack Nodes; - Nodes nodes_; - Errors errors_; - JSONCPP_STRING document_; - Location begin_; - Location end_; - Location current_; - Location lastValueEnd_; - Value* lastValue_; - JSONCPP_STRING commentsBefore_; + using Nodes = std::stack; + + Nodes nodes_{}; + Errors errors_{}; + String document_{}; + Location begin_ = nullptr; + Location end_ = nullptr; + Location current_ = nullptr; + Location lastValueEnd_ = nullptr; + Value* lastValue_ = nullptr; + bool lastValueHasAComment_ = false; + String commentsBefore_{}; OurFeatures const features_; - bool collectComments_; -}; // OurReader + bool collectComments_ = false; +}; // OurReader // complete copy of Read impl, for OurReader -bool OurReader::containsNewLine(OurReader::Location begin, OurReader::Location end) { - for (; begin < end; ++begin) - if (*begin == '\n' || *begin == '\r') - return true; - return false; +bool OurReader::containsNewLine(OurReader::Location begin, + OurReader::Location end) { + return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; }); } -OurReader::OurReader(OurFeatures const& features) - : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), - lastValue_(), commentsBefore_(), - features_(features), collectComments_() { -} +OurReader::OurReader(OurFeatures const& features) : features_(features) {} -bool OurReader::parse(const char* beginDoc, - const char* endDoc, - Value& root, - bool collectComments) { +bool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root, + bool collectComments) { if (!features_.allowComments_) { collectComments = false; } @@ -1277,22 +1245,23 @@ bool OurReader::parse(const char* beginDoc, end_ = endDoc; collectComments_ = collectComments; current_ = begin_; - lastValueEnd_ = 0; - lastValue_ = 0; + lastValueEnd_ = nullptr; + lastValue_ = nullptr; commentsBefore_.clear(); errors_.clear(); while (!nodes_.empty()) nodes_.pop(); nodes_.push(&root); + // skip byte order mark if it exists at the beginning of the UTF-8 text. + skipBom(features_.skipBom_); bool successful = readValue(); + nodes_.pop(); Token token; skipCommentTokens(token); - if (features_.failIfExtra_) { - if ((features_.strictRoot_ || token.type_ != tokenError) && token.type_ != tokenEndOfStream) { - addError("Extra non-whitespace after JSON value.", token); - return false; - } + if (features_.failIfExtra_ && (token.type_ != tokenEndOfStream)) { + addError("Extra non-whitespace after JSON value.", token); + return false; } if (collectComments_ && !commentsBefore_.empty()) root.setComment(commentsBefore_, commentAfter); @@ -1314,7 +1283,8 @@ bool OurReader::parse(const char* beginDoc, bool OurReader::readValue() { // To preserve the old behaviour we cast size_t to int. - if (static_cast(nodes_.size()) > features_.stackLimit_) throwRuntimeError("Exceeded stackLimit in readValue()."); + if (nodes_.size() > features_.stackLimit_) + throwRuntimeError("Exceeded stackLimit in readValue()."); Token token; skipCommentTokens(token); bool successful = true; @@ -1339,54 +1309,42 @@ bool OurReader::readValue() { case tokenString: successful = decodeString(token); break; - case tokenTrue: - { + case tokenTrue: { Value v(true); currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenFalse: - { + } break; + case tokenFalse: { Value v(false); currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenNull: - { + } break; + case tokenNull: { Value v; currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenNaN: - { + } break; + case tokenNaN: { Value v(std::numeric_limits::quiet_NaN()); currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenPosInf: - { + } break; + case tokenPosInf: { Value v(std::numeric_limits::infinity()); currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenNegInf: - { + } break; + case tokenNegInf: { Value v(-std::numeric_limits::infinity()); currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); - } - break; + } break; case tokenArraySeparator: case tokenObjectEnd: case tokenArrayEnd: @@ -1408,6 +1366,7 @@ bool OurReader::readValue() { if (collectComments_) { lastValueEnd_ = current_; + lastValueHasAComment_ = false; lastValue_ = ¤tValue(); } @@ -1448,10 +1407,13 @@ bool OurReader::readToken(Token& token) { break; case '\'': if (features_.allowSingleQuotes_) { - token.type_ = tokenString; - ok = readStringSingleQuote(); + token.type_ = tokenString; + ok = readStringSingleQuote(); + } else { + // If we don't allow single quotes, this is a failure case. + ok = false; + } break; - } // else fall through case '/': token.type_ = tokenComment; ok = readComment(); @@ -1477,6 +1439,14 @@ bool OurReader::readToken(Token& token) { ok = features_.allowSpecialFloats_ && match("nfinity", 7); } break; + case '+': + if (readNumber(true)) { + token.type_ = tokenNumber; + } else { + token.type_ = tokenPosInf; + ok = features_.allowSpecialFloats_ && match("nfinity", 7); + } + break; case 't': token.type_ = tokenTrue; ok = match("rue", 3); @@ -1521,7 +1491,7 @@ bool OurReader::readToken(Token& token) { if (!ok) token.type_ = tokenError; token.end_ = current_; - return true; + return ok; } void OurReader::skipSpaces() { @@ -1534,7 +1504,17 @@ void OurReader::skipSpaces() { } } -bool OurReader::match(Location pattern, int patternLength) { +void OurReader::skipBom(bool skipBom) { + // The default behavior is to skip BOM. + if (skipBom) { + if ((end_ - begin_) >= 3 && strncmp(begin_, "\xEF\xBB\xBF", 3) == 0) { + begin_ += 3; + current_ = begin_; + } + } +} + +bool OurReader::match(const Char* pattern, int patternLength) { if (end_ - current_ < patternLength) return false; int index = patternLength; @@ -1546,21 +1526,32 @@ bool OurReader::match(Location pattern, int patternLength) { } bool OurReader::readComment() { - Location commentBegin = current_ - 1; - Char c = getNextChar(); + const Location commentBegin = current_ - 1; + const Char c = getNextChar(); bool successful = false; - if (c == '*') - successful = readCStyleComment(); - else if (c == '/') + bool cStyleWithEmbeddedNewline = false; + + const bool isCStyleComment = (c == '*'); + const bool isCppStyleComment = (c == '/'); + if (isCStyleComment) { + successful = readCStyleComment(&cStyleWithEmbeddedNewline); + } else if (isCppStyleComment) { successful = readCppStyleComment(); + } + if (!successful) return false; if (collectComments_) { CommentPlacement placement = commentBefore; - if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { - if (c != '*' || !containsNewLine(commentBegin, current_)) - placement = commentAfterOnSameLine; + + if (!lastValueHasAComment_) { + if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { + if (isCppStyleComment || !cStyleWithEmbeddedNewline) { + placement = commentAfterOnSameLine; + lastValueHasAComment_ = true; + } + } } addComment(commentBegin, current_, placement); @@ -1568,16 +1559,17 @@ bool OurReader::readComment() { return true; } -JSONCPP_STRING OurReader::normalizeEOL(OurReader::Location begin, OurReader::Location end) { - JSONCPP_STRING normalized; +String OurReader::normalizeEOL(OurReader::Location begin, + OurReader::Location end) { + String normalized; normalized.reserve(static_cast(end - begin)); OurReader::Location current = begin; while (current != end) { char c = *current++; if (c == '\r') { if (current != end && *current == '\n') - // convert dos EOL - ++current; + // convert dos EOL + ++current; // convert Mac EOL normalized += '\n'; } else { @@ -1587,24 +1579,29 @@ JSONCPP_STRING OurReader::normalizeEOL(OurReader::Location begin, OurReader::Loc return normalized; } -void -OurReader::addComment(Location begin, Location end, CommentPlacement placement) { +void OurReader::addComment(Location begin, Location end, + CommentPlacement placement) { assert(collectComments_); - const JSONCPP_STRING& normalized = normalizeEOL(begin, end); + const String& normalized = normalizeEOL(begin, end); if (placement == commentAfterOnSameLine) { - assert(lastValue_ != 0); + assert(lastValue_ != nullptr); lastValue_->setComment(normalized, placement); } else { commentsBefore_ += normalized; } } -bool OurReader::readCStyleComment() { +bool OurReader::readCStyleComment(bool* containsNewLineResult) { + *containsNewLineResult = false; + while ((current_ + 1) < end_) { Char c = getNextChar(); if (c == '*' && *current_ == '/') break; + if (c == '\n') + *containsNewLineResult = true; } + return getNextChar() == '/'; } @@ -1625,7 +1622,7 @@ bool OurReader::readCppStyleComment() { } bool OurReader::readNumber(bool checkInf) { - const char *p = current_; + Location p = current_; if (checkInf && p != end_ && *p == 'I') { current_ = ++p; return false; @@ -1662,7 +1659,6 @@ bool OurReader::readString() { return c == '"'; } - bool OurReader::readStringSingleQuote() { Char c = 0; while (current_ != end_) { @@ -1675,19 +1671,21 @@ bool OurReader::readStringSingleQuote() { return c == '\''; } -bool OurReader::readObject(Token& tokenStart) { +bool OurReader::readObject(Token& token) { Token tokenName; - JSONCPP_STRING name; + String name; Value init(objectValue); currentValue().swapPayload(init); - currentValue().setOffsetStart(tokenStart.start_ - begin_); + currentValue().setOffsetStart(token.start_ - begin_); while (readToken(tokenName)) { bool initialTokenOk = true; while (tokenName.type_ == tokenComment && initialTokenOk) initialTokenOk = readToken(tokenName); if (!initialTokenOk) break; - if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object + if (tokenName.type_ == tokenObjectEnd && + (name.empty() || + features_.allowTrailingCommas_)) // empty object or trailing comma return true; name.clear(); if (tokenName.type_ == tokenString) { @@ -1701,17 +1699,17 @@ bool OurReader::readObject(Token& tokenStart) { } else { break; } + if (name.length() >= (1U << 30)) + throwRuntimeError("keylength >= 2^30"); + if (features_.rejectDupKeys_ && currentValue().isMember(name)) { + String msg = "Duplicate key: '" + name + "'"; + return addErrorAndRecover(msg, tokenName, tokenObjectEnd); + } Token colon; if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { - return addErrorAndRecover( - "Missing ':' after object member name", colon, tokenObjectEnd); - } - if (name.length() >= (1U<<30)) throwRuntimeError("keylength >= 2^30"); - if (features_.rejectDupKeys_ && currentValue().isMember(name)) { - JSONCPP_STRING msg = "Duplicate key: '" + name + "'"; - return addErrorAndRecover( - msg, tokenName, tokenObjectEnd); + return addErrorAndRecover("Missing ':' after object member name", colon, + tokenObjectEnd); } Value& value = currentValue()[name]; nodes_.push(&value); @@ -1724,8 +1722,8 @@ bool OurReader::readObject(Token& tokenStart) { if (!readToken(comma) || (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && comma.type_ != tokenComment)) { - return addErrorAndRecover( - "Missing ',' or '}' in object declaration", comma, tokenObjectEnd); + return addErrorAndRecover("Missing ',' or '}' in object declaration", + comma, tokenObjectEnd); } bool finalizeTokenOk = true; while (comma.type_ == tokenComment && finalizeTokenOk) @@ -1733,23 +1731,27 @@ bool OurReader::readObject(Token& tokenStart) { if (comma.type_ == tokenObjectEnd) return true; } - return addErrorAndRecover( - "Missing '}' or object member name", tokenName, tokenObjectEnd); + return addErrorAndRecover("Missing '}' or object member name", tokenName, + tokenObjectEnd); } -bool OurReader::readArray(Token& tokenStart) { +bool OurReader::readArray(Token& token) { Value init(arrayValue); currentValue().swapPayload(init); - currentValue().setOffsetStart(tokenStart.start_ - begin_); - skipSpaces(); - if (current_ != end_ && *current_ == ']') // empty array - { - Token endArray; - readToken(endArray); - return true; - } + currentValue().setOffsetStart(token.start_ - begin_); int index = 0; for (;;) { + skipSpaces(); + if (current_ != end_ && *current_ == ']' && + (index == 0 || + (features_.allowTrailingCommas_ && + !features_.allowDroppedNullPlaceholders_))) // empty array or trailing + // comma + { + Token endArray; + readToken(endArray); + return true; + } Value& value = currentValue()[index++]; nodes_.push(&value); bool ok = readValue(); @@ -1757,19 +1759,19 @@ bool OurReader::readArray(Token& tokenStart) { if (!ok) // error already set return recoverFromError(tokenArrayEnd); - Token token; + Token currentToken; // Accept Comment after last item in the array. - ok = readToken(token); - while (token.type_ == tokenComment && ok) { - ok = readToken(token); + ok = readToken(currentToken); + while (currentToken.type_ == tokenComment && ok) { + ok = readToken(currentToken); } - bool badTokenType = - (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd); + bool badTokenType = (currentToken.type_ != tokenArraySeparator && + currentToken.type_ != tokenArrayEnd); if (!ok || badTokenType) { - return addErrorAndRecover( - "Missing ',' or ']' in array declaration", token, tokenArrayEnd); + return addErrorAndRecover("Missing ',' or ']' in array declaration", + currentToken, tokenArrayEnd); } - if (token.type_ == tokenArrayEnd) + if (currentToken.type_ == tokenArrayEnd) break; } return true; @@ -1790,38 +1792,78 @@ bool OurReader::decodeNumber(Token& token, Value& decoded) { // larger than the maximum supported value of an integer then // we decode the number as a double. Location current = token.start_; - bool isNegative = *current == '-'; - if (isNegative) + const bool isNegative = *current == '-'; + if (isNegative) { ++current; - // TODO: Help the compiler do the div and mod at compile time or get rid of them. - Value::LargestUInt maxIntegerValue = - isNegative ? Value::LargestUInt(-Value::minLargestInt) - : Value::maxLargestUInt; - Value::LargestUInt threshold = maxIntegerValue / 10; + } + + // We assume we can represent the largest and smallest integer types as + // unsigned integers with separate sign. This is only true if they can fit + // into an unsigned integer. + static_assert(Value::maxLargestInt <= Value::maxLargestUInt, + "Int must be smaller than UInt"); + + // We need to convert minLargestInt into a positive number. The easiest way + // to do this conversion is to assume our "threshold" value of minLargestInt + // divided by 10 can fit in maxLargestInt when absolute valued. This should + // be a safe assumption. + static_assert(Value::minLargestInt <= -Value::maxLargestInt, + "The absolute value of minLargestInt must be greater than or " + "equal to maxLargestInt"); + static_assert(Value::minLargestInt / 10 >= -Value::maxLargestInt, + "The absolute value of minLargestInt must be only 1 magnitude " + "larger than maxLargest Int"); + + static constexpr Value::LargestUInt positive_threshold = + Value::maxLargestUInt / 10; + static constexpr Value::UInt positive_last_digit = Value::maxLargestUInt % 10; + + // For the negative values, we have to be more careful. Since typically + // -Value::minLargestInt will cause an overflow, we first divide by 10 and + // then take the inverse. This assumes that minLargestInt is only a single + // power of 10 different in magnitude, which we check above. For the last + // digit, we take the modulus before negating for the same reason. + static constexpr auto negative_threshold = + Value::LargestUInt(-(Value::minLargestInt / 10)); + static constexpr auto negative_last_digit = + Value::UInt(-(Value::minLargestInt % 10)); + + const Value::LargestUInt threshold = + isNegative ? negative_threshold : positive_threshold; + const Value::UInt max_last_digit = + isNegative ? negative_last_digit : positive_last_digit; + Value::LargestUInt value = 0; while (current < token.end_) { Char c = *current++; if (c < '0' || c > '9') return decodeDouble(token, decoded); - Value::UInt digit(static_cast(c - '0')); + + const auto digit(static_cast(c - '0')); if (value >= threshold) { // We've hit or exceeded the max value divided by 10 (rounded down). If - // a) we've only just touched the limit, b) this is the last digit, and + // a) we've only just touched the limit, meaing value == threshold, + // b) this is the last digit, or // c) it's small enough to fit in that rounding delta, we're okay. // Otherwise treat this number as a double to avoid overflow. if (value > threshold || current != token.end_ || - digit > maxIntegerValue % 10) { + digit > max_last_digit) { return decodeDouble(token, decoded); } } value = value * 10 + digit; } - if (isNegative) - decoded = -Value::LargestInt(value); - else if (value <= Value::LargestUInt(Value::maxInt)) + + if (isNegative) { + // We use the same magnitude assumption here, just in case. + const auto last_digit = static_cast(value % 10); + decoded = -Value::LargestInt(value / 10) * 10 - last_digit; + } else if (value <= Value::LargestUInt(Value::maxLargestInt)) { decoded = Value::LargestInt(value); - else + } else { decoded = value; + } + return true; } @@ -1837,44 +1879,18 @@ bool OurReader::decodeDouble(Token& token) { bool OurReader::decodeDouble(Token& token, Value& decoded) { double value = 0; - const int bufferSize = 32; - int count; - ptrdiff_t const length = token.end_ - token.start_; - - // Sanity check to avoid buffer overflow exploits. - if (length < 0) { - return addError("Unable to parse token length", token); + const String buffer(token.start_, token.end_); + IStringStream is(buffer); + if (!(is >> value)) { + return addError( + "'" + String(token.start_, token.end_) + "' is not a number.", token); } - size_t const ulength = static_cast(length); - - // Avoid using a string constant for the format control string given to - // sscanf, as this can cause hard to debug crashes on OS X. See here for more - // info: - // - // http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html - char format[] = "%lf"; - - if (length <= bufferSize) { - Char buffer[bufferSize + 1]; - memcpy(buffer, token.start_, ulength); - buffer[length] = 0; - fixNumericLocaleInput(buffer, buffer + length); - count = sscanf(buffer, format, &value); - } else { - JSONCPP_STRING buffer(token.start_, token.end_); - count = sscanf(buffer.c_str(), format, &value); - } - - if (count != 1) - return addError("'" + JSONCPP_STRING(token.start_, token.end_) + - "' is not a number.", - token); decoded = value; return true; } bool OurReader::decodeString(Token& token) { - JSONCPP_STRING decoded_string; + String decoded_string; if (!decodeString(token, decoded_string)) return false; Value decoded(decoded_string); @@ -1884,7 +1900,7 @@ bool OurReader::decodeString(Token& token) { return true; } -bool OurReader::decodeString(Token& token, JSONCPP_STRING& decoded) { +bool OurReader::decodeString(Token& token, String& decoded) { decoded.reserve(static_cast(token.end_ - token.start_ - 2)); Location current = token.start_ + 1; // skip '"' Location end = token.end_ - 1; // do not include '"' @@ -1892,7 +1908,7 @@ bool OurReader::decodeString(Token& token, JSONCPP_STRING& decoded) { Char c = *current++; if (c == '"') break; - else if (c == '\\') { + if (c == '\\') { if (current == end) return addError("Empty escape sequence in string", token, current); Char escape = *current++; @@ -1937,10 +1953,8 @@ bool OurReader::decodeString(Token& token, JSONCPP_STRING& decoded) { return true; } -bool OurReader::decodeUnicodeCodePoint(Token& token, - Location& current, - Location end, - unsigned int& unicode) { +bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current, + Location end, unsigned int& unicode) { if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) return false; @@ -1949,10 +1963,9 @@ bool OurReader::decodeUnicodeCodePoint(Token& token, if (end - current < 6) return addError( "additional six characters expected to parse unicode surrogate pair.", - token, - current); - unsigned int surrogatePair; + token, current); if (*(current++) == '\\' && *(current++) == 'u') { + unsigned int surrogatePair; if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); } else @@ -1960,20 +1973,17 @@ bool OurReader::decodeUnicodeCodePoint(Token& token, } else return addError("expecting another \\u token to begin the second half of " "a unicode surrogate pair", - token, - current); + token, current); } return true; } -bool OurReader::decodeUnicodeEscapeSequence(Token& token, - Location& current, - Location end, - unsigned int& ret_unicode) { +bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current, + Location end, + unsigned int& ret_unicode) { if (end - current < 4) return addError( - "Bad unicode escape sequence in string: four digits expected.", - token, + "Bad unicode escape sequence in string: four digits expected.", token, current); int unicode = 0; for (int index = 0; index < 4; ++index) { @@ -1988,15 +1998,13 @@ bool OurReader::decodeUnicodeEscapeSequence(Token& token, else return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", - token, - current); + token, current); } ret_unicode = static_cast(unicode); return true; } -bool -OurReader::addError(const JSONCPP_STRING& message, Token& token, Location extra) { +bool OurReader::addError(const String& message, Token& token, Location extra) { ErrorInfo info; info.token_ = token; info.message_ = message; @@ -2018,9 +2026,8 @@ bool OurReader::recoverFromError(TokenType skipUntilToken) { return false; } -bool OurReader::addErrorAndRecover(const JSONCPP_STRING& message, - Token& token, - TokenType skipUntilToken) { +bool OurReader::addErrorAndRecover(const String& message, Token& token, + TokenType skipUntilToken) { addError(message, token); return recoverFromError(skipUntilToken); } @@ -2033,9 +2040,8 @@ OurReader::Char OurReader::getNextChar() { return *current_++; } -void OurReader::getLocationLineAndColumn(Location location, - int& line, - int& column) const { +void OurReader::getLocationLineAndColumn(Location location, int& line, + int& column) const { Location current = begin_; Location lastLineStart = current; line = 0; @@ -2056,20 +2062,17 @@ void OurReader::getLocationLineAndColumn(Location location, ++line; } -JSONCPP_STRING OurReader::getLocationLineAndColumn(Location location) const { +String OurReader::getLocationLineAndColumn(Location location) const { int line, column; getLocationLineAndColumn(location, line, column); char buffer[18 + 16 + 16 + 1]; - snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); + jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); return buffer; } -JSONCPP_STRING OurReader::getFormattedErrorMessages() const { - JSONCPP_STRING formattedMessage; - for (Errors::const_iterator itError = errors_.begin(); - itError != errors_.end(); - ++itError) { - const ErrorInfo& error = *itError; +String OurReader::getFormattedErrorMessages() const { + String formattedMessage; + for (const auto& error : errors_) { formattedMessage += "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; formattedMessage += " " + error.message_ + "\n"; @@ -2082,10 +2085,7 @@ JSONCPP_STRING OurReader::getFormattedErrorMessages() const { std::vector OurReader::getStructuredErrors() const { std::vector allErrors; - for (Errors::const_iterator itError = errors_.begin(); - itError != errors_.end(); - ++itError) { - const ErrorInfo& error = *itError; + for (const auto& error : errors_) { OurReader::StructuredError structured; structured.offset_start = error.token_.start_ - begin_; structured.offset_limit = error.token_.end_ - begin_; @@ -2095,59 +2095,15 @@ std::vector OurReader::getStructuredErrors() const { return allErrors; } -bool OurReader::pushError(const Value& value, const JSONCPP_STRING& message) { - ptrdiff_t length = end_ - begin_; - if(value.getOffsetStart() > length - || value.getOffsetLimit() > length) - return false; - Token token; - token.type_ = tokenError; - token.start_ = begin_ + value.getOffsetStart(); - token.end_ = end_ + value.getOffsetLimit(); - ErrorInfo info; - info.token_ = token; - info.message_ = message; - info.extra_ = 0; - errors_.push_back(info); - return true; -} - -bool OurReader::pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra) { - ptrdiff_t length = end_ - begin_; - if(value.getOffsetStart() > length - || value.getOffsetLimit() > length - || extra.getOffsetLimit() > length) - return false; - Token token; - token.type_ = tokenError; - token.start_ = begin_ + value.getOffsetStart(); - token.end_ = begin_ + value.getOffsetLimit(); - ErrorInfo info; - info.token_ = token; - info.message_ = message; - info.extra_ = begin_ + extra.getOffsetStart(); - errors_.push_back(info); - return true; -} - -bool OurReader::good() const { - return !errors_.size(); -} - - class OurCharReader : public CharReader { bool const collectComments_; OurReader reader_; + public: - OurCharReader( - bool collectComments, - OurFeatures const& features) - : collectComments_(collectComments) - , reader_(features) - {} - bool parse( - char const* beginDoc, char const* endDoc, - Value* root, JSONCPP_STRING* errs) JSONCPP_OVERRIDE { + OurCharReader(bool collectComments, OurFeatures const& features) + : collectComments_(collectComments), reader_(features) {} + bool parse(char const* beginDoc, char const* endDoc, Value* root, + String* errs) override { bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_); if (errs) { *errs = reader_.getFormattedErrorMessages(); @@ -2156,67 +2112,64 @@ public: } }; -CharReaderBuilder::CharReaderBuilder() -{ - setDefaults(&settings_); -} -CharReaderBuilder::~CharReaderBuilder() -{} -CharReader* CharReaderBuilder::newCharReader() const -{ +CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); } +CharReaderBuilder::~CharReaderBuilder() = default; +CharReader* CharReaderBuilder::newCharReader() const { bool collectComments = settings_["collectComments"].asBool(); OurFeatures features = OurFeatures::all(); features.allowComments_ = settings_["allowComments"].asBool(); + features.allowTrailingCommas_ = settings_["allowTrailingCommas"].asBool(); features.strictRoot_ = settings_["strictRoot"].asBool(); - features.allowDroppedNullPlaceholders_ = settings_["allowDroppedNullPlaceholders"].asBool(); + features.allowDroppedNullPlaceholders_ = + settings_["allowDroppedNullPlaceholders"].asBool(); features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool(); features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool(); - features.stackLimit_ = settings_["stackLimit"].asInt(); + + // Stack limit is always a size_t, so we get this as an unsigned int + // regardless of it we have 64-bit integer support enabled. + features.stackLimit_ = static_cast(settings_["stackLimit"].asUInt()); features.failIfExtra_ = settings_["failIfExtra"].asBool(); features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool(); features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool(); + features.skipBom_ = settings_["skipBom"].asBool(); return new OurCharReader(collectComments, features); } -static void getValidReaderKeys(std::set* valid_keys) -{ - valid_keys->clear(); - valid_keys->insert("collectComments"); - valid_keys->insert("allowComments"); - valid_keys->insert("strictRoot"); - valid_keys->insert("allowDroppedNullPlaceholders"); - valid_keys->insert("allowNumericKeys"); - valid_keys->insert("allowSingleQuotes"); - valid_keys->insert("stackLimit"); - valid_keys->insert("failIfExtra"); - valid_keys->insert("rejectDupKeys"); - valid_keys->insert("allowSpecialFloats"); -} -bool CharReaderBuilder::validate(Json::Value* invalid) const -{ - Json::Value my_invalid; - if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL - Json::Value& inv = *invalid; - std::set valid_keys; - getValidReaderKeys(&valid_keys); - Value::Members keys = settings_.getMemberNames(); - size_t n = keys.size(); - for (size_t i = 0; i < n; ++i) { - JSONCPP_STRING const& key = keys[i]; - if (valid_keys.find(key) == valid_keys.end()) { - inv[key] = settings_[key]; - } + +bool CharReaderBuilder::validate(Json::Value* invalid) const { + static const auto& valid_keys = *new std::set{ + "collectComments", + "allowComments", + "allowTrailingCommas", + "strictRoot", + "allowDroppedNullPlaceholders", + "allowNumericKeys", + "allowSingleQuotes", + "stackLimit", + "failIfExtra", + "rejectDupKeys", + "allowSpecialFloats", + "skipBom", + }; + for (auto si = settings_.begin(); si != settings_.end(); ++si) { + auto key = si.name(); + if (valid_keys.count(key)) + continue; + if (invalid) + (*invalid)[std::move(key)] = *si; + else + return false; } - return 0u == inv.size(); + return invalid ? invalid->empty() : true; } -Value& CharReaderBuilder::operator[](JSONCPP_STRING key) -{ + +Value& CharReaderBuilder::operator[](const String& key) { return settings_[key]; } // static -void CharReaderBuilder::strictMode(Json::Value* settings) -{ -//! [CharReaderBuilderStrictMode] +void CharReaderBuilder::strictMode(Json::Value* settings) { + //! [CharReaderBuilderStrictMode] (*settings)["allowComments"] = false; + (*settings)["allowTrailingCommas"] = false; (*settings)["strictRoot"] = true; (*settings)["allowDroppedNullPlaceholders"] = false; (*settings)["allowNumericKeys"] = false; @@ -2225,14 +2178,15 @@ void CharReaderBuilder::strictMode(Json::Value* settings) (*settings)["failIfExtra"] = true; (*settings)["rejectDupKeys"] = true; (*settings)["allowSpecialFloats"] = false; -//! [CharReaderBuilderStrictMode] + (*settings)["skipBom"] = true; + //! [CharReaderBuilderStrictMode] } // static -void CharReaderBuilder::setDefaults(Json::Value* settings) -{ -//! [CharReaderBuilderDefaults] +void CharReaderBuilder::setDefaults(Json::Value* settings) { + //! [CharReaderBuilderDefaults] (*settings)["collectComments"] = true; (*settings)["allowComments"] = true; + (*settings)["allowTrailingCommas"] = true; (*settings)["strictRoot"] = false; (*settings)["allowDroppedNullPlaceholders"] = false; (*settings)["allowNumericKeys"] = false; @@ -2241,19 +2195,18 @@ void CharReaderBuilder::setDefaults(Json::Value* settings) (*settings)["failIfExtra"] = false; (*settings)["rejectDupKeys"] = false; (*settings)["allowSpecialFloats"] = false; -//! [CharReaderBuilderDefaults] + (*settings)["skipBom"] = true; + //! [CharReaderBuilderDefaults] } ////////////////////////////////// // global functions -bool parseFromStream( - CharReader::Factory const& fact, JSONCPP_ISTREAM& sin, - Value* root, JSONCPP_STRING* errs) -{ - JSONCPP_OSTRINGSTREAM ssin; +bool parseFromStream(CharReader::Factory const& fact, IStream& sin, Value* root, + String* errs) { + OStringStream ssin; ssin << sin.rdbuf(); - JSONCPP_STRING doc = ssin.str(); + String doc = ssin.str(); char const* begin = doc.data(); char const* end = begin + doc.size(); // Note that we do not actually need a null-terminator. @@ -2261,9 +2214,9 @@ bool parseFromStream( return reader->parse(begin, end, root, errs); } -JSONCPP_ISTREAM& operator>>(JSONCPP_ISTREAM& sin, Value& root) { +IStream& operator>>(IStream& sin, Value& root) { CharReaderBuilder b; - JSONCPP_STRING errs; + String errs; bool ok = parseFromStream(b, sin, &root, &errs); if (!ok) { throwRuntimeError(errs); @@ -2303,31 +2256,21 @@ namespace Json { // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// -ValueIteratorBase::ValueIteratorBase() - : current_(), isNull_(true) { -} +ValueIteratorBase::ValueIteratorBase() : current_() {} ValueIteratorBase::ValueIteratorBase( const Value::ObjectValues::iterator& current) : current_(current), isNull_(false) {} -Value& ValueIteratorBase::deref() const { - return current_->second; -} +Value& ValueIteratorBase::deref() { return current_->second; } +const Value& ValueIteratorBase::deref() const { return current_->second; } -void ValueIteratorBase::increment() { - ++current_; -} +void ValueIteratorBase::increment() { ++current_; } -void ValueIteratorBase::decrement() { - --current_; -} +void ValueIteratorBase::decrement() { --current_; } ValueIteratorBase::difference_type ValueIteratorBase::computeDistance(const SelfType& other) const { -#ifdef JSON_USE_CPPTL_SMALLMAP - return other.current_ - current_; -#else // Iterator for null value are initialized using the default // constructor, which initialize current_ to the default // std::map::iterator. As begin() and end() are two instance @@ -2348,7 +2291,6 @@ ValueIteratorBase::computeDistance(const SelfType& other) const { ++myDistance; } return myDistance; -#endif } bool ValueIteratorBase::isEqual(const SelfType& other) const { @@ -2380,12 +2322,13 @@ UInt ValueIteratorBase::index() const { return Value::UInt(-1); } -JSONCPP_STRING ValueIteratorBase::name() const { +String ValueIteratorBase::name() const { char const* keey; char const* end; keey = memberName(&end); - if (!keey) return JSONCPP_STRING(); - return JSONCPP_STRING(keey, end); + if (!keey) + return String(); + return String(keey, end); } char const* ValueIteratorBase::memberName() const { @@ -2396,8 +2339,8 @@ char const* ValueIteratorBase::memberName() const { char const* ValueIteratorBase::memberName(char const** end) const { const char* cname = (*current_).first.data(); if (!cname) { - *end = NULL; - return NULL; + *end = nullptr; + return nullptr; } *end = cname + (*current_).first.length(); return cname; @@ -2411,7 +2354,7 @@ char const* ValueIteratorBase::memberName(char const** end) const { // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// -ValueConstIterator::ValueConstIterator() {} +ValueConstIterator::ValueConstIterator() = default; ValueConstIterator::ValueConstIterator( const Value::ObjectValues::iterator& current) @@ -2434,7 +2377,7 @@ operator=(const ValueIteratorBase& other) { // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// -ValueIterator::ValueIterator() {} +ValueIterator::ValueIterator() = default; ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current) : ValueIteratorBase(current) {} @@ -2444,8 +2387,7 @@ ValueIterator::ValueIterator(const ValueConstIterator& other) throwRuntimeError("ConstIterator to Iterator should never be allowed."); } -ValueIterator::ValueIterator(const ValueIterator& other) - : ValueIteratorBase(other) {} +ValueIterator::ValueIterator(const ValueIterator& other) = default; ValueIterator& ValueIterator::operator=(const SelfType& other) { copy(other); @@ -2477,20 +2419,54 @@ ValueIterator& ValueIterator::operator=(const SelfType& other) { #include #include #endif // if !defined(JSON_IS_AMALGAMATION) -#include +#include +#include +#include +#include +#include +#include #include #include -#include -#include -#ifdef JSON_USE_CPPTL -#include + +// Provide implementation equivalent of std::snprintf for older _MSC compilers +#if defined(_MSC_VER) && _MSC_VER < 1900 +#include +static int msvc_pre1900_c99_vsnprintf(char* outBuf, size_t size, + const char* format, va_list ap) { + int count = -1; + if (size != 0) + count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); + if (count == -1) + count = _vscprintf(format, ap); + return count; +} + +int JSON_API msvc_pre1900_c99_snprintf(char* outBuf, size_t size, + const char* format, ...) { + va_list ap; + va_start(ap, format); + const int count = msvc_pre1900_c99_vsnprintf(outBuf, size, format, ap); + va_end(ap); + return count; +} +#endif + +// Disable warning C4702 : unreachable code +#if defined(_MSC_VER) +#pragma warning(disable : 4702) #endif -#include // size_t -#include // min() #define JSON_ASSERT_UNREACHABLE assert(false) namespace Json { +template +static std::unique_ptr cloneUnique(const std::unique_ptr& p) { + std::unique_ptr r; + if (p) { + r = std::unique_ptr(new T(*p)); + } + return r; +} // This is a walkaround to avoid the static initialization of Value::null. // kNull must be word-aligned to avoid crashing on ARM. We use an alignment of @@ -2500,50 +2476,34 @@ namespace Json { #else #define ALIGNAS(byte_alignment) #endif -//static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 }; -//const unsigned char& kNullRef = kNull[0]; -//const Value& Value::null = reinterpret_cast(kNullRef); -//const Value& Value::nullRef = null; // static -Value const& Value::nullSingleton() -{ - static Value const nullStatic; - return nullStatic; +Value const& Value::nullSingleton() { + static Value const nullStatic; + return nullStatic; } -// for backwards compatibility, we'll leave these global references around, but DO NOT -// use them in JSONCPP library code any more! +#if JSON_USE_NULLREF +// for backwards compatibility, we'll leave these global references around, but +// DO NOT use them in JSONCPP library code any more! +// static Value const& Value::null = Value::nullSingleton(); -Value const& Value::nullRef = Value::nullSingleton(); -const Int Value::minInt = Int(~(UInt(-1) / 2)); -const Int Value::maxInt = Int(UInt(-1) / 2); -const UInt Value::maxUInt = UInt(-1); -#if defined(JSON_HAS_INT64) -const Int64 Value::minInt64 = Int64(~(UInt64(-1) / 2)); -const Int64 Value::maxInt64 = Int64(UInt64(-1) / 2); -const UInt64 Value::maxUInt64 = UInt64(-1); -// The constant is hard-coded because some compiler have trouble -// converting Value::maxUInt64 to a double correctly (AIX/xlC). -// Assumes that UInt64 is a 64 bits integer. -static const double maxUInt64AsDouble = 18446744073709551615.0; -#endif // defined(JSON_HAS_INT64) -const LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2)); -const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2); -const LargestUInt Value::maxLargestUInt = LargestUInt(-1); +// static +Value const& Value::nullRef = Value::nullSingleton(); +#endif #if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) template static inline bool InRange(double d, T min, U max) { // The casts can lose precision, but we are looking only for // an approximate range. Might fail on edge cases though. ~cdunn - //return d >= static_cast(min) && d <= static_cast(max); - return d >= min && d <= max; + return d >= static_cast(min) && d <= static_cast(max); } #else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) static inline double integerToDouble(Json::UInt64 value) { - return static_cast(Int64(value / 2)) * 2.0 + static_cast(Int64(value & 1)); + return static_cast(Int64(value / 2)) * 2.0 + + static_cast(Int64(value & 1)); } template static inline double integerToDouble(T value) { @@ -2563,19 +2523,16 @@ static inline bool InRange(double d, T min, U max) { * computed using strlen(value). * @return Pointer on the duplicate instance of string. */ -static inline char* duplicateStringValue(const char* value, - size_t length) -{ +static inline char* duplicateStringValue(const char* value, size_t length) { // Avoid an integer overflow in the call to malloc below by limiting length // to a sane value. if (length >= static_cast(Value::maxInt)) length = Value::maxInt - 1; - char* newString = static_cast(malloc(length + 1)); - if (newString == NULL) { - throwRuntimeError( - "in Json::Value::duplicateStringValue(): " - "Failed to allocate string value buffer"); + auto newString = static_cast(malloc(length + 1)); + if (newString == nullptr) { + throwRuntimeError("in Json::Value::duplicateStringValue(): " + "Failed to allocate string value buffer"); } memcpy(newString, value, length); newString[length] = 0; @@ -2584,31 +2541,28 @@ static inline char* duplicateStringValue(const char* value, /* Record the length as a prefix. */ -static inline char* duplicateAndPrefixStringValue( - const char* value, - unsigned int length) -{ +static inline char* duplicateAndPrefixStringValue(const char* value, + unsigned int length) { // Avoid an integer overflow in the call to malloc below by limiting length // to a sane value. - JSON_ASSERT_MESSAGE(length <= static_cast(Value::maxInt) - sizeof(unsigned) - 1U, + JSON_ASSERT_MESSAGE(length <= static_cast(Value::maxInt) - + sizeof(unsigned) - 1U, "in Json::Value::duplicateAndPrefixStringValue(): " "length too big for prefixing"); - unsigned actualLength = length + static_cast(sizeof(unsigned)) + 1U; - char* newString = static_cast(malloc(actualLength)); - if (newString == 0) { - throwRuntimeError( - "in Json::Value::duplicateAndPrefixStringValue(): " - "Failed to allocate string value buffer"); + size_t actualLength = sizeof(length) + length + 1; + auto newString = static_cast(malloc(actualLength)); + if (newString == nullptr) { + throwRuntimeError("in Json::Value::duplicateAndPrefixStringValue(): " + "Failed to allocate string value buffer"); } *reinterpret_cast(newString) = length; memcpy(newString + sizeof(unsigned), value, length); - newString[actualLength - 1U] = 0; // to avoid buffer over-run accidents by users later + newString[actualLength - 1U] = + 0; // to avoid buffer over-run accidents by users later return newString; } -inline static void decodePrefixedString( - bool isPrefixed, char const* prefixed, - unsigned* length, char const** value) -{ +inline static void decodePrefixedString(bool isPrefixed, char const* prefixed, + unsigned* length, char const** value) { if (!isPrefixed) { *length = static_cast(strlen(prefixed)); *value = prefixed; @@ -2617,7 +2571,8 @@ inline static void decodePrefixedString( *value = prefixed + sizeof(unsigned); } } -/** Free the string duplicated by duplicateStringValue()/duplicateAndPrefixStringValue(). +/** Free the string duplicated by + * duplicateStringValue()/duplicateAndPrefixStringValue(). */ #if JSONCPP_USING_SECURE_MEMORY static inline void releasePrefixedStringValue(char* value) { @@ -2630,17 +2585,13 @@ static inline void releasePrefixedStringValue(char* value) { } static inline void releaseStringValue(char* value, unsigned length) { // length==0 => we allocated the strings memory - size_t size = (length==0) ? strlen(value) : length; + size_t size = (length == 0) ? strlen(value) : length; memset(value, 0, size); free(value); } -#else // !JSONCPP_USING_SECURE_MEMORY -static inline void releasePrefixedStringValue(char* value) { - free(value); -} -static inline void releaseStringValue(char* value, unsigned) { - free(value); -} +#else // !JSONCPP_USING_SECURE_MEMORY +static inline void releasePrefixedStringValue(char* value) { free(value); } +static inline void releaseStringValue(char* value, unsigned) { free(value); } #endif // JSONCPP_USING_SECURE_MEMORY } // namespace Json @@ -2659,58 +2610,28 @@ static inline void releaseStringValue(char* value, unsigned) { namespace Json { -Exception::Exception(JSONCPP_STRING const& msg) - : msg_(msg) -{} -Exception::~Exception() JSONCPP_NOEXCEPT -{} -char const* Exception::what() const JSONCPP_NOEXCEPT -{ - return msg_.c_str(); -} -RuntimeError::RuntimeError(JSONCPP_STRING const& msg) - : Exception(msg) -{} -LogicError::LogicError(JSONCPP_STRING const& msg) - : Exception(msg) -{} -JSONCPP_NORETURN void throwRuntimeError(JSONCPP_STRING const& msg) -{ +#if JSON_USE_EXCEPTION +Exception::Exception(String msg) : msg_(std::move(msg)) {} +Exception::~Exception() noexcept = default; +char const* Exception::what() const noexcept { return msg_.c_str(); } +RuntimeError::RuntimeError(String const& msg) : Exception(msg) {} +LogicError::LogicError(String const& msg) : Exception(msg) {} +JSONCPP_NORETURN void throwRuntimeError(String const& msg) { throw RuntimeError(msg); } -JSONCPP_NORETURN void throwLogicError(JSONCPP_STRING const& msg) -{ +JSONCPP_NORETURN void throwLogicError(String const& msg) { throw LogicError(msg); } - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class Value::CommentInfo -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -Value::CommentInfo::CommentInfo() : comment_(0) -{} - -Value::CommentInfo::~CommentInfo() { - if (comment_) - releaseStringValue(comment_, 0u); +#else // !JSON_USE_EXCEPTION +JSONCPP_NORETURN void throwRuntimeError(String const& msg) { + std::cerr << msg << std::endl; + abort(); } - -void Value::CommentInfo::setComment(const char* text, size_t len) { - if (comment_) { - releaseStringValue(comment_, 0u); - comment_ = 0; - } - JSON_ASSERT(text != 0); - JSON_ASSERT_MESSAGE( - text[0] == '\0' || text[0] == '/', - "in Json::Value::setComment(): Comments must start with /"); - // It seems that /**/ style comments are acceptable as well. - comment_ = duplicateStringValue(text, len); +JSONCPP_NORETURN void throwLogicError(String const& msg) { + std::cerr << msg << std::endl; + abort(); } +#endif // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// @@ -2723,36 +2644,44 @@ void Value::CommentInfo::setComment(const char* text, size_t len) { // Notes: policy_ indicates if the string was allocated when // a string is stored. -Value::CZString::CZString(ArrayIndex aindex) : cstr_(0), index_(aindex) {} +Value::CZString::CZString(ArrayIndex index) : cstr_(nullptr), index_(index) {} -Value::CZString::CZString(char const* str, unsigned ulength, DuplicationPolicy allocate) +Value::CZString::CZString(char const* str, unsigned length, + DuplicationPolicy allocate) : cstr_(str) { // allocate != duplicate storage_.policy_ = allocate & 0x3; - storage_.length_ = ulength & 0x3FFFFFFF; + storage_.length_ = length & 0x3FFFFFFF; } Value::CZString::CZString(const CZString& other) { - cstr_ = (other.storage_.policy_ != noDuplication && other.cstr_ != 0 - ? duplicateStringValue(other.cstr_, other.storage_.length_) - : other.cstr_); - storage_.policy_ = static_cast(other.cstr_ - ? (static_cast(other.storage_.policy_) == noDuplication - ? noDuplication : duplicate) - : static_cast(other.storage_.policy_)) & 3U; + cstr_ = (other.storage_.policy_ != noDuplication && other.cstr_ != nullptr + ? duplicateStringValue(other.cstr_, other.storage_.length_) + : other.cstr_); + storage_.policy_ = + static_cast( + other.cstr_ + ? (static_cast(other.storage_.policy_) == + noDuplication + ? noDuplication + : duplicate) + : static_cast(other.storage_.policy_)) & + 3U; storage_.length_ = other.storage_.length_; } -#if JSON_HAS_RVALUE_REFERENCES Value::CZString::CZString(CZString&& other) - : cstr_(other.cstr_), index_(other.index_) { + : cstr_(other.cstr_), index_(other.index_) { other.cstr_ = nullptr; } -#endif Value::CZString::~CZString() { if (cstr_ && storage_.policy_ == duplicate) { - releaseStringValue(const_cast(cstr_), storage_.length_ + 1u); //+1 for null terminating character for sake of completeness but not actually necessary + releaseStringValue(const_cast(cstr_), + storage_.length_ + 1U); // +1 for null terminating + // character for sake of + // completeness but not actually + // necessary } } @@ -2767,36 +2696,39 @@ Value::CZString& Value::CZString::operator=(const CZString& other) { return *this; } -#if JSON_HAS_RVALUE_REFERENCES Value::CZString& Value::CZString::operator=(CZString&& other) { cstr_ = other.cstr_; index_ = other.index_; other.cstr_ = nullptr; return *this; } -#endif bool Value::CZString::operator<(const CZString& other) const { - if (!cstr_) return index_ < other.index_; - //return strcmp(cstr_, other.cstr_) < 0; + if (!cstr_) + return index_ < other.index_; + // return strcmp(cstr_, other.cstr_) < 0; // Assume both are strings. unsigned this_len = this->storage_.length_; unsigned other_len = other.storage_.length_; unsigned min_len = std::min(this_len, other_len); JSON_ASSERT(this->cstr_ && other.cstr_); int comp = memcmp(this->cstr_, other.cstr_, min_len); - if (comp < 0) return true; - if (comp > 0) return false; + if (comp < 0) + return true; + if (comp > 0) + return false; return (this_len < other_len); } bool Value::CZString::operator==(const CZString& other) const { - if (!cstr_) return index_ == other.index_; - //return strcmp(cstr_, other.cstr_) == 0; + if (!cstr_) + return index_ == other.index_; + // return strcmp(cstr_, other.cstr_) == 0; // Assume both are strings. unsigned this_len = this->storage_.length_; unsigned other_len = other.storage_.length_; - if (this_len != other_len) return false; + if (this_len != other_len) + return false; JSON_ASSERT(this->cstr_ && other.cstr_); int comp = memcmp(this->cstr_, other.cstr_, this_len); return comp == 0; @@ -2804,10 +2736,12 @@ bool Value::CZString::operator==(const CZString& other) const { ArrayIndex Value::CZString::index() const { return index_; } -//const char* Value::CZString::c_str() const { return cstr_; } +// const char* Value::CZString::c_str() const { return cstr_; } const char* Value::CZString::data() const { return cstr_; } unsigned Value::CZString::length() const { return storage_.length_; } -bool Value::CZString::isStaticString() const { return storage_.policy_ == noDuplication; } +bool Value::CZString::isStaticString() const { + return storage_.policy_ == noDuplication; +} // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// @@ -2821,10 +2755,10 @@ bool Value::CZString::isStaticString() const { return storage_.policy_ == noDupl * memset( this, 0, sizeof(Value) ) * This optimization is used in ValueInternalMap fast allocator. */ -Value::Value(ValueType vtype) { +Value::Value(ValueType type) { static char const emptyString[] = ""; - initBasic(vtype); - switch (vtype) { + initBasic(type); + switch (type) { case nullValue: break; case intValue: @@ -2877,20 +2811,22 @@ Value::Value(double value) { Value::Value(const char* value) { initBasic(stringValue, true); - JSON_ASSERT_MESSAGE(value != NULL, "Null Value Passed to Value Constructor"); - value_.string_ = duplicateAndPrefixStringValue(value, static_cast(strlen(value))); + JSON_ASSERT_MESSAGE(value != nullptr, + "Null Value Passed to Value Constructor"); + value_.string_ = duplicateAndPrefixStringValue( + value, static_cast(strlen(value))); } -Value::Value(const char* beginValue, const char* endValue) { +Value::Value(const char* begin, const char* end) { initBasic(stringValue, true); value_.string_ = - duplicateAndPrefixStringValue(beginValue, static_cast(endValue - beginValue)); + duplicateAndPrefixStringValue(begin, static_cast(end - begin)); } -Value::Value(const JSONCPP_STRING& value) { +Value::Value(const String& value) { initBasic(stringValue, true); - value_.string_ = - duplicateAndPrefixStringValue(value.data(), static_cast(value.length())); + value_.string_ = duplicateAndPrefixStringValue( + value.data(), static_cast(value.length())); } Value::Value(const StaticString& value) { @@ -2898,114 +2834,44 @@ Value::Value(const StaticString& value) { value_.string_ = const_cast(value.c_str()); } -#ifdef JSON_USE_CPPTL -Value::Value(const CppTL::ConstString& value) { - initBasic(stringValue, true); - value_.string_ = duplicateAndPrefixStringValue(value, static_cast(value.length())); -} -#endif - Value::Value(bool value) { initBasic(booleanValue); value_.bool_ = value; } -Value::Value(Value const& other) - : type_(other.type_), allocated_(false) - , - comments_(0), start_(other.start_), limit_(other.limit_) -{ - switch (type_) { - case nullValue: - case intValue: - case uintValue: - case realValue: - case booleanValue: - value_ = other.value_; - break; - case stringValue: - if (other.value_.string_ && other.allocated_) { - unsigned len; - char const* str; - decodePrefixedString(other.allocated_, other.value_.string_, - &len, &str); - value_.string_ = duplicateAndPrefixStringValue(str, len); - allocated_ = true; - } else { - value_.string_ = other.value_.string_; - allocated_ = false; - } - break; - case arrayValue: - case objectValue: - value_.map_ = new ObjectValues(*other.value_.map_); - break; - default: - JSON_ASSERT_UNREACHABLE; - } - if (other.comments_) { - comments_ = new CommentInfo[numberOfCommentPlacement]; - for (int comment = 0; comment < numberOfCommentPlacement; ++comment) { - const CommentInfo& otherComment = other.comments_[comment]; - if (otherComment.comment_) - comments_[comment].setComment( - otherComment.comment_, strlen(otherComment.comment_)); - } - } +Value::Value(const Value& other) { + dupPayload(other); + dupMeta(other); } -#if JSON_HAS_RVALUE_REFERENCES -// Move constructor Value::Value(Value&& other) { initBasic(nullValue); swap(other); } -#endif Value::~Value() { - switch (type_) { - case nullValue: - case intValue: - case uintValue: - case realValue: - case booleanValue: - break; - case stringValue: - if (allocated_) - releasePrefixedStringValue(value_.string_); - break; - case arrayValue: - case objectValue: - delete value_.map_; - break; - default: - JSON_ASSERT_UNREACHABLE; - } - - delete[] comments_; - + releasePayload(); value_.uint_ = 0; } -Value& Value::operator=(Value other) { - swap(other); +Value& Value::operator=(const Value& other) { + Value(other).swap(*this); + return *this; +} + +Value& Value::operator=(Value&& other) { + other.swap(*this); return *this; } void Value::swapPayload(Value& other) { - ValueType temp = type_; - type_ = other.type_; - other.type_ = temp; + std::swap(bits_, other.bits_); std::swap(value_, other.value_); - int temp2 = allocated_; - allocated_ = other.allocated_; - other.allocated_ = temp2 & 0x1; } void Value::copyPayload(const Value& other) { - type_ = other.type_; - value_ = other.value_; - allocated_ = other.allocated_; + releasePayload(); + dupPayload(other); } void Value::swap(Value& other) { @@ -3017,12 +2883,12 @@ void Value::swap(Value& other) { void Value::copy(const Value& other) { copyPayload(other); - comments_ = other.comments_; - start_ = other.start_; - limit_ = other.limit_; + dupMeta(other); } -ValueType Value::type() const { return type_; } +ValueType Value::type() const { + return static_cast(bits_.value_type_); +} int Value::compare(const Value& other) const { if (*this < other) @@ -3033,10 +2899,10 @@ int Value::compare(const Value& other) const { } bool Value::operator<(const Value& other) const { - int typeDelta = type_ - other.type_; + int typeDelta = type() - other.type(); if (typeDelta) - return typeDelta < 0 ? true : false; - switch (type_) { + return typeDelta < 0; + switch (type()) { case nullValue: return false; case intValue: @@ -3047,30 +2913,33 @@ bool Value::operator<(const Value& other) const { return value_.real_ < other.value_.real_; case booleanValue: return value_.bool_ < other.value_.bool_; - case stringValue: - { - if ((value_.string_ == 0) || (other.value_.string_ == 0)) { - if (other.value_.string_) return true; - else return false; + case stringValue: { + if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) { + return other.value_.string_ != nullptr; } unsigned this_len; unsigned other_len; char const* this_str; char const* other_str; - decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); - decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str); + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); + decodePrefixedString(other.isAllocated(), other.value_.string_, &other_len, + &other_str); unsigned min_len = std::min(this_len, other_len); JSON_ASSERT(this_str && other_str); int comp = memcmp(this_str, other_str, min_len); - if (comp < 0) return true; - if (comp > 0) return false; + if (comp < 0) + return true; + if (comp > 0) + return false; return (this_len < other_len); } case arrayValue: case objectValue: { - int delta = int(value_.map_->size() - other.value_.map_->size()); - if (delta) - return delta < 0; + auto thisSize = value_.map_->size(); + auto otherSize = other.value_.map_->size(); + if (thisSize != otherSize) + return thisSize < otherSize; return (*value_.map_) < (*other.value_.map_); } default: @@ -3086,14 +2955,9 @@ bool Value::operator>=(const Value& other) const { return !(*this < other); } bool Value::operator>(const Value& other) const { return other < *this; } bool Value::operator==(const Value& other) const { - // if ( type_ != other.type_ ) - // GCC 2.95.3 says: - // attempt to take address of bit-field structure member `Json::Value::type_' - // Beats me, but a temp solves the problem. - int temp = other.type_; - if (type_ != temp) + if (type() != other.type()) return false; - switch (type_) { + switch (type()) { case nullValue: return true; case intValue: @@ -3104,18 +2968,20 @@ bool Value::operator==(const Value& other) const { return value_.real_ == other.value_.real_; case booleanValue: return value_.bool_ == other.value_.bool_; - case stringValue: - { - if ((value_.string_ == 0) || (other.value_.string_ == 0)) { + case stringValue: { + if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) { return (value_.string_ == other.value_.string_); } unsigned this_len; unsigned other_len; char const* this_str; char const* other_str; - decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); - decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str); - if (this_len != other_len) return false; + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); + decodePrefixedString(other.isAllocated(), other.value_.string_, &other_len, + &other_str); + if (this_len != other_len) + return false; JSON_ASSERT(this_str && other_str); int comp = memcmp(this_str, other_str, this_len); return comp == 0; @@ -3133,47 +2999,55 @@ bool Value::operator==(const Value& other) const { bool Value::operator!=(const Value& other) const { return !(*this == other); } const char* Value::asCString() const { - JSON_ASSERT_MESSAGE(type_ == stringValue, + JSON_ASSERT_MESSAGE(type() == stringValue, "in Json::Value::asCString(): requires stringValue"); - if (value_.string_ == 0) return 0; + if (value_.string_ == nullptr) + return nullptr; unsigned this_len; char const* this_str; - decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); return this_str; } #if JSONCPP_USING_SECURE_MEMORY unsigned Value::getCStringLength() const { - JSON_ASSERT_MESSAGE(type_ == stringValue, - "in Json::Value::asCString(): requires stringValue"); - if (value_.string_ == 0) return 0; + JSON_ASSERT_MESSAGE(type() == stringValue, + "in Json::Value::asCString(): requires stringValue"); + if (value_.string_ == 0) + return 0; unsigned this_len; char const* this_str; - decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); return this_len; } #endif -bool Value::getString(char const** str, char const** cend) const { - if (type_ != stringValue) return false; - if (value_.string_ == 0) return false; +bool Value::getString(char const** begin, char const** end) const { + if (type() != stringValue) + return false; + if (value_.string_ == nullptr) + return false; unsigned length; - decodePrefixedString(this->allocated_, this->value_.string_, &length, str); - *cend = *str + length; + decodePrefixedString(this->isAllocated(), this->value_.string_, &length, + begin); + *end = *begin + length; return true; } -JSONCPP_STRING Value::asString() const { - switch (type_) { +String Value::asString() const { + switch (type()) { case nullValue: return ""; - case stringValue: - { - if (value_.string_ == 0) return ""; + case stringValue: { + if (value_.string_ == nullptr) + return ""; unsigned this_len; char const* this_str; - decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); - return JSONCPP_STRING(this_str, this_len); + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); + return String(this_str, this_len); } case booleanValue: return value_.bool_ ? "true" : "false"; @@ -3188,18 +3062,8 @@ JSONCPP_STRING Value::asString() const { } } -#ifdef JSON_USE_CPPTL -CppTL::ConstString Value::asConstString() const { - unsigned len; - char const* str; - decodePrefixedString(allocated_, value_.string_, - &len, &str); - return CppTL::ConstString(str, len); -} -#endif - Value::Int Value::asInt() const { - switch (type_) { + switch (type()) { case intValue: JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range"); return Int(value_.int_); @@ -3221,7 +3085,7 @@ Value::Int Value::asInt() const { } Value::UInt Value::asUInt() const { - switch (type_) { + switch (type()) { case intValue: JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range"); return UInt(value_.int_); @@ -3245,7 +3109,7 @@ Value::UInt Value::asUInt() const { #if defined(JSON_HAS_INT64) Value::Int64 Value::asInt64() const { - switch (type_) { + switch (type()) { case intValue: return Int64(value_.int_); case uintValue: @@ -3266,7 +3130,7 @@ Value::Int64 Value::asInt64() const { } Value::UInt64 Value::asUInt64() const { - switch (type_) { + switch (type()) { case intValue: JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range"); return UInt64(value_.int_); @@ -3304,7 +3168,7 @@ LargestUInt Value::asLargestUInt() const { } double Value::asDouble() const { - switch (type_) { + switch (type()) { case intValue: return static_cast(value_.int_); case uintValue: @@ -3326,7 +3190,7 @@ double Value::asDouble() const { } float Value::asFloat() const { - switch (type_) { + switch (type()) { case intValue: return static_cast(value_.int_); case uintValue: @@ -3341,7 +3205,7 @@ float Value::asFloat() const { case nullValue: return 0.0; case booleanValue: - return value_.bool_ ? 1.0f : 0.0f; + return value_.bool_ ? 1.0F : 0.0F; default: break; } @@ -3349,18 +3213,20 @@ float Value::asFloat() const { } bool Value::asBool() const { - switch (type_) { + switch (type()) { case booleanValue: return value_.bool_; case nullValue: return false; case intValue: - return value_.int_ ? true : false; + return value_.int_ != 0; case uintValue: - return value_.uint_ ? true : false; - case realValue: - // This is kind of strange. Not recommended. - return (value_.real_ != 0.0) ? true : false; + return value_.uint_ != 0; + case realValue: { + // According to JavaScript language zero or NaN is regarded as false + const auto value_classification = std::fpclassify(value_.real_); + return value_classification != FP_ZERO && value_classification != FP_NAN; + } default: break; } @@ -3371,30 +3237,30 @@ bool Value::isConvertibleTo(ValueType other) const { switch (other) { case nullValue: return (isNumeric() && asDouble() == 0.0) || - (type_ == booleanValue && value_.bool_ == false) || - (type_ == stringValue && asString().empty()) || - (type_ == arrayValue && value_.map_->size() == 0) || - (type_ == objectValue && value_.map_->size() == 0) || - type_ == nullValue; + (type() == booleanValue && !value_.bool_) || + (type() == stringValue && asString().empty()) || + (type() == arrayValue && value_.map_->empty()) || + (type() == objectValue && value_.map_->empty()) || + type() == nullValue; case intValue: return isInt() || - (type_ == realValue && InRange(value_.real_, minInt, maxInt)) || - type_ == booleanValue || type_ == nullValue; + (type() == realValue && InRange(value_.real_, minInt, maxInt)) || + type() == booleanValue || type() == nullValue; case uintValue: return isUInt() || - (type_ == realValue && InRange(value_.real_, 0, maxUInt)) || - type_ == booleanValue || type_ == nullValue; + (type() == realValue && InRange(value_.real_, 0, maxUInt)) || + type() == booleanValue || type() == nullValue; case realValue: - return isNumeric() || type_ == booleanValue || type_ == nullValue; + return isNumeric() || type() == booleanValue || type() == nullValue; case booleanValue: - return isNumeric() || type_ == booleanValue || type_ == nullValue; + return isNumeric() || type() == booleanValue || type() == nullValue; case stringValue: - return isNumeric() || type_ == booleanValue || type_ == stringValue || - type_ == nullValue; + return isNumeric() || type() == booleanValue || type() == stringValue || + type() == nullValue; case arrayValue: - return type_ == arrayValue || type_ == nullValue; + return type() == arrayValue || type() == nullValue; case objectValue: - return type_ == objectValue || type_ == nullValue; + return type() == objectValue || type() == nullValue; } JSON_ASSERT_UNREACHABLE; return false; @@ -3402,7 +3268,7 @@ bool Value::isConvertibleTo(ValueType other) const { /// Number of values in array or object ArrayIndex Value::size() const { - switch (type_) { + switch (type()) { case nullValue: case intValue: case uintValue: @@ -3426,20 +3292,19 @@ ArrayIndex Value::size() const { bool Value::empty() const { if (isNull() || isArray() || isObject()) - return size() == 0u; - else - return false; + return size() == 0U; + return false; } -Value::operator bool() const { return ! isNull(); } +Value::operator bool() const { return !isNull(); } void Value::clear() { - JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue || - type_ == objectValue, + JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue || + type() == objectValue, "in Json::Value::clear(): requires complex value"); start_ = 0; limit_ = 0; - switch (type_) { + switch (type()) { case arrayValue: case objectValue: value_.map_->clear(); @@ -3450,15 +3315,15 @@ void Value::clear() { } void Value::resize(ArrayIndex newSize) { - JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue, + JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue, "in Json::Value::resize(): requires arrayValue"); - if (type_ == nullValue) + if (type() == nullValue) *this = Value(arrayValue); ArrayIndex oldSize = size(); if (newSize == 0) clear(); else if (newSize > oldSize) - (*this)[newSize - 1]; + this->operator[](newSize - 1); else { for (ArrayIndex index = newSize; index < oldSize; ++index) { value_.map_->erase(index); @@ -3469,12 +3334,12 @@ void Value::resize(ArrayIndex newSize) { Value& Value::operator[](ArrayIndex index) { JSON_ASSERT_MESSAGE( - type_ == nullValue || type_ == arrayValue, + type() == nullValue || type() == arrayValue, "in Json::Value::operator[](ArrayIndex): requires arrayValue"); - if (type_ == nullValue) + if (type() == nullValue) *this = Value(arrayValue); CZString key(index); - ObjectValues::iterator it = value_.map_->lower_bound(key); + auto it = value_.map_->lower_bound(key); if (it != value_.map_->end() && (*it).first == key) return (*it).second; @@ -3492,9 +3357,9 @@ Value& Value::operator[](int index) { const Value& Value::operator[](ArrayIndex index) const { JSON_ASSERT_MESSAGE( - type_ == nullValue || type_ == arrayValue, + type() == nullValue || type() == arrayValue, "in Json::Value::operator[](ArrayIndex)const: requires arrayValue"); - if (type_ == nullValue) + if (type() == nullValue) return nullSingleton(); CZString key(index); ObjectValues::const_iterator it = value_.map_->find(key); @@ -3510,26 +3375,85 @@ const Value& Value::operator[](int index) const { return (*this)[ArrayIndex(index)]; } -void Value::initBasic(ValueType vtype, bool allocated) { - type_ = vtype; - allocated_ = allocated; - comments_ = 0; +void Value::initBasic(ValueType type, bool allocated) { + setType(type); + setIsAllocated(allocated); + comments_ = Comments{}; start_ = 0; limit_ = 0; } +void Value::dupPayload(const Value& other) { + setType(other.type()); + setIsAllocated(false); + switch (type()) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + value_ = other.value_; + break; + case stringValue: + if (other.value_.string_ && other.isAllocated()) { + unsigned len; + char const* str; + decodePrefixedString(other.isAllocated(), other.value_.string_, &len, + &str); + value_.string_ = duplicateAndPrefixStringValue(str, len); + setIsAllocated(true); + } else { + value_.string_ = other.value_.string_; + } + break; + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(*other.value_.map_); + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + +void Value::releasePayload() { + switch (type()) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + break; + case stringValue: + if (isAllocated()) + releasePrefixedStringValue(value_.string_); + break; + case arrayValue: + case objectValue: + delete value_.map_; + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + +void Value::dupMeta(const Value& other) { + comments_ = other.comments_; + start_ = other.start_; + limit_ = other.limit_; +} + // Access an object value by name, create a null member if it does not exist. // @pre Type of '*this' is object or null. // @param key is null-terminated. Value& Value::resolveReference(const char* key) { JSON_ASSERT_MESSAGE( - type_ == nullValue || type_ == objectValue, + type() == nullValue || type() == objectValue, "in Json::Value::resolveReference(): requires objectValue"); - if (type_ == nullValue) + if (type() == nullValue) *this = Value(objectValue); - CZString actualKey( - key, static_cast(strlen(key)), CZString::noDuplication); // NOTE! - ObjectValues::iterator it = value_.map_->lower_bound(actualKey); + CZString actualKey(key, static_cast(strlen(key)), + CZString::noDuplication); // NOTE! + auto it = value_.map_->lower_bound(actualKey); if (it != value_.map_->end() && (*it).first == actualKey) return (*it).second; @@ -3540,16 +3464,15 @@ Value& Value::resolveReference(const char* key) { } // @param key is not null-terminated. -Value& Value::resolveReference(char const* key, char const* cend) -{ +Value& Value::resolveReference(char const* key, char const* end) { JSON_ASSERT_MESSAGE( - type_ == nullValue || type_ == objectValue, + type() == nullValue || type() == objectValue, "in Json::Value::resolveReference(key, end): requires objectValue"); - if (type_ == nullValue) + if (type() == nullValue) *this = Value(objectValue); - CZString actualKey( - key, static_cast(cend-key), CZString::duplicateOnCopy); - ObjectValues::iterator it = value_.map_->lower_bound(actualKey); + CZString actualKey(key, static_cast(end - key), + CZString::duplicateOnCopy); + auto it = value_.map_->lower_bound(actualKey); if (it != value_.map_->end() && (*it).first == actualKey) return (*it).second; @@ -3566,27 +3489,35 @@ Value Value::get(ArrayIndex index, const Value& defaultValue) const { bool Value::isValidIndex(ArrayIndex index) const { return index < size(); } -Value const* Value::find(char const* key, char const* cend) const -{ - JSON_ASSERT_MESSAGE( - type_ == nullValue || type_ == objectValue, - "in Json::Value::find(key, end, found): requires objectValue or nullValue"); - if (type_ == nullValue) return NULL; - CZString actualKey(key, static_cast(cend-key), CZString::noDuplication); +Value const* Value::find(char const* begin, char const* end) const { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue, + "in Json::Value::find(begin, end): requires " + "objectValue or nullValue"); + if (type() == nullValue) + return nullptr; + CZString actualKey(begin, static_cast(end - begin), + CZString::noDuplication); ObjectValues::const_iterator it = value_.map_->find(actualKey); - if (it == value_.map_->end()) return NULL; + if (it == value_.map_->end()) + return nullptr; return &(*it).second; } -const Value& Value::operator[](const char* key) const -{ +Value* Value::demand(char const* begin, char const* end) { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue, + "in Json::Value::demand(begin, end): requires " + "objectValue or nullValue"); + return &resolveReference(begin, end); +} +const Value& Value::operator[](const char* key) const { Value const* found = find(key, key + strlen(key)); - if (!found) return nullSingleton(); + if (!found) + return nullSingleton(); return *found; } -Value const& Value::operator[](JSONCPP_STRING const& key) const -{ +Value const& Value::operator[](const String& key) const { Value const* found = find(key.data(), key.data() + key.length()); - if (!found) return nullSingleton(); + if (!found) + return nullSingleton(); return *found; } @@ -3594,7 +3525,7 @@ Value& Value::operator[](const char* key) { return resolveReference(key, key + strlen(key)); } -Value& Value::operator[](const JSONCPP_STRING& key) { +Value& Value::operator[](const String& key) { return resolveReference(key.data(), key.data() + key.length()); } @@ -3602,178 +3533,140 @@ Value& Value::operator[](const StaticString& key) { return resolveReference(key.c_str()); } -#ifdef JSON_USE_CPPTL -Value& Value::operator[](const CppTL::ConstString& key) { - return resolveReference(key.c_str(), key.end_c_str()); +Value& Value::append(const Value& value) { return append(Value(value)); } + +Value& Value::append(Value&& value) { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue, + "in Json::Value::append: requires arrayValue"); + if (type() == nullValue) { + *this = Value(arrayValue); + } + return this->value_.map_->emplace(size(), std::move(value)).first->second; } -Value const& Value::operator[](CppTL::ConstString const& key) const -{ - Value const* found = find(key.c_str(), key.end_c_str()); - if (!found) return nullSingleton(); - return *found; + +bool Value::insert(ArrayIndex index, const Value& newValue) { + return insert(index, Value(newValue)); } -#endif -Value& Value::append(const Value& value) { return (*this)[size()] = value; } +bool Value::insert(ArrayIndex index, Value&& newValue) { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue, + "in Json::Value::insert: requires arrayValue"); + ArrayIndex length = size(); + if (index > length) { + return false; + } + for (ArrayIndex i = length; i > index; i--) { + (*this)[i] = std::move((*this)[i - 1]); + } + (*this)[index] = std::move(newValue); + return true; +} -#if JSON_HAS_RVALUE_REFERENCES - Value& Value::append(Value&& value) { return (*this)[size()] = std::move(value); } -#endif - -Value Value::get(char const* key, char const* cend, Value const& defaultValue) const -{ - Value const* found = find(key, cend); +Value Value::get(char const* begin, char const* end, + Value const& defaultValue) const { + Value const* found = find(begin, end); return !found ? defaultValue : *found; } -Value Value::get(char const* key, Value const& defaultValue) const -{ +Value Value::get(char const* key, Value const& defaultValue) const { return get(key, key + strlen(key), defaultValue); } -Value Value::get(JSONCPP_STRING const& key, Value const& defaultValue) const -{ +Value Value::get(String const& key, Value const& defaultValue) const { return get(key.data(), key.data() + key.length(), defaultValue); } - -bool Value::removeMember(const char* key, const char* cend, Value* removed) -{ - if (type_ != objectValue) { +bool Value::removeMember(const char* begin, const char* end, Value* removed) { + if (type() != objectValue) { return false; } - CZString actualKey(key, static_cast(cend-key), CZString::noDuplication); - ObjectValues::iterator it = value_.map_->find(actualKey); + CZString actualKey(begin, static_cast(end - begin), + CZString::noDuplication); + auto it = value_.map_->find(actualKey); if (it == value_.map_->end()) return false; - *removed = it->second; + if (removed) + *removed = std::move(it->second); value_.map_->erase(it); return true; } -bool Value::removeMember(const char* key, Value* removed) -{ +bool Value::removeMember(const char* key, Value* removed) { return removeMember(key, key + strlen(key), removed); } -bool Value::removeMember(JSONCPP_STRING const& key, Value* removed) -{ +bool Value::removeMember(String const& key, Value* removed) { return removeMember(key.data(), key.data() + key.length(), removed); } -void Value::removeMember(const char* key) -{ - JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue, +void Value::removeMember(const char* key) { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue, "in Json::Value::removeMember(): requires objectValue"); - if (type_ == nullValue) + if (type() == nullValue) return; CZString actualKey(key, unsigned(strlen(key)), CZString::noDuplication); value_.map_->erase(actualKey); } -void Value::removeMember(const JSONCPP_STRING& key) -{ - removeMember(key.c_str()); -} +void Value::removeMember(const String& key) { removeMember(key.c_str()); } bool Value::removeIndex(ArrayIndex index, Value* removed) { - if (type_ != arrayValue) { + if (type() != arrayValue) { return false; } CZString key(index); - ObjectValues::iterator it = value_.map_->find(key); + auto it = value_.map_->find(key); if (it == value_.map_->end()) { return false; } - *removed = it->second; + if (removed) + *removed = it->second; ArrayIndex oldSize = size(); // shift left all items left, into the place of the "removed" - for (ArrayIndex i = index; i < (oldSize - 1); ++i){ + for (ArrayIndex i = index; i < (oldSize - 1); ++i) { CZString keey(i); (*value_.map_)[keey] = (*this)[i + 1]; } // erase the last one ("leftover") CZString keyLast(oldSize - 1); - ObjectValues::iterator itLast = value_.map_->find(keyLast); + auto itLast = value_.map_->find(keyLast); value_.map_->erase(itLast); return true; } -#ifdef JSON_USE_CPPTL -Value Value::get(const CppTL::ConstString& key, - const Value& defaultValue) const { - return get(key.c_str(), key.end_c_str(), defaultValue); +bool Value::isMember(char const* begin, char const* end) const { + Value const* value = find(begin, end); + return nullptr != value; } -#endif - -bool Value::isMember(char const* key, char const* cend) const -{ - Value const* value = find(key, cend); - return NULL != value; -} -bool Value::isMember(char const* key) const -{ +bool Value::isMember(char const* key) const { return isMember(key, key + strlen(key)); } -bool Value::isMember(JSONCPP_STRING const& key) const -{ +bool Value::isMember(String const& key) const { return isMember(key.data(), key.data() + key.length()); } -#ifdef JSON_USE_CPPTL -bool Value::isMember(const CppTL::ConstString& key) const { - return isMember(key.c_str(), key.end_c_str()); -} -#endif - Value::Members Value::getMemberNames() const { JSON_ASSERT_MESSAGE( - type_ == nullValue || type_ == objectValue, + type() == nullValue || type() == objectValue, "in Json::Value::getMemberNames(), value must be objectValue"); - if (type_ == nullValue) + if (type() == nullValue) return Value::Members(); Members members; members.reserve(value_.map_->size()); ObjectValues::const_iterator it = value_.map_->begin(); ObjectValues::const_iterator itEnd = value_.map_->end(); for (; it != itEnd; ++it) { - members.push_back(JSONCPP_STRING((*it).first.data(), - (*it).first.length())); + members.push_back(String((*it).first.data(), (*it).first.length())); } return members; } -// -//# ifdef JSON_USE_CPPTL -// EnumMemberNames -// Value::enumMemberNames() const -//{ -// if ( type_ == objectValue ) -// { -// return CppTL::Enum::any( CppTL::Enum::transform( -// CppTL::Enum::keys( *(value_.map_), CppTL::Type() ), -// MemberNamesTransform() ) ); -// } -// return EnumMemberNames(); -//} -// -// -// EnumValues -// Value::enumValues() const -//{ -// if ( type_ == objectValue || type_ == arrayValue ) -// return CppTL::Enum::anyValues( *(value_.map_), -// CppTL::Type() ); -// return EnumValues(); -//} -// -//# endif static bool IsIntegral(double d) { double integral_part; return modf(d, &integral_part) == 0.0; } -bool Value::isNull() const { return type_ == nullValue; } +bool Value::isNull() const { return type() == nullValue; } -bool Value::isBool() const { return type_ == booleanValue; } +bool Value::isBool() const { return type() == booleanValue; } bool Value::isInt() const { - switch (type_) { + switch (type()) { case intValue: #if defined(JSON_HAS_INT64) return value_.int_ >= minInt && value_.int_ <= maxInt; @@ -3792,7 +3685,7 @@ bool Value::isInt() const { } bool Value::isUInt() const { - switch (type_) { + switch (type()) { case intValue: #if defined(JSON_HAS_INT64) return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt); @@ -3816,7 +3709,7 @@ bool Value::isUInt() const { bool Value::isInt64() const { #if defined(JSON_HAS_INT64) - switch (type_) { + switch (type()) { case intValue: return true; case uintValue: @@ -3836,7 +3729,7 @@ bool Value::isInt64() const { bool Value::isUInt64() const { #if defined(JSON_HAS_INT64) - switch (type_) { + switch (type()) { case intValue: return value_.int_ >= 0; case uintValue: @@ -3855,61 +3748,92 @@ bool Value::isUInt64() const { } bool Value::isIntegral() const { - switch (type_) { - case intValue: - case uintValue: - return true; - case realValue: + switch (type()) { + case intValue: + case uintValue: + return true; + case realValue: #if defined(JSON_HAS_INT64) - // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a - // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we - // require the value to be strictly less than the limit. - return value_.real_ >= double(minInt64) && value_.real_ < maxUInt64AsDouble && IsIntegral(value_.real_); + // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a + // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= double(minInt64) && + value_.real_ < maxUInt64AsDouble && IsIntegral(value_.real_); #else - return value_.real_ >= minInt && value_.real_ <= maxUInt && IsIntegral(value_.real_); + return value_.real_ >= minInt && value_.real_ <= maxUInt && + IsIntegral(value_.real_); #endif // JSON_HAS_INT64 - default: - break; + default: + break; } return false; } -bool Value::isDouble() const { return type_ == intValue || type_ == uintValue || type_ == realValue; } +bool Value::isDouble() const { + return type() == intValue || type() == uintValue || type() == realValue; +} bool Value::isNumeric() const { return isDouble(); } -bool Value::isString() const { return type_ == stringValue; } +bool Value::isString() const { return type() == stringValue; } -bool Value::isArray() const { return type_ == arrayValue; } +bool Value::isArray() const { return type() == arrayValue; } -bool Value::isObject() const { return type_ == objectValue; } +bool Value::isObject() const { return type() == objectValue; } -void Value::setComment(const char* comment, size_t len, CommentPlacement placement) { - if (!comments_) - comments_ = new CommentInfo[numberOfCommentPlacement]; - if ((len > 0) && (comment[len-1] == '\n')) { - // Always discard trailing newline, to aid indentation. - len -= 1; +Value::Comments::Comments(const Comments& that) + : ptr_{cloneUnique(that.ptr_)} {} + +Value::Comments::Comments(Comments&& that) : ptr_{std::move(that.ptr_)} {} + +Value::Comments& Value::Comments::operator=(const Comments& that) { + ptr_ = cloneUnique(that.ptr_); + return *this; +} + +Value::Comments& Value::Comments::operator=(Comments&& that) { + ptr_ = std::move(that.ptr_); + return *this; +} + +bool Value::Comments::has(CommentPlacement slot) const { + return ptr_ && !(*ptr_)[slot].empty(); +} + +String Value::Comments::get(CommentPlacement slot) const { + if (!ptr_) + return {}; + return (*ptr_)[slot]; +} + +void Value::Comments::set(CommentPlacement slot, String comment) { + if (!ptr_) { + ptr_ = std::unique_ptr(new Array()); + } + // check comments array boundry. + if (slot < CommentPlacement::numberOfCommentPlacement) { + (*ptr_)[slot] = std::move(comment); } - comments_[placement].setComment(comment, len); } -void Value::setComment(const char* comment, CommentPlacement placement) { - setComment(comment, strlen(comment), placement); -} - -void Value::setComment(const JSONCPP_STRING& comment, CommentPlacement placement) { - setComment(comment.c_str(), comment.length(), placement); +void Value::setComment(String comment, CommentPlacement placement) { + if (!comment.empty() && (comment.back() == '\n')) { + // Always discard trailing newline, to aid indentation. + comment.pop_back(); + } + JSON_ASSERT(!comment.empty()); + JSON_ASSERT_MESSAGE( + comment[0] == '\0' || comment[0] == '/', + "in Json::Value::setComment(): Comments must start with /"); + comments_.set(placement, std::move(comment)); } bool Value::hasComment(CommentPlacement placement) const { - return comments_ != 0 && comments_[placement].comment_ != 0; + return comments_.has(placement); } -JSONCPP_STRING Value::getComment(CommentPlacement placement) const { - if (hasComment(placement)) - return comments_[placement].comment_; - return ""; +String Value::getComment(CommentPlacement placement) const { + return comments_.get(placement); } void Value::setOffsetStart(ptrdiff_t start) { start_ = start; } @@ -3920,18 +3844,18 @@ ptrdiff_t Value::getOffsetStart() const { return start_; } ptrdiff_t Value::getOffsetLimit() const { return limit_; } -JSONCPP_STRING Value::toStyledString() const { +String Value::toStyledString() const { StreamWriterBuilder builder; - JSONCPP_STRING out = this->hasComment(commentBefore) ? "\n" : ""; + String out = this->hasComment(commentBefore) ? "\n" : ""; out += Json::writeString(builder, *this); - out += "\n"; + out += '\n'; return out; } Value::const_iterator Value::begin() const { - switch (type_) { + switch (type()) { case arrayValue: case objectValue: if (value_.map_) @@ -3940,11 +3864,11 @@ Value::const_iterator Value::begin() const { default: break; } - return const_iterator(); + return {}; } Value::const_iterator Value::end() const { - switch (type_) { + switch (type()) { case arrayValue: case objectValue: if (value_.map_) @@ -3953,11 +3877,11 @@ Value::const_iterator Value::end() const { default: break; } - return const_iterator(); + return {}; } Value::iterator Value::begin() { - switch (type_) { + switch (type()) { case arrayValue: case objectValue: if (value_.map_) @@ -3970,7 +3894,7 @@ Value::iterator Value::begin() { } Value::iterator Value::end() { - switch (type_) { + switch (type()) { case arrayValue: case objectValue: if (value_.map_) @@ -3985,25 +3909,20 @@ Value::iterator Value::end() { // class PathArgument // ////////////////////////////////////////////////////////////////// -PathArgument::PathArgument() : key_(), index_(), kind_(kindNone) {} +PathArgument::PathArgument() = default; PathArgument::PathArgument(ArrayIndex index) - : key_(), index_(index), kind_(kindIndex) {} + : index_(index), kind_(kindIndex) {} -PathArgument::PathArgument(const char* key) - : key_(key), index_(), kind_(kindKey) {} +PathArgument::PathArgument(const char* key) : key_(key), kind_(kindKey) {} -PathArgument::PathArgument(const JSONCPP_STRING& key) - : key_(key.c_str()), index_(), kind_(kindKey) {} +PathArgument::PathArgument(String key) : key_(std::move(key)), kind_(kindKey) {} // class Path // ////////////////////////////////////////////////////////////////// -Path::Path(const JSONCPP_STRING& path, - const PathArgument& a1, - const PathArgument& a2, - const PathArgument& a3, - const PathArgument& a4, +Path::Path(const String& path, const PathArgument& a1, const PathArgument& a2, + const PathArgument& a3, const PathArgument& a4, const PathArgument& a5) { InArgs in; in.reserve(5); @@ -4015,10 +3934,10 @@ Path::Path(const JSONCPP_STRING& path, makePath(path, in); } -void Path::makePath(const JSONCPP_STRING& path, const InArgs& in) { +void Path::makePath(const String& path, const InArgs& in) { const char* current = path.c_str(); const char* end = current + path.length(); - InArgs::const_iterator itInArg = in.begin(); + auto itInArg = in.begin(); while (current != end) { if (*current == '[') { ++current; @@ -4041,13 +3960,12 @@ void Path::makePath(const JSONCPP_STRING& path, const InArgs& in) { const char* beginName = current; while (current != end && !strchr("[.", *current)) ++current; - args_.push_back(JSONCPP_STRING(beginName, current)); + args_.push_back(String(beginName, current)); } } } -void Path::addPathInArg(const JSONCPP_STRING& /*path*/, - const InArgs& in, +void Path::addPathInArg(const String& /*path*/, const InArgs& in, InArgs::const_iterator& itInArg, PathArgument::Kind kind) { if (itInArg == in.end()) { @@ -4059,30 +3977,29 @@ void Path::addPathInArg(const JSONCPP_STRING& /*path*/, } } -void Path::invalidPath(const JSONCPP_STRING& /*path*/, int /*location*/) { +void Path::invalidPath(const String& /*path*/, int /*location*/) { // Error: invalid path. } const Value& Path::resolve(const Value& root) const { const Value* node = &root; - for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { - const PathArgument& arg = *it; + for (const auto& arg : args_) { if (arg.kind_ == PathArgument::kindIndex) { if (!node->isArray() || !node->isValidIndex(arg.index_)) { - // Error: unable to resolve path (array value expected at position... - return Value::null; + // Error: unable to resolve path (array value expected at position... ) + return Value::nullSingleton(); } node = &((*node)[arg.index_]); } else if (arg.kind_ == PathArgument::kindKey) { if (!node->isObject()) { // Error: unable to resolve path (object value expected at position...) - return Value::null; + return Value::nullSingleton(); } node = &((*node)[arg.key_]); if (node == &Value::nullSingleton()) { // Error: unable to resolve path (object has no member named '' at // position...) - return Value::null; + return Value::nullSingleton(); } } } @@ -4091,8 +4008,7 @@ const Value& Path::resolve(const Value& root) const { Value Path::resolve(const Value& root, const Value& defaultValue) const { const Value* node = &root; - for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { - const PathArgument& arg = *it; + for (const auto& arg : args_) { if (arg.kind_ == PathArgument::kindIndex) { if (!node->isArray() || !node->isValidIndex(arg.index_)) return defaultValue; @@ -4110,8 +4026,7 @@ Value Path::resolve(const Value& root, const Value& defaultValue) const { Value& Path::make(Value& root) const { Value* node = &root; - for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { - const PathArgument& arg = *it; + for (const auto& arg : args_) { if (arg.kind_ == PathArgument::kindIndex) { if (!node->isArray()) { // Error: node is not an array at position ... @@ -4148,71 +4063,81 @@ Value& Path::make(Value& root) const { // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #if !defined(JSON_IS_AMALGAMATION) -#include #include "json_tool.h" +#include #endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include #include #include +#include #include #include -#include -#include -#include + +#if __cplusplus >= 201103L +#include #include -#if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1800 // Between VC++ 6.0 and VC++ 11.0 +#if !defined(isnan) +#define isnan std::isnan +#endif + +#if !defined(isfinite) +#define isfinite std::isfinite +#endif + +#else +#include +#include + +#if defined(_MSC_VER) +#if !defined(isnan) +#include +#define isnan _isnan +#endif + +#if !defined(isfinite) #include #define isfinite _finite -#elif defined(__sun) && defined(__SVR4) //Solaris +#endif + +#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES) +#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1 +#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES + +#endif //_MSC_VER + +#if defined(__sun) && defined(__SVR4) // Solaris #if !defined(isfinite) #include #define isfinite finite #endif -#elif defined(_AIX) -#if !defined(isfinite) -#include -#define isfinite finite #endif -#elif defined(__hpux) + +#if defined(__hpux) #if !defined(isfinite) #if defined(__ia64) && !defined(finite) -#define isfinite(x) ((sizeof(x) == sizeof(float) ? \ - _Isfinitef(x) : _IsFinite(x))) -#else -#include +#define isfinite(x) \ + ((sizeof(x) == sizeof(float) ? _Isfinitef(x) : _IsFinite(x))) +#endif +#endif +#endif + +#if !defined(isnan) +// IEEE standard states that NaN values will not compare to themselves +#define isnan(x) (x != x) +#endif + +#if !defined(__APPLE__) +#if !defined(isfinite) #define isfinite finite #endif #endif -#else -#include -#if !(defined(__QNXNTO__)) // QNX already defines isfinite -#define isfinite std::isfinite -#endif #endif #if defined(_MSC_VER) -#if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above -#define snprintf sprintf_s -#elif _MSC_VER >= 1900 // VC++ 14.0 and above -#define snprintf std::snprintf -#else -#define snprintf _snprintf -#endif -#elif defined(__ANDROID__) || defined(__QNXNTO__) -#define snprintf snprintf -#elif __cplusplus >= 201103L -#if !defined(__MINGW32__) && !defined(__CYGWIN__) -#define snprintf std::snprintf -#endif -#endif - -#if defined(__BORLANDC__) -#include -#define isfinite _finite -#define snprintf _snprintf -#endif - -#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 // Disable warning about strdup being deprecated. #pragma warning(disable : 4996) #endif @@ -4220,12 +4145,12 @@ Value& Path::make(Value& root) const { namespace Json { #if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) -typedef std::unique_ptr StreamWriterPtr; +using StreamWriterPtr = std::unique_ptr; #else -typedef std::auto_ptr StreamWriterPtr; +using StreamWriterPtr = std::auto_ptr; #endif -JSONCPP_STRING valueToString(LargestInt value) { +String valueToString(LargestInt value) { UIntToStringBuffer buffer; char* current = buffer + sizeof(buffer); if (value == Value::minLargestInt) { @@ -4241,7 +4166,7 @@ JSONCPP_STRING valueToString(LargestInt value) { return current; } -JSONCPP_STRING valueToString(LargestUInt value) { +String valueToString(LargestUInt value) { UIntToStringBuffer buffer; char* current = buffer + sizeof(buffer); uintToString(value, current); @@ -4251,67 +4176,70 @@ JSONCPP_STRING valueToString(LargestUInt value) { #if defined(JSON_HAS_INT64) -JSONCPP_STRING valueToString(Int value) { - return valueToString(LargestInt(value)); -} +String valueToString(Int value) { return valueToString(LargestInt(value)); } -JSONCPP_STRING valueToString(UInt value) { - return valueToString(LargestUInt(value)); -} +String valueToString(UInt value) { return valueToString(LargestUInt(value)); } #endif // # if defined(JSON_HAS_INT64) namespace { -JSONCPP_STRING valueToString(double value, bool useSpecialFloats, unsigned int precision) { - // Allocate a buffer that is more than large enough to store the 16 digits of - // precision requested below. - char buffer[36]; - int len = -1; - - char formatString[15]; - snprintf(formatString, sizeof(formatString), "%%.%ug", precision); - +String valueToString(double value, bool useSpecialFloats, + unsigned int precision, PrecisionType precisionType) { // Print into the buffer. We need not request the alternative representation // that always has a decimal point because JSON doesn't distinguish the // concepts of reals and integers. - if (isfinite(value)) { - len = snprintf(buffer, sizeof(buffer), formatString, value); - fixNumericLocale(buffer, buffer + len); - - // try to ensure we preserve the fact that this was given to us as a double on input - if (!strchr(buffer, '.') && !strchr(buffer, 'e')) { - strcat(buffer, ".0"); - } - - } else { - // IEEE standard states that NaN values will not compare to themselves - if (value != value) { - len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "NaN" : "null"); - } else if (value < 0) { - len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "-Infinity" : "-1e+9999"); - } else { - len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "Infinity" : "1e+9999"); - } + if (!isfinite(value)) { + static const char* const reps[2][3] = {{"NaN", "-Infinity", "Infinity"}, + {"null", "-1e+9999", "1e+9999"}}; + return reps[useSpecialFloats ? 0 : 1] + [isnan(value) ? 0 : (value < 0) ? 1 : 2]; + } + + String buffer(size_t(36), '\0'); + while (true) { + int len = jsoncpp_snprintf( + &*buffer.begin(), buffer.size(), + (precisionType == PrecisionType::significantDigits) ? "%.*g" : "%.*f", + precision, value); + assert(len >= 0); + auto wouldPrint = static_cast(len); + if (wouldPrint >= buffer.size()) { + buffer.resize(wouldPrint + 1); + continue; + } + buffer.resize(wouldPrint); + break; + } + + buffer.erase(fixNumericLocale(buffer.begin(), buffer.end()), buffer.end()); + + // strip the zero padding from the right + if (precisionType == PrecisionType::decimalPlaces) { + buffer.erase(fixZerosInTheEnd(buffer.begin(), buffer.end()), buffer.end()); + } + + // try to ensure we preserve the fact that this was given to us as a double on + // input + if (buffer.find('.') == buffer.npos && buffer.find('e') == buffer.npos) { + buffer += ".0"; } - assert(len >= 0); return buffer; } +} // namespace + +String valueToString(double value, unsigned int precision, + PrecisionType precisionType) { + return valueToString(value, false, precision, precisionType); } -JSONCPP_STRING valueToString(double value) { return valueToString(value, false, 17); } +String valueToString(bool value) { return value ? "true" : "false"; } -JSONCPP_STRING valueToString(bool value) { return value ? "true" : "false"; } - -static bool isAnyCharRequiredQuoting(char const* s, size_t n) { +static bool doesAnyCharRequireEscaping(char const* s, size_t n) { assert(s || !n); - char const* const end = s + n; - for (char const* cur = s; cur < end; ++cur) { - if (*cur == '\\' || *cur == '\"' || *cur < ' ' - || static_cast(*cur) < 0x80) - return true; - } - return false; + return std::any_of(s, s + n, [](unsigned char c) { + return c == '\\' || c == '"' || c < 0x20 || c > 0x7F; + }); } static unsigned int utf8ToCodepoint(const char*& s, const char* e) { @@ -4326,8 +4254,8 @@ static unsigned int utf8ToCodepoint(const char*& s, const char* e) { if (e - s < 2) return REPLACEMENT_CHARACTER; - unsigned int calculated = ((firstByte & 0x1F) << 6) - | (static_cast(s[1]) & 0x3F); + unsigned int calculated = + ((firstByte & 0x1F) << 6) | (static_cast(s[1]) & 0x3F); s += 1; // oversized encoded characters are invalid return calculated < 0x80 ? REPLACEMENT_CHARACTER : calculated; @@ -4337,9 +4265,9 @@ static unsigned int utf8ToCodepoint(const char*& s, const char* e) { if (e - s < 3) return REPLACEMENT_CHARACTER; - unsigned int calculated = ((firstByte & 0x0F) << 12) - | ((static_cast(s[1]) & 0x3F) << 6) - | (static_cast(s[2]) & 0x3F); + unsigned int calculated = ((firstByte & 0x0F) << 12) | + ((static_cast(s[1]) & 0x3F) << 6) | + (static_cast(s[2]) & 0x3F); s += 2; // surrogates aren't valid codepoints itself // shouldn't be UTF-8 encoded @@ -4353,10 +4281,10 @@ static unsigned int utf8ToCodepoint(const char*& s, const char* e) { if (e - s < 4) return REPLACEMENT_CHARACTER; - unsigned int calculated = ((firstByte & 0x07) << 24) - | ((static_cast(s[1]) & 0x3F) << 12) - | ((static_cast(s[2]) & 0x3F) << 6) - | (static_cast(s[3]) & 0x3F); + unsigned int calculated = ((firstByte & 0x07) << 18) | + ((static_cast(s[1]) & 0x3F) << 12) | + ((static_cast(s[2]) & 0x3F) << 6) | + (static_cast(s[3]) & 0x3F); s += 3; // oversized encoded characters are invalid return calculated < 0x10000 ? REPLACEMENT_CHARACTER : calculated; @@ -4365,28 +4293,27 @@ static unsigned int utf8ToCodepoint(const char*& s, const char* e) { return REPLACEMENT_CHARACTER; } -static const char hex2[] = - "000102030405060708090a0b0c0d0e0f" - "101112131415161718191a1b1c1d1e1f" - "202122232425262728292a2b2c2d2e2f" - "303132333435363738393a3b3c3d3e3f" - "404142434445464748494a4b4c4d4e4f" - "505152535455565758595a5b5c5d5e5f" - "606162636465666768696a6b6c6d6e6f" - "707172737475767778797a7b7c7d7e7f" - "808182838485868788898a8b8c8d8e8f" - "909192939495969798999a9b9c9d9e9f" - "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" - "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" - "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" - "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" - "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" - "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"; +static const char hex2[] = "000102030405060708090a0b0c0d0e0f" + "101112131415161718191a1b1c1d1e1f" + "202122232425262728292a2b2c2d2e2f" + "303132333435363738393a3b3c3d3e3f" + "404142434445464748494a4b4c4d4e4f" + "505152535455565758595a5b5c5d5e5f" + "606162636465666768696a6b6c6d6e6f" + "707172737475767778797a7b7c7d7e7f" + "808182838485868788898a8b8c8d8e8f" + "909192939495969798999a9b9c9d9e9f" + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" + "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" + "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" + "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"; -static JSONCPP_STRING toHex16Bit(unsigned int x) { +static String toHex16Bit(unsigned int x) { const unsigned int hi = (x >> 8) & 0xff; const unsigned int lo = x & 0xff; - JSONCPP_STRING result(4, ' '); + String result(4, ' '); result[0] = hex2[2 * hi]; result[1] = hex2[2 * hi + 1]; result[2] = hex2[2 * lo]; @@ -4394,18 +4321,26 @@ static JSONCPP_STRING toHex16Bit(unsigned int x) { return result; } -static JSONCPP_STRING valueToQuotedStringN(const char* value, unsigned length) { - if (value == NULL) +static void appendRaw(String& result, unsigned ch) { + result += static_cast(ch); +} + +static void appendHex(String& result, unsigned ch) { + result.append("\\u").append(toHex16Bit(ch)); +} + +static String valueToQuotedStringN(const char* value, unsigned length, + bool emitUTF8 = false) { + if (value == nullptr) return ""; - if (!isAnyCharRequiredQuoting(value, length)) - return JSONCPP_STRING("\"") + value + "\""; + if (!doesAnyCharRequireEscaping(value, length)) + return String("\"") + value + "\""; // We have to walk value and escape any special characters. - // Appending to JSONCPP_STRING is not efficient, but this should be rare. + // Appending to String is not efficient, but this should be rare. // (Note: forward slashes are *not* rare, but I am not escaping them.) - JSONCPP_STRING::size_type maxsize = - length * 2 + 3; // allescaped+quotes+NULL - JSONCPP_STRING result; + String::size_type maxsize = length * 2 + 3; // allescaped+quotes+NULL + String result; result.reserve(maxsize); // to avoid lots of mallocs result += "\""; char const* end = value + length; @@ -4441,45 +4376,50 @@ static JSONCPP_STRING valueToQuotedStringN(const char* value, unsigned length) { // Should add a flag to allow this compatibility mode and prevent this // sequence from occurring. default: { - unsigned int cp = utf8ToCodepoint(c, end); - // don't escape non-control characters - // (short escape sequence are applied above) - if (cp < 0x80 && cp >= 0x20) - result += static_cast(cp); - else if (cp < 0x10000) { // codepoint is in Basic Multilingual Plane - result += "\\u"; - result += toHex16Bit(cp); + if (emitUTF8) { + unsigned codepoint = static_cast(*c); + if (codepoint < 0x20) { + appendHex(result, codepoint); + } else { + appendRaw(result, codepoint); } - else { // codepoint is not in Basic Multilingual Plane - // convert to surrogate pair first - cp -= 0x10000; - result += "\\u"; - result += toHex16Bit((cp >> 10) + 0xD800); - result += "\\u"; - result += toHex16Bit((cp & 0x3FF) + 0xDC00); + } else { + unsigned codepoint = utf8ToCodepoint(c, end); // modifies `c` + if (codepoint < 0x20) { + appendHex(result, codepoint); + } else if (codepoint < 0x80) { + appendRaw(result, codepoint); + } else if (codepoint < 0x10000) { + // Basic Multilingual Plane + appendHex(result, codepoint); + } else { + // Extended Unicode. Encode 20 bits as a surrogate pair. + codepoint -= 0x10000; + appendHex(result, 0xd800 + ((codepoint >> 10) & 0x3ff)); + appendHex(result, 0xdc00 + (codepoint & 0x3ff)); } } - break; + } break; } } result += "\""; return result; } -JSONCPP_STRING valueToQuotedString(const char* value) { +String valueToQuotedString(const char* value) { return valueToQuotedStringN(value, static_cast(strlen(value))); } // Class Writer // ////////////////////////////////////////////////////////////////// -Writer::~Writer() {} +Writer::~Writer() = default; // Class FastWriter // ////////////////////////////////////////////////////////////////// FastWriter::FastWriter() - : yamlCompatibilityEnabled_(false), dropNullPlaceholders_(false), - omitEndingLineFeed_(false) {} + + = default; void FastWriter::enableYAMLCompatibility() { yamlCompatibilityEnabled_ = true; } @@ -4487,11 +4427,11 @@ void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; } void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; } -JSONCPP_STRING FastWriter::write(const Value& root) { +String FastWriter::write(const Value& root) { document_.clear(); writeValue(root); if (!omitEndingLineFeed_) - document_ += "\n"; + document_ += '\n'; return document_; } @@ -4510,13 +4450,13 @@ void FastWriter::writeValue(const Value& value) { case realValue: document_ += valueToString(value.asDouble()); break; - case stringValue: - { + case stringValue: { // Is NULL possible for value.string_? No. char const* str; char const* end; bool ok = value.getString(&str, &end); - if (ok) document_ += valueToQuotedStringN(str, static_cast(end-str)); + if (ok) + document_ += valueToQuotedStringN(str, static_cast(end - str)); break; } case booleanValue: @@ -4535,12 +4475,12 @@ void FastWriter::writeValue(const Value& value) { case objectValue: { Value::Members members(value.getMemberNames()); document_ += '{'; - for (Value::Members::iterator it = members.begin(); it != members.end(); - ++it) { - const JSONCPP_STRING& name = *it; + for (auto it = members.begin(); it != members.end(); ++it) { + const String& name = *it; if (it != members.begin()) document_ += ','; - document_ += valueToQuotedStringN(name.data(), static_cast(name.length())); + document_ += valueToQuotedStringN(name.data(), + static_cast(name.length())); document_ += yamlCompatibilityEnabled_ ? ": " : ":"; writeValue(value[name]); } @@ -4552,17 +4492,16 @@ void FastWriter::writeValue(const Value& value) { // Class StyledWriter // ////////////////////////////////////////////////////////////////// -StyledWriter::StyledWriter() - : rightMargin_(74), indentSize_(3), addChildValues_() {} +StyledWriter::StyledWriter() = default; -JSONCPP_STRING StyledWriter::write(const Value& root) { +String StyledWriter::write(const Value& root) { document_.clear(); addChildValues_ = false; indentString_.clear(); writeCommentBeforeValue(root); writeValue(root); writeCommentAfterValueOnSameLine(root); - document_ += "\n"; + document_ += '\n'; return document_; } @@ -4580,14 +4519,15 @@ void StyledWriter::writeValue(const Value& value) { case realValue: pushValue(valueToString(value.asDouble())); break; - case stringValue: - { + case stringValue: { // Is NULL possible for value.string_? No. char const* str; char const* end; bool ok = value.getString(&str, &end); - if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); - else pushValue(""); + if (ok) + pushValue(valueToQuotedStringN(str, static_cast(end - str))); + else + pushValue(""); break; } case booleanValue: @@ -4603,9 +4543,9 @@ void StyledWriter::writeValue(const Value& value) { else { writeWithIndent("{"); indent(); - Value::Members::iterator it = members.begin(); + auto it = members.begin(); for (;;) { - const JSONCPP_STRING& name = *it; + const String& name = *it; const Value& childValue = value[name]; writeCommentBeforeValue(childValue); writeWithIndent(valueToQuotedString(name.c_str())); @@ -4675,7 +4615,7 @@ bool StyledWriter::isMultilineArray(const Value& value) { for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { const Value& childValue = value[index]; isMultiLine = ((childValue.isArray() || childValue.isObject()) && - childValue.size() > 0); + !childValue.empty()); } if (!isMultiLine) // check if line length > max line length { @@ -4695,7 +4635,7 @@ bool StyledWriter::isMultilineArray(const Value& value) { return isMultiLine; } -void StyledWriter::pushValue(const JSONCPP_STRING& value) { +void StyledWriter::pushValue(const String& value) { if (addChildValues_) childValues_.push_back(value); else @@ -4713,12 +4653,12 @@ void StyledWriter::writeIndent() { document_ += indentString_; } -void StyledWriter::writeWithIndent(const JSONCPP_STRING& value) { +void StyledWriter::writeWithIndent(const String& value) { writeIndent(); document_ += value; } -void StyledWriter::indent() { indentString_ += JSONCPP_STRING(indentSize_, ' '); } +void StyledWriter::indent() { indentString_ += String(indentSize_, ' '); } void StyledWriter::unindent() { assert(indentString_.size() >= indentSize_); @@ -4729,20 +4669,19 @@ void StyledWriter::writeCommentBeforeValue(const Value& root) { if (!root.hasComment(commentBefore)) return; - document_ += "\n"; + document_ += '\n'; writeIndent(); - const JSONCPP_STRING& comment = root.getComment(commentBefore); - JSONCPP_STRING::const_iterator iter = comment.begin(); + const String& comment = root.getComment(commentBefore); + String::const_iterator iter = comment.begin(); while (iter != comment.end()) { document_ += *iter; - if (*iter == '\n' && - ((iter+1) != comment.end() && *(iter + 1) == '/')) + if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/')) writeIndent(); ++iter; } // Comments are stripped of trailing newlines, so add one here - document_ += "\n"; + document_ += '\n'; } void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) { @@ -4750,9 +4689,9 @@ void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) { document_ += " " + root.getComment(commentAfterOnSameLine); if (root.hasComment(commentAfter)) { - document_ += "\n"; + document_ += '\n'; document_ += root.getComment(commentAfter); - document_ += "\n"; + document_ += '\n'; } } @@ -4765,22 +4704,23 @@ bool StyledWriter::hasCommentForValue(const Value& value) { // Class StyledStreamWriter // ////////////////////////////////////////////////////////////////// -StyledStreamWriter::StyledStreamWriter(JSONCPP_STRING indentation) - : document_(NULL), rightMargin_(74), indentation_(indentation), - addChildValues_() {} +StyledStreamWriter::StyledStreamWriter(String indentation) + : document_(nullptr), indentation_(std::move(indentation)), + addChildValues_(), indented_(false) {} -void StyledStreamWriter::write(JSONCPP_OSTREAM& out, const Value& root) { +void StyledStreamWriter::write(OStream& out, const Value& root) { document_ = &out; addChildValues_ = false; indentString_.clear(); indented_ = true; writeCommentBeforeValue(root); - if (!indented_) writeIndent(); + if (!indented_) + writeIndent(); indented_ = true; writeValue(root); writeCommentAfterValueOnSameLine(root); *document_ << "\n"; - document_ = NULL; // Forget the stream, for safety. + document_ = nullptr; // Forget the stream, for safety. } void StyledStreamWriter::writeValue(const Value& value) { @@ -4797,14 +4737,15 @@ void StyledStreamWriter::writeValue(const Value& value) { case realValue: pushValue(valueToString(value.asDouble())); break; - case stringValue: - { + case stringValue: { // Is NULL possible for value.string_? No. char const* str; char const* end; bool ok = value.getString(&str, &end); - if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); - else pushValue(""); + if (ok) + pushValue(valueToQuotedStringN(str, static_cast(end - str))); + else + pushValue(""); break; } case booleanValue: @@ -4820,9 +4761,9 @@ void StyledStreamWriter::writeValue(const Value& value) { else { writeWithIndent("{"); indent(); - Value::Members::iterator it = members.begin(); + auto it = members.begin(); for (;;) { - const JSONCPP_STRING& name = *it; + const String& name = *it; const Value& childValue = value[name]; writeCommentBeforeValue(childValue); writeWithIndent(valueToQuotedString(name.c_str())); @@ -4859,7 +4800,8 @@ void StyledStreamWriter::writeArrayValue(const Value& value) { if (hasChildValue) writeWithIndent(childValues_[index]); else { - if (!indented_) writeIndent(); + if (!indented_) + writeIndent(); indented_ = true; writeValue(childValue); indented_ = false; @@ -4894,7 +4836,7 @@ bool StyledStreamWriter::isMultilineArray(const Value& value) { for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { const Value& childValue = value[index]; isMultiLine = ((childValue.isArray() || childValue.isObject()) && - childValue.size() > 0); + !childValue.empty()); } if (!isMultiLine) // check if line length > max line length { @@ -4914,7 +4856,7 @@ bool StyledStreamWriter::isMultilineArray(const Value& value) { return isMultiLine; } -void StyledStreamWriter::pushValue(const JSONCPP_STRING& value) { +void StyledStreamWriter::pushValue(const String& value) { if (addChildValues_) childValues_.push_back(value); else @@ -4929,8 +4871,9 @@ void StyledStreamWriter::writeIndent() { *document_ << '\n' << indentString_; } -void StyledStreamWriter::writeWithIndent(const JSONCPP_STRING& value) { - if (!indented_) writeIndent(); +void StyledStreamWriter::writeWithIndent(const String& value) { + if (!indented_) + writeIndent(); *document_ << value; indented_ = false; } @@ -4946,13 +4889,13 @@ void StyledStreamWriter::writeCommentBeforeValue(const Value& root) { if (!root.hasComment(commentBefore)) return; - if (!indented_) writeIndent(); - const JSONCPP_STRING& comment = root.getComment(commentBefore); - JSONCPP_STRING::const_iterator iter = comment.begin(); + if (!indented_) + writeIndent(); + const String& comment = root.getComment(commentBefore); + String::const_iterator iter = comment.begin(); while (iter != comment.end()) { *document_ << *iter; - if (*iter == '\n' && - ((iter+1) != comment.end() && *(iter + 1) == '/')) + if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/')) // writeIndent(); // would include newline *document_ << indentString_; ++iter; @@ -4984,84 +4927,73 @@ bool StyledStreamWriter::hasCommentForValue(const Value& value) { struct CommentStyle { /// Decide whether to write comments. enum Enum { - None, ///< Drop all comments. - Most, ///< Recover odd behavior of previous versions (not implemented yet). - All ///< Keep all comments. + None, ///< Drop all comments. + Most, ///< Recover odd behavior of previous versions (not implemented yet). + All ///< Keep all comments. }; }; -struct BuiltStyledStreamWriter : public StreamWriter -{ - BuiltStyledStreamWriter( - JSONCPP_STRING const& indentation, - CommentStyle::Enum cs, - JSONCPP_STRING const& colonSymbol, - JSONCPP_STRING const& nullSymbol, - JSONCPP_STRING const& endingLineFeedSymbol, - bool useSpecialFloats, - unsigned int precision); - int write(Value const& root, JSONCPP_OSTREAM* sout) JSONCPP_OVERRIDE; +struct BuiltStyledStreamWriter : public StreamWriter { + BuiltStyledStreamWriter(String indentation, CommentStyle::Enum cs, + String colonSymbol, String nullSymbol, + String endingLineFeedSymbol, bool useSpecialFloats, + bool emitUTF8, unsigned int precision, + PrecisionType precisionType); + int write(Value const& root, OStream* sout) override; + private: void writeValue(Value const& value); void writeArrayValue(Value const& value); bool isMultilineArray(Value const& value); - void pushValue(JSONCPP_STRING const& value); + void pushValue(String const& value); void writeIndent(); - void writeWithIndent(JSONCPP_STRING const& value); + void writeWithIndent(String const& value); void indent(); void unindent(); void writeCommentBeforeValue(Value const& root); void writeCommentAfterValueOnSameLine(Value const& root); static bool hasCommentForValue(const Value& value); - typedef std::vector ChildValues; + using ChildValues = std::vector; ChildValues childValues_; - JSONCPP_STRING indentString_; + String indentString_; unsigned int rightMargin_; - JSONCPP_STRING indentation_; + String indentation_; CommentStyle::Enum cs_; - JSONCPP_STRING colonSymbol_; - JSONCPP_STRING nullSymbol_; - JSONCPP_STRING endingLineFeedSymbol_; + String colonSymbol_; + String nullSymbol_; + String endingLineFeedSymbol_; bool addChildValues_ : 1; bool indented_ : 1; bool useSpecialFloats_ : 1; + bool emitUTF8_ : 1; unsigned int precision_; + PrecisionType precisionType_; }; BuiltStyledStreamWriter::BuiltStyledStreamWriter( - JSONCPP_STRING const& indentation, - CommentStyle::Enum cs, - JSONCPP_STRING const& colonSymbol, - JSONCPP_STRING const& nullSymbol, - JSONCPP_STRING const& endingLineFeedSymbol, - bool useSpecialFloats, - unsigned int precision) - : rightMargin_(74) - , indentation_(indentation) - , cs_(cs) - , colonSymbol_(colonSymbol) - , nullSymbol_(nullSymbol) - , endingLineFeedSymbol_(endingLineFeedSymbol) - , addChildValues_(false) - , indented_(false) - , useSpecialFloats_(useSpecialFloats) - , precision_(precision) -{ -} -int BuiltStyledStreamWriter::write(Value const& root, JSONCPP_OSTREAM* sout) -{ + String indentation, CommentStyle::Enum cs, String colonSymbol, + String nullSymbol, String endingLineFeedSymbol, bool useSpecialFloats, + bool emitUTF8, unsigned int precision, PrecisionType precisionType) + : rightMargin_(74), indentation_(std::move(indentation)), cs_(cs), + colonSymbol_(std::move(colonSymbol)), nullSymbol_(std::move(nullSymbol)), + endingLineFeedSymbol_(std::move(endingLineFeedSymbol)), + addChildValues_(false), indented_(false), + useSpecialFloats_(useSpecialFloats), emitUTF8_(emitUTF8), + precision_(precision), precisionType_(precisionType) {} +int BuiltStyledStreamWriter::write(Value const& root, OStream* sout) { sout_ = sout; addChildValues_ = false; indented_ = true; indentString_.clear(); writeCommentBeforeValue(root); - if (!indented_) writeIndent(); + if (!indented_) + writeIndent(); indented_ = true; writeValue(root); writeCommentAfterValueOnSameLine(root); *sout_ << endingLineFeedSymbol_; - sout_ = NULL; + sout_ = nullptr; return 0; } void BuiltStyledStreamWriter::writeValue(Value const& value) { @@ -5076,16 +5008,19 @@ void BuiltStyledStreamWriter::writeValue(Value const& value) { pushValue(valueToString(value.asLargestUInt())); break; case realValue: - pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_)); + pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_, + precisionType_)); break; - case stringValue: - { + case stringValue: { // Is NULL is possible for value.string_? No. char const* str; char const* end; bool ok = value.getString(&str, &end); - if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); - else pushValue(""); + if (ok) + pushValue(valueToQuotedStringN(str, static_cast(end - str), + emitUTF8_)); + else + pushValue(""); break; } case booleanValue: @@ -5101,12 +5036,13 @@ void BuiltStyledStreamWriter::writeValue(Value const& value) { else { writeWithIndent("{"); indent(); - Value::Members::iterator it = members.begin(); + auto it = members.begin(); for (;;) { - JSONCPP_STRING const& name = *it; + String const& name = *it; Value const& childValue = value[name]; writeCommentBeforeValue(childValue); - writeWithIndent(valueToQuotedStringN(name.data(), static_cast(name.length()))); + writeWithIndent(valueToQuotedStringN( + name.data(), static_cast(name.length()), emitUTF8_)); *sout_ << colonSymbol_; writeValue(childValue); if (++it == members.end()) { @@ -5140,7 +5076,8 @@ void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { if (hasChildValue) writeWithIndent(childValues_[index]); else { - if (!indented_) writeIndent(); + if (!indented_) + writeIndent(); indented_ = true; writeValue(childValue); indented_ = false; @@ -5158,13 +5095,15 @@ void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { { assert(childValues_.size() == size); *sout_ << "["; - if (!indentation_.empty()) *sout_ << " "; + if (!indentation_.empty()) + *sout_ << " "; for (unsigned index = 0; index < size; ++index) { if (index > 0) *sout_ << ((!indentation_.empty()) ? ", " : ","); *sout_ << childValues_[index]; } - if (!indentation_.empty()) *sout_ << " "; + if (!indentation_.empty()) + *sout_ << " "; *sout_ << "]"; } } @@ -5177,7 +5116,7 @@ bool BuiltStyledStreamWriter::isMultilineArray(Value const& value) { for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { Value const& childValue = value[index]; isMultiLine = ((childValue.isArray() || childValue.isObject()) && - childValue.size() > 0); + !childValue.empty()); } if (!isMultiLine) // check if line length > max line length { @@ -5197,7 +5136,7 @@ bool BuiltStyledStreamWriter::isMultilineArray(Value const& value) { return isMultiLine; } -void BuiltStyledStreamWriter::pushValue(JSONCPP_STRING const& value) { +void BuiltStyledStreamWriter::pushValue(String const& value) { if (addChildValues_) childValues_.push_back(value); else @@ -5216,8 +5155,9 @@ void BuiltStyledStreamWriter::writeIndent() { } } -void BuiltStyledStreamWriter::writeWithIndent(JSONCPP_STRING const& value) { - if (!indented_) writeIndent(); +void BuiltStyledStreamWriter::writeWithIndent(String const& value) { + if (!indented_) + writeIndent(); *sout_ << value; indented_ = false; } @@ -5230,17 +5170,18 @@ void BuiltStyledStreamWriter::unindent() { } void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { - if (cs_ == CommentStyle::None) return; + if (cs_ == CommentStyle::None) + return; if (!root.hasComment(commentBefore)) return; - if (!indented_) writeIndent(); - const JSONCPP_STRING& comment = root.getComment(commentBefore); - JSONCPP_STRING::const_iterator iter = comment.begin(); + if (!indented_) + writeIndent(); + const String& comment = root.getComment(commentBefore); + String::const_iterator iter = comment.begin(); while (iter != comment.end()) { *sout_ << *iter; - if (*iter == '\n' && - ((iter+1) != comment.end() && *(iter + 1) == '/')) + if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/')) // writeIndent(); // would write extra newline *sout_ << indentString_; ++iter; @@ -5248,8 +5189,10 @@ void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { indented_ = false; } -void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) { - if (cs_ == CommentStyle::None) return; +void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine( + Value const& root) { + if (cs_ == CommentStyle::None) + return; if (root.hasComment(commentAfterOnSameLine)) *sout_ << " " + root.getComment(commentAfterOnSameLine); @@ -5269,28 +5212,19 @@ bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) { /////////////// // StreamWriter -StreamWriter::StreamWriter() - : sout_(NULL) -{ -} -StreamWriter::~StreamWriter() -{ -} -StreamWriter::Factory::~Factory() -{} -StreamWriterBuilder::StreamWriterBuilder() -{ - setDefaults(&settings_); -} -StreamWriterBuilder::~StreamWriterBuilder() -{} -StreamWriter* StreamWriterBuilder::newStreamWriter() const -{ - JSONCPP_STRING indentation = settings_["indentation"].asString(); - JSONCPP_STRING cs_str = settings_["commentStyle"].asString(); - bool eyc = settings_["enableYAMLCompatibility"].asBool(); - bool dnp = settings_["dropNullPlaceholders"].asBool(); - bool usf = settings_["useSpecialFloats"].asBool(); +StreamWriter::StreamWriter() : sout_(nullptr) {} +StreamWriter::~StreamWriter() = default; +StreamWriter::Factory::~Factory() = default; +StreamWriterBuilder::StreamWriterBuilder() { setDefaults(&settings_); } +StreamWriterBuilder::~StreamWriterBuilder() = default; +StreamWriter* StreamWriterBuilder::newStreamWriter() const { + const String indentation = settings_["indentation"].asString(); + const String cs_str = settings_["commentStyle"].asString(); + const String pt_str = settings_["precisionType"].asString(); + const bool eyc = settings_["enableYAMLCompatibility"].asBool(); + const bool dnp = settings_["dropNullPlaceholders"].asBool(); + const bool usf = settings_["useSpecialFloats"].asBool(); + const bool emitUTF8 = settings_["emitUTF8"].asBool(); unsigned int pre = settings_["precision"].asUInt(); CommentStyle::Enum cs = CommentStyle::All; if (cs_str == "All") { @@ -5300,74 +5234,80 @@ StreamWriter* StreamWriterBuilder::newStreamWriter() const } else { throwRuntimeError("commentStyle must be 'All' or 'None'"); } - JSONCPP_STRING colonSymbol = " : "; + PrecisionType precisionType(significantDigits); + if (pt_str == "significant") { + precisionType = PrecisionType::significantDigits; + } else if (pt_str == "decimal") { + precisionType = PrecisionType::decimalPlaces; + } else { + throwRuntimeError("precisionType must be 'significant' or 'decimal'"); + } + String colonSymbol = " : "; if (eyc) { colonSymbol = ": "; } else if (indentation.empty()) { colonSymbol = ":"; } - JSONCPP_STRING nullSymbol = "null"; + String nullSymbol = "null"; if (dnp) { nullSymbol.clear(); } - if (pre > 17) pre = 17; - JSONCPP_STRING endingLineFeedSymbol; - return new BuiltStyledStreamWriter( - indentation, cs, - colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre); + if (pre > 17) + pre = 17; + String endingLineFeedSymbol; + return new BuiltStyledStreamWriter(indentation, cs, colonSymbol, nullSymbol, + endingLineFeedSymbol, usf, emitUTF8, pre, + precisionType); } -static void getValidWriterKeys(std::set* valid_keys) -{ - valid_keys->clear(); - valid_keys->insert("indentation"); - valid_keys->insert("commentStyle"); - valid_keys->insert("enableYAMLCompatibility"); - valid_keys->insert("dropNullPlaceholders"); - valid_keys->insert("useSpecialFloats"); - valid_keys->insert("precision"); -} -bool StreamWriterBuilder::validate(Json::Value* invalid) const -{ - Json::Value my_invalid; - if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL - Json::Value& inv = *invalid; - std::set valid_keys; - getValidWriterKeys(&valid_keys); - Value::Members keys = settings_.getMemberNames(); - size_t n = keys.size(); - for (size_t i = 0; i < n; ++i) { - JSONCPP_STRING const& key = keys[i]; - if (valid_keys.find(key) == valid_keys.end()) { - inv[key] = settings_[key]; - } + +bool StreamWriterBuilder::validate(Json::Value* invalid) const { + static const auto& valid_keys = *new std::set{ + "indentation", + "commentStyle", + "enableYAMLCompatibility", + "dropNullPlaceholders", + "useSpecialFloats", + "emitUTF8", + "precision", + "precisionType", + }; + for (auto si = settings_.begin(); si != settings_.end(); ++si) { + auto key = si.name(); + if (valid_keys.count(key)) + continue; + if (invalid) + (*invalid)[std::move(key)] = *si; + else + return false; } - return 0u == inv.size(); + return invalid ? invalid->empty() : true; } -Value& StreamWriterBuilder::operator[](JSONCPP_STRING key) -{ + +Value& StreamWriterBuilder::operator[](const String& key) { return settings_[key]; } // static -void StreamWriterBuilder::setDefaults(Json::Value* settings) -{ +void StreamWriterBuilder::setDefaults(Json::Value* settings) { //! [StreamWriterBuilderDefaults] (*settings)["commentStyle"] = "All"; (*settings)["indentation"] = "\t"; (*settings)["enableYAMLCompatibility"] = false; (*settings)["dropNullPlaceholders"] = false; (*settings)["useSpecialFloats"] = false; + (*settings)["emitUTF8"] = false; (*settings)["precision"] = 17; + (*settings)["precisionType"] = "significant"; //! [StreamWriterBuilderDefaults] } -JSONCPP_STRING writeString(StreamWriter::Factory const& builder, Value const& root) { - JSONCPP_OSTRINGSTREAM sout; - StreamWriterPtr const writer(builder.newStreamWriter()); +String writeString(StreamWriter::Factory const& factory, Value const& root) { + OStringStream sout; + StreamWriterPtr const writer(factory.newStreamWriter()); writer->write(root, &sout); return sout.str(); } -JSONCPP_OSTREAM& operator<<(JSONCPP_OSTREAM& sout, Value const& root) { +OStream& operator<<(OStream& sout, Value const& root) { StreamWriterBuilder builder; StreamWriterPtr const writer(builder.newStreamWriter()); writer->write(root, &sout); diff --git a/minetest.conf.example b/minetest.conf.example index 520125713..6b315b6ea 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -604,26 +604,11 @@ #### Bumpmapping -# Enables bumpmapping for textures. Normalmaps need to be supplied by the texture pack -# or need to be auto-generated. +# Enables bumpmapping for textures. Normalmaps need to be supplied by the texture pack. # Requires shaders to be enabled. # type: bool # enable_bumpmapping = false -# Enables on the fly normalmap generation (Emboss effect). -# Requires bumpmapping to be enabled. -# type: bool -# generate_normalmaps = false - -# Strength of generated normalmaps. -# type: float -# normalmaps_strength = 0.6 - -# Defines sampling step of texture. -# A higher value results in smoother normal maps. -# type: int min: 0 max: 2 -# normalmaps_smooth = 0 - #### Parallax Occlusion # Enables parallax occlusion mapping. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3d6d1b0ea..b8ce69f1d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -351,6 +351,10 @@ else() if (ICONV_LIBRARY) set(PLATFORM_LIBS ${PLATFORM_LIBS} ${ICONV_LIBRARY}) endif() + if (HAIKU) + set(PLATFORM_LIBS ${PLATFORM_LIBS} intl network) + endif() + endif() check_include_files(endian.h HAVE_ENDIAN_H) @@ -673,10 +677,11 @@ endif(BUILD_SERVER) # Blacklisted locales that don't work. # see issue #4638 set(GETTEXT_BLACKLISTED_LOCALES - be + ar he - ko ky + ms_Arab + th ) option(APPLY_LOCALE_BLACKLIST "Use a blacklist to avoid broken locales" TRUE) @@ -697,21 +702,14 @@ include(CheckCSourceCompiles) if(MSVC) # Visual Studio - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D WIN32_LEAN_AND_MEAN /MP") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D WIN32_LEAN_AND_MEAN") # EHa enables SEH exceptions (used for catching segfaults) - set(CMAKE_CXX_FLAGS_RELEASE "/EHa /Ox /GL /FD /MD /GS- /Zi /fp:fast /D NDEBUG /D _HAS_ITERATOR_DEBUGGING=0 /TP") + set(CMAKE_CXX_FLAGS_RELEASE "/EHa /Ox /MD /GS- /Zi /fp:fast /D NDEBUG /D _HAS_ITERATOR_DEBUGGING=0") if(CMAKE_SIZEOF_VOID_P EQUAL 4) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /arch:SSE") endif() - - if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/INCREMENTAL:NO /DEBUG /OPT:REF /OPT:ICF") - else() - set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/LTCG /INCREMENTAL:NO /DEBUG /OPT:REF /OPT:ICF") - endif() - - set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup") + set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/INCREMENTAL:NO /DEBUG /OPT:REF /OPT:ICF /SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup") set(CMAKE_CXX_FLAGS_SEMIDEBUG "/MDd /Zi /Ob0 /O1 /RTC1") @@ -722,6 +720,19 @@ if(MSVC) # Flags for C files (sqlite) # /MD = dynamically link to MSVCRxxx.dll set(CMAKE_C_FLAGS_RELEASE "/O2 /Ob2 /MD") + + # Flags that cannot be shared between cl and clang-cl + # https://clang.llvm.org/docs/UsersManual.html#clang-cl + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fuse-ld=lld") + + # Disable pragma-pack warning + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wno-pragma-pack") + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /TP /FD /GL") + set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG") + endif() else() # GCC or compatible compilers such as Clang set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") diff --git a/src/activeobject.h b/src/activeobject.h index 85e160d10..0829858ad 100644 --- a/src/activeobject.h +++ b/src/activeobject.h @@ -120,9 +120,9 @@ public: virtual void setAttachment(int parent_id, const std::string &bone, v3f position, - v3f rotation) {} + v3f rotation, bool force_visible) {} virtual void getAttachment(int *parent_id, std::string *bone, v3f *position, - v3f *rotation) const {} + v3f *rotation, bool *force_visible) const {} virtual void clearChildAttachments() {} virtual void clearParentAttachment() {} virtual void addAttachmentChild(int child_id) {} diff --git a/src/activeobjectmgr.h b/src/activeobjectmgr.h index 8c4630c79..5139d61dd 100644 --- a/src/activeobjectmgr.h +++ b/src/activeobjectmgr.h @@ -41,12 +41,12 @@ public: m_active_objects.find(id); return (n != m_active_objects.end() ? n->second : nullptr); } - + std::unordered_map getAllActiveObjects() const { return m_active_objects; } - + protected: u16 getFreeId() const { diff --git a/src/chat.cpp b/src/chat.cpp index c3ed59804..2f65e68b3 100644 --- a/src/chat.cpp +++ b/src/chat.cpp @@ -123,16 +123,16 @@ void ChatBuffer::deleteByAge(f32 maxAge) deleteOldest(count); } -u32 ChatBuffer::getColumns() const -{ - return m_cols; -} - u32 ChatBuffer::getRows() const { return m_rows; } +void ChatBuffer::scrollTop() +{ + m_scroll = getTopScrollPos(); +} + void ChatBuffer::reformat(u32 cols, u32 rows) { if (cols == 0 || rows == 0) @@ -220,11 +220,6 @@ void ChatBuffer::scrollBottom() m_scroll = getBottomScrollPos(); } -void ChatBuffer::scrollTop() -{ - m_scroll = getTopScrollPos(); -} - u32 ChatBuffer::formatChatLine(const ChatLine& line, u32 cols, std::vector& destination) const { diff --git a/src/chat.h b/src/chat.h index f84ece206..0b98e4d3c 100644 --- a/src/chat.h +++ b/src/chat.h @@ -94,8 +94,6 @@ public: // Delete lines older than maxAge. void deleteByAge(f32 maxAge); - // Get number of columns, 0 if reformat has not been called yet. - u32 getColumns() const; // Get number of rows, 0 if reformat has not been called yet. u32 getRows() const; // Update console size and reformat all formatted lines. diff --git a/src/client/camera.cpp b/src/client/camera.cpp index abc55e4b7..53c0b351c 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -30,7 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "wieldmesh.h" #include "noise.h" // easeCurve #include "sound.h" -#include "event.h" +#include "mtevent.h" #include "nodedef.h" #include "util/numeric.h" #include "constants.h" @@ -340,14 +340,14 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, f32 tool_r // mods expect the player head to be at the parent's position // plus eye height. if (player->getParent()) - player_position = player->getParent()->getPosition(); + player_position = player->getParent()->getPosition() + v3f(0, g_settings->getBool("float_above_parent") ? BS : 0, 0); // Smooth the camera movement when the player instantly moves upward due to stepheight. // To smooth the 'not touching_ground' stepheight, smoothing is necessary when jumping // or swimming (for when moving from liquid to land). // Disable smoothing if climbing or flying, to avoid upwards offset of player model // when seen in 3rd person view. - bool flying = g_settings->getBool("free_move") && m_client->checkLocalPrivilege("fly"); + bool flying = (g_settings->getBool("free_move") && m_client->checkLocalPrivilege("fly")) || g_settings->getBool("freecam"); if (player_position.Y > old_player_position.Y && !player->is_climbing && !flying) { f32 oldy = old_player_position.Y; f32 newy = player_position.Y; diff --git a/src/client/camera.h b/src/client/camera.h index 3a59637bc..16a1961be 100644 --- a/src/client/camera.h +++ b/src/client/camera.h @@ -169,8 +169,6 @@ public: void removeNametag(Nametag *nametag); - const std::list &getNametags() { return m_nametags; } - void drawNametags(); inline void addArmInertia(f32 player_yaw); diff --git a/src/client/client.cpp b/src/client/client.cpp index 77df29120..8d3ec7e6b 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -130,6 +130,7 @@ Client::Client( if (g_settings->getBool("enable_minimap")) { m_minimap = new Minimap(this); } + m_cache_save_interval = g_settings->getU16("server_map_save_interval"); } @@ -241,18 +242,13 @@ void Client::scanModSubfolder(const std::string &mod_name, const std::string &mo infostream << "Client::scanModSubfolder(): Loading \"" << real_path << "\" as \"" << vfs_path << "\"." << std::endl; - std::ifstream is(real_path, std::ios::binary | std::ios::ate); - if(!is.good()) { + std::string contents; + if (!fs::ReadFile(real_path, contents)) { errorstream << "Client::scanModSubfolder(): Can't read file \"" << real_path << "\"." << std::endl; continue; } - auto size = is.tellg(); - std::string contents(size, '\0'); - is.seekg(0); - is.read(&contents[0], size); - infostream << " size: " << size << " bytes" << std::endl; m_mod_vfs.emplace(vfs_path, contents); } } @@ -333,6 +329,8 @@ Client::~Client() } delete m_minimap; + m_minimap = nullptr; + delete m_media_downloader; } @@ -1310,7 +1308,7 @@ void Client::sendPlayerPos(v3f pos) player->last_pitch == player->getPitch() && player->last_yaw == player->getYaw() && player->last_keyPressed == player->keyPressed && - player->last_camera_fov == camera_fov && + player->last_camera_fov == camera_fov && player->last_wanted_range == wanted_range) return; @@ -1671,15 +1669,13 @@ void Client::updateAllMapBlocks() for (s16 Z = currentBlock.Z - 2; Z <= currentBlock.Z + 2; Z++) addUpdateMeshTask(v3s16(X, Y, Z), false, true); - std::map *sectors = m_env.getMap().getSectorsPtr(); + Map &map = m_env.getMap(); - for (auto §or_it : *sectors) { - MapSector *sector = sector_it.second; - MapBlockVect blocks; - sector->getBlocks(blocks); - for (MapBlock *block : blocks) { - addUpdateMeshTask(block->getPos(), false, false); - } + std::vector positions; + map.listAllLoadedBlocks(positions); + + for (v3s16 p : positions) { + addUpdateMeshTask(p, false, false); } } @@ -1693,11 +1689,6 @@ ClientEvent *Client::getClientEvent() return event; } -bool Client::connectedToServer() -{ - return m_con->Connected(); -} - const Address Client::getServerAddress() { return m_con->GetPeerAddress(PEER_ID_SERVER); diff --git a/src/client/client.h b/src/client/client.h index c6e32264f..e9ef20f48 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -224,6 +224,7 @@ public: void handleCommand_CSMRestrictionFlags(NetworkPacket *pkt); void handleCommand_PlayerSpeed(NetworkPacket *pkt); void handleCommand_MediaPush(NetworkPacket *pkt); + void handleCommand_MinimapModes(NetworkPacket *pkt); void ProcessData(NetworkPacket *pkt); @@ -340,7 +341,6 @@ public: u16 getProtoVersion() { return m_proto_ver; } - bool connectedToServer(); void confirmRegistration(); bool m_is_registration_confirmation_state = false; bool m_simple_singleplayer_mode; diff --git a/src/client/clientenvironment.cpp b/src/client/clientenvironment.cpp index 12c732c27..d584bb694 100644 --- a/src/client/clientenvironment.cpp +++ b/src/client/clientenvironment.cpp @@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "clientmap.h" #include "scripting_client.h" #include "mapblock_mesh.h" -#include "event.h" +#include "mtevent.h" #include "collision.h" #include "nodedef.h" #include "profiler.h" @@ -147,8 +147,8 @@ void ClientEnvironment::step(float dtime) stepTimeOfDay(dtime); // Get some settings - bool fly_allowed = m_client->checkLocalPrivilege("fly"); - bool free_move = fly_allowed && g_settings->getBool("free_move"); + bool fly_allowed = m_client->checkLocalPrivilege("fly") || g_settings->getBool("freecam"); + bool free_move = (fly_allowed && g_settings->getBool("free_move")) || g_settings->getBool("freecam"); // Get local player LocalPlayer *lplayer = getLocalPlayer(); @@ -183,84 +183,61 @@ void ClientEnvironment::step(float dtime) if(dtime > 0.5) dtime = 0.5; - f32 dtime_downcount = dtime; - /* Stuff that has a maximum time increment */ - u32 loopcount = 0; - do - { - loopcount++; + u32 steps = ceil(dtime / dtime_max_increment); + f32 dtime_part = dtime / steps; + for (; steps > 0; --steps) { + /* + Local player handling + */ - f32 dtime_part; - if(dtime_downcount > dtime_max_increment) - { - dtime_part = dtime_max_increment; - dtime_downcount -= dtime_part; - } - else - { - dtime_part = dtime_downcount; - /* - Setting this to 0 (no -=dtime_part) disables an infinite loop - when dtime_part is so small that dtime_downcount -= dtime_part - does nothing - */ - dtime_downcount = 0; + // Control local player + lplayer->applyControl(dtime_part, this); + + // Apply physics + if (!free_move && !is_climbing && !g_settings->getBool("freecam")) { + // Gravity + v3f speed = lplayer->getSpeed(); + if (!lplayer->in_liquid) + speed.Y -= lplayer->movement_gravity * + lplayer->physics_override_gravity * dtime_part * 2.0f; + + // Liquid floating / sinking + if (lplayer->in_liquid && !lplayer->swimming_vertical && + !lplayer->swimming_pitch) + speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2.0f; + + // Liquid resistance + if (lplayer->in_liquid_stable || lplayer->in_liquid) { + // How much the node's viscosity blocks movement, ranges + // between 0 and 1. Should match the scale at which viscosity + // increase affects other liquid attributes. + static const f32 viscosity_factor = 0.3f; + + v3f d_wanted = -speed / lplayer->movement_liquid_fluidity; + f32 dl = d_wanted.getLength(); + if (dl > lplayer->movement_liquid_fluidity_smooth) + dl = lplayer->movement_liquid_fluidity_smooth; + + dl *= (lplayer->liquid_viscosity * viscosity_factor) + + (1 - viscosity_factor); + v3f d = d_wanted.normalize() * (dl * dtime_part * 100.0f); + speed += d; + } + + lplayer->setSpeed(speed); } /* - Handle local player + Move the lplayer. + This also does collision detection. */ - - { - // Control local player - lplayer->applyControl(dtime_part, this); - - // Apply physics - if (!free_move && !is_climbing && ! g_settings->getBool("freecam")) { - // Gravity - v3f speed = lplayer->getSpeed(); - if (!lplayer->in_liquid) - speed.Y -= lplayer->movement_gravity * - lplayer->physics_override_gravity * dtime_part * 2.0f; - - // Liquid floating / sinking - if (lplayer->in_liquid && !lplayer->swimming_vertical && - !lplayer->swimming_pitch) - speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2.0f; - - // Liquid resistance - if (lplayer->in_liquid_stable || lplayer->in_liquid) { - // How much the node's viscosity blocks movement, ranges - // between 0 and 1. Should match the scale at which viscosity - // increase affects other liquid attributes. - static const f32 viscosity_factor = 0.3f; - - v3f d_wanted = -speed / lplayer->movement_liquid_fluidity; - f32 dl = d_wanted.getLength(); - if (dl > lplayer->movement_liquid_fluidity_smooth) - dl = lplayer->movement_liquid_fluidity_smooth; - - dl *= (lplayer->liquid_viscosity * viscosity_factor) + - (1 - viscosity_factor); - v3f d = d_wanted.normalize() * (dl * dtime_part * 100.0f); - speed += d; - } - - lplayer->setSpeed(speed); - } - - /* - Move the lplayer. - This also does collision detection. - */ - lplayer->move(dtime_part, this, position_max_increment, - &player_collisions); - } - } while (dtime_downcount > 0.001); + lplayer->move(dtime_part, this, position_max_increment, + &player_collisions); + } bool player_immortal = lplayer->getCAO() && lplayer->getCAO()->isImmortal(); @@ -368,21 +345,6 @@ bool isFreeClientActiveObjectId(const u16 id, } -u16 getFreeClientActiveObjectId(ClientActiveObjectMap &objects) -{ - // try to reuse id's as late as possible - static u16 last_used_id = 0; - u16 startid = last_used_id; - for(;;) { - last_used_id ++; - if (isFreeClientActiveObjectId(last_used_id, objects)) - return last_used_id; - - if (last_used_id == startid) - return 0; - } -} - u16 ClientEnvironment::addActiveObject(ClientActiveObject *object) { // Register object. If failed return zero id diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index ce16797e6..29427f609 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -327,13 +327,13 @@ void ClientLauncher::init_args(GameStartData &start_data, const Settings &cmd_ar // Join a remote server start_data.address = cmd_args.get("address"); start_data.world_path.clear(); + start_data.name = g_settings->get("name"); } if (!start_data.world_path.empty()) { // Start a singleplayer instance start_data.address = ""; } - start_data.name = g_settings->get("name"); if (cmd_args.exists("name")) start_data.name = cmd_args.get("name"); @@ -419,7 +419,6 @@ bool ClientLauncher::launch_game(std::string &error_message, /* Show the GUI menu */ std::string server_name, server_description; - start_data.local_server = false; if (!skip_main_menu) { // Initialize menu data // TODO: Re-use existing structs (GameStartData) @@ -467,6 +466,9 @@ bool ClientLauncher::launch_game(std::string &error_message, start_data.local_server = !menudata.simple_singleplayer_mode && start_data.address.empty(); + } else { + start_data.local_server = !start_data.world_path.empty() && + start_data.address.empty() && !start_data.name.empty(); } if (!RenderingEngine::run()) diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp index d41b66741..937c38bf1 100644 --- a/src/client/clientmap.cpp +++ b/src/client/clientmap.cpp @@ -36,7 +36,7 @@ ClientMap::ClientMap( MapDrawControl &control, s32 id ): - Map(dout_client, client), + Map(client), scene::ISceneNode(RenderingEngine::get_scene_manager()->getRootSceneNode(), RenderingEngine::get_scene_manager(), id), m_client(client), @@ -122,20 +122,20 @@ void ClientMap::updateDrawList() } m_drawlist.clear(); - v3f camera_position = m_camera_position; - v3f camera_direction = m_camera_direction; - f32 camera_fov = m_camera_fov; + const v3f camera_position = m_camera_position; + const v3f camera_direction = m_camera_direction; // Use a higher fov to accomodate faster camera movements. // Blocks are cropped better when they are drawn. - // Or maybe they aren't? Well whatever. - camera_fov *= 1.2; + const f32 camera_fov = m_camera_fov * 1.1f; v3s16 cam_pos_nodes = floatToInt(camera_position, BS); v3s16 p_blocks_min; v3s16 p_blocks_max; getBlocksInViewRange(cam_pos_nodes, &p_blocks_min, &p_blocks_max); + // Number of blocks currently loaded by the client + u32 blocks_loaded = 0; // Number of blocks with mesh in rendering range u32 blocks_in_range_with_mesh = 0; // Number of blocks occlusion culled @@ -144,7 +144,7 @@ void ClientMap::updateDrawList() // No occlusion culling when free_move is on and camera is // inside ground bool occlusion_culling_enabled = true; - if (g_settings->getBool("free_move") && g_settings->getBool("noclip")) { + if ((g_settings->getBool("free_move") && g_settings->getBool("noclip")) || g_settings->getBool("freecam")) { MapNode n = getNode(cam_pos_nodes); if (n.getContent() == CONTENT_IGNORE || m_nodedef->get(n).solidness == 2) @@ -160,6 +160,7 @@ void ClientMap::updateDrawList() MapSector *sector = sector_it.second; v2s16 sp = sector->getPos(); + blocks_loaded += sector->size(); if (!m_control.range_all) { if (sp.X < p_blocks_min.X || sp.X > p_blocks_max.X || sp.Y < p_blocks_min.Z || sp.Y > p_blocks_max.Z) @@ -181,8 +182,12 @@ void ClientMap::updateDrawList() if not seen on display */ - if (block->mesh) + if (block->mesh) { block->mesh->updateCameraOffset(m_camera_offset); + } else { + // Ignore if mesh doesn't exist + continue; + } float range = 100000 * BS; if (!m_control.range_all) @@ -193,13 +198,6 @@ void ClientMap::updateDrawList() camera_direction, camera_fov, range, &d)) continue; - - /* - Ignore if mesh doesn't exist - */ - if (!block->mesh) - continue; - blocks_in_range_with_mesh++; /* @@ -228,6 +226,7 @@ void ClientMap::updateDrawList() g_profiler->avg("MapBlock meshes in range [#]", blocks_in_range_with_mesh); g_profiler->avg("MapBlocks occlusion culled [#]", blocks_occlusion_culled); g_profiler->avg("MapBlocks drawn [#]", m_drawlist.size()); + g_profiler->avg("MapBlocks loaded [#]", blocks_loaded); } struct MeshBufList @@ -293,13 +292,13 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) /* Get animation parameters */ - float animation_time = m_client->getAnimationTime(); - int crack = m_client->getCrackLevel(); - u32 daynight_ratio = m_client->getEnv().getDayNightRatio(); + const float animation_time = m_client->getAnimationTime(); + const int crack = m_client->getCrackLevel(); + const u32 daynight_ratio = m_client->getEnv().getDayNightRatio(); - v3f camera_position = m_camera_position; - v3f camera_direction = m_camera_direction; - f32 camera_fov = m_camera_fov; + const v3f camera_position = m_camera_position; + const v3f camera_direction = m_camera_direction; + const f32 camera_fov = m_camera_fov; /* Get all blocks and draw all visible ones @@ -589,7 +588,7 @@ void ClientMap::renderPostFx(CameraMode cam_mode) const ContentFeatures& features = m_nodedef->get(n); video::SColor post_effect_color = features.post_effect_color; if(features.solidness == 2 && !((g_settings->getBool("noclip") || g_settings->getBool("freecam")) && - m_client->checkLocalPrivilege("noclip")) && + (m_client->checkLocalPrivilege("noclip") || g_settings->getBool("freecam"))) && cam_mode == CAMERA_MODE_FIRST) { post_effect_color = video::SColor(255, 0, 0, 0); diff --git a/src/client/clientmedia.cpp b/src/client/clientmedia.cpp index 8cd3b6bcc..c4c08c05d 100644 --- a/src/client/clientmedia.cpp +++ b/src/client/clientmedia.cpp @@ -260,7 +260,8 @@ void ClientMediaDownloader::initialStep(Client *client) fetch_request.request_id = m_httpfetch_next_id; // == i fetch_request.timeout = m_httpfetch_timeout; fetch_request.connect_timeout = m_httpfetch_timeout; - fetch_request.post_data = required_hash_set; + fetch_request.method = HTTP_POST; + fetch_request.raw_data = required_hash_set; fetch_request.extra_headers.emplace_back( "Content-Type: application/octet-stream"); diff --git a/src/client/clientobject.h b/src/client/clientobject.h index ecd8059ef..4a1743d72 100644 --- a/src/client/clientobject.h +++ b/src/client/clientobject.h @@ -21,6 +21,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes_extrabloated.h" #include "activeobject.h" +#include +#include +#include +#include +#include #include #include diff --git a/src/client/clouds.cpp b/src/client/clouds.cpp index 887a62f25..253dee8b9 100644 --- a/src/client/clouds.cpp +++ b/src/client/clouds.cpp @@ -170,8 +170,9 @@ void Clouds::render() // Read noise - bool *grid = new bool[m_cloud_radius_i * 2 * m_cloud_radius_i * 2]; - + std::vector grid(m_cloud_radius_i * 2 * m_cloud_radius_i * 2); // vector is broken + std::vector vertices; + vertices.reserve(16 * m_cloud_radius_i * m_cloud_radius_i); for(s16 zi = -m_cloud_radius_i; zi < m_cloud_radius_i; zi++) { u32 si = (zi + m_cloud_radius_i) * m_cloud_radius_i * 2 + m_cloud_radius_i; @@ -195,12 +196,7 @@ void Clouds::render() { s16 zi = zi0; s16 xi = xi0; - // Draw from front to back (needed for transparency) - /*if(zi <= 0) - zi = -m_cloud_radius_i - zi; - if(xi <= 0) - xi = -m_cloud_radius_i - xi;*/ - // Draw from back to front + // Draw from back to front for proper transparency if(zi >= 0) zi = m_cloud_radius_i - zi - 1; if(xi >= 0) @@ -220,17 +216,10 @@ void Clouds::render() video::S3DVertex(0,0,0, 0,0,0, c_top, 0, 0) }; - /*if(zi <= 0 && xi <= 0){ - v[0].Color.setBlue(255); - v[1].Color.setBlue(255); - v[2].Color.setBlue(255); - v[3].Color.setBlue(255); - }*/ - - f32 rx = cloud_size / 2.0f; + const f32 rx = cloud_size / 2.0f; // if clouds are flat, the top layer should be at the given height - f32 ry = m_enable_3d ? m_params.thickness * BS : 0.0f; - f32 rz = cloud_size / 2; + const f32 ry = m_enable_3d ? m_params.thickness * BS : 0.0f; + const f32 rz = cloud_size / 2; for(int i=0; idrawVertexPrimitiveList(v, 4, indices, 2, - video::EVT_STANDARD, scene::EPT_TRIANGLES, video::EIT_16BIT); + vertices.push_back(vertex); + } } } - - delete[] grid; + int quad_count = vertices.size() / 4; + std::vector indices; + indices.reserve(quad_count * 6); + for (int k = 0; k < quad_count; k++) { + indices.push_back(4 * k + 0); + indices.push_back(4 * k + 1); + indices.push_back(4 * k + 2); + indices.push_back(4 * k + 2); + indices.push_back(4 * k + 3); + indices.push_back(4 * k + 0); + } + driver->drawVertexPrimitiveList(vertices.data(), vertices.size(), indices.data(), 2 * quad_count, + video::EVT_STANDARD, scene::EPT_TRIANGLES, video::EIT_16BIT); // Restore fog settings driver->setFog(fog_color, fog_type, fog_start, fog_end, fog_density, @@ -342,14 +341,13 @@ void Clouds::step(float dtime) void Clouds::update(const v3f &camera_p, const video::SColorf &color_diffuse) { + video::SColorf ambient(m_params.color_ambient); + video::SColorf bright(m_params.color_bright); m_camera_pos = camera_p; - m_color.r = MYMIN(MYMAX(color_diffuse.r * m_params.color_bright.getRed(), - m_params.color_ambient.getRed()), 255) / 255.0f; - m_color.g = MYMIN(MYMAX(color_diffuse.g * m_params.color_bright.getGreen(), - m_params.color_ambient.getGreen()), 255) / 255.0f; - m_color.b = MYMIN(MYMAX(color_diffuse.b * m_params.color_bright.getBlue(), - m_params.color_ambient.getBlue()), 255) / 255.0f; - m_color.a = m_params.color_bright.getAlpha() / 255.0f; + m_color.r = core::clamp(color_diffuse.r * bright.r, ambient.r, 1.0f); + m_color.g = core::clamp(color_diffuse.g * bright.g, ambient.g, 1.0f); + m_color.b = core::clamp(color_diffuse.b * bright.b, ambient.b, 1.0f); + m_color.a = bright.a; // is the camera inside the cloud mesh? m_camera_inside_cloud = false; // default diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 9ad83ab93..60dd28503 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -47,6 +47,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include "client/shader.h" +#include "script/scripting_client.h" +#include "client/minimap.h" class Settings; struct ToolCapabilities; @@ -353,6 +355,8 @@ void GenericCAO::initialize(const std::string &data) m_is_local_player = true; m_is_visible = false; player->setCAO(this); + + m_prop.show_on_minimap = false; } } @@ -371,7 +375,7 @@ void GenericCAO::processInitData(const std::string &data) } // PROTOCOL_VERSION >= 37 - m_name = deSerializeString(is); + m_name = deSerializeString16(is); m_is_player = readU8(is); m_id = readU16(is); m_position = readV3F32(is); @@ -381,7 +385,7 @@ void GenericCAO::processInitData(const std::string &data) const u8 num_messages = readU8(is); for (int i = 0; i < num_messages; i++) { - std::string message = deSerializeLongString(is); + std::string message = deSerializeString32(is); processMessage(message); } @@ -456,18 +460,21 @@ void GenericCAO::setChildrenVisible(bool toset) for (u16 cao_id : m_attachment_child_ids) { GenericCAO *obj = m_env->getGenericCAO(cao_id); if (obj) { - obj->setVisible(toset); + // Check if the entity is forced to appear in first person. + obj->setVisible(obj->m_force_visible ? true : toset); } } } -void GenericCAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation) +void GenericCAO::setAttachment(int parent_id, const std::string &bone, + v3f position, v3f rotation, bool force_visible) { int old_parent = m_attachment_parent_id; m_attachment_parent_id = parent_id; m_attachment_bone = bone; m_attachment_position = position; m_attachment_rotation = rotation; + m_force_visible = force_visible; ClientActiveObject *parent = m_env->getActiveObject(parent_id); @@ -481,15 +488,30 @@ void GenericCAO::setAttachment(int parent_id, const std::string &bone, v3f posit } updateAttachments(); + + // Forcibly show attachments if required by set_attach + if (m_force_visible) { + m_is_visible = true; + } else if (!m_is_local_player) { + // Objects attached to the local player should be hidden in first person + m_is_visible = !m_attached_to_local || + m_client->getCamera()->getCameraMode() != CAMERA_MODE_FIRST || g_settings->getBool("freecam"); + m_force_visible = false; + } else { + // Local players need to have this set, + // otherwise first person attachments fail. + m_is_visible = true; + } } void GenericCAO::getAttachment(int *parent_id, std::string *bone, v3f *position, - v3f *rotation) const + v3f *rotation, bool *force_visible) const { *parent_id = m_attachment_parent_id; *bone = m_attachment_bone; *position = m_attachment_position; *rotation = m_attachment_rotation; + *force_visible = m_force_visible; } void GenericCAO::clearChildAttachments() @@ -499,7 +521,7 @@ void GenericCAO::clearChildAttachments() int child_id = *m_attachment_child_ids.begin(); if (ClientActiveObject *child = m_env->getActiveObject(child_id)) - child->setAttachment(0, "", v3f(), v3f()); + child->setAttachment(0, "", v3f(), v3f(), false); removeAttachmentChild(child_id); } @@ -508,9 +530,9 @@ void GenericCAO::clearChildAttachments() void GenericCAO::clearParentAttachment() { if (m_attachment_parent_id) - setAttachment(0, "", m_attachment_position, m_attachment_rotation); + setAttachment(0, "", m_attachment_position, m_attachment_rotation, false); else - setAttachment(0, "", v3f(), v3f()); + setAttachment(0, "", v3f(), v3f(), false); } void GenericCAO::addAttachmentChild(int child_id) @@ -568,6 +590,9 @@ void GenericCAO::removeFromScene(bool permanent) m_client->getCamera()->removeNametag(m_nametag); m_nametag = nullptr; } + + if (m_marker && m_client->getMinimap()) + m_client->getMinimap()->removeMarker(&m_marker); } void GenericCAO::addToScene(ITextureSource *tsrc) @@ -796,11 +821,13 @@ void GenericCAO::addToScene(ITextureSource *tsrc) node->setParent(m_matrixnode); updateNametag(); + updateMarker(); updateNodePos(); updateAnimation(); updateBonePosition(); updateAttachments(); setNodeLight(m_last_light); + updateMeshCulling(); } void GenericCAO::updateLight(u32 day_night_ratio) @@ -829,6 +856,8 @@ void GenericCAO::updateLight(u32 day_night_ratio) light_at_pos = blend_light(day_night_ratio, LIGHT_SUN, 0); u8 light = decode_light(light_at_pos + m_glow); + if (g_settings->getBool("fullbright")) + light = 255; if (light != m_last_light) { m_last_light = light; setNodeLight(light); @@ -889,10 +918,30 @@ u16 GenericCAO::getLightPosition(v3s16 *pos) return 3; } +void GenericCAO::updateMarker() +{ + if (!m_client->getMinimap()) + return; + + if (!m_prop.show_on_minimap) { + if (m_marker) + m_client->getMinimap()->removeMarker(&m_marker); + return; + } + + if (m_marker) + return; + + scene::ISceneNode *node = getSceneNode(); + if (!node) + return; + m_marker = m_client->getMinimap()->addMarker(node); +} + void GenericCAO::updateNametag() { - if (m_is_local_player && ! g_settings->getBool("freecam")) // No nametag for local player - return; + //if (m_is_local_player && ! g_settings->getBool("freecam")) // No nametag for local player + //return; if (m_prop.nametag.empty()) { // Delete nametag @@ -970,24 +1019,24 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) bool allow_update = false; // increase speed if using fast or flying fast - if((g_settings->getBool("fast_move") && + if(((g_settings->getBool("fast_move") && m_client->checkLocalPrivilege("fast")) && (controls.aux1 || (!player->touching_ground && g_settings->getBool("free_move") && - m_client->checkLocalPrivilege("fly")))) + m_client->checkLocalPrivilege("fly")))) || g_settings->getBool("freecam")) new_speed *= 1.5; // slowdown speed if sneeking if (controls.sneak && walking && ! g_settings->getBool("no_slow")) new_speed /= 2; - if (walking && (controls.LMB || controls.RMB)) { + if (walking && (controls.dig || controls.place)) { new_anim = player->local_animations[3]; player->last_animation = WD_ANIM; - } else if(walking) { + } else if (walking) { new_anim = player->local_animations[1]; player->last_animation = WALK_ANIM; - } else if(controls.LMB || controls.RMB) { + } else if (controls.dig || controls.place) { new_anim = player->local_animations[2]; player->last_animation = DIG_ANIM; } @@ -1010,9 +1059,9 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) // Update local player animations if ((player->last_animation != old_anim || - m_animation_speed != old_anim_speed) && - player->last_animation != NO_ANIM && allow_update) - updateAnimation(); + m_animation_speed != old_anim_speed) && + player->last_animation != NO_ANIM && allow_update) + updateAnimation(); } } @@ -1182,6 +1231,7 @@ void GenericCAO::updateTexturePos() int row = m_tx_basepos.Y; int col = m_tx_basepos.X; + // Yawpitch goes rightwards if (m_tx_select_horiz_by_yawpitch) { if (cam_to_entity.Y > 0.75) col += 5; @@ -1212,6 +1262,27 @@ void GenericCAO::updateTexturePos() float tys = m_tx_size.Y; setBillboardTextureMatrix(m_spritenode, txs, tys, col, row); } + + else if (m_meshnode) { + if (m_prop.visual == "upright_sprite") { + int row = m_tx_basepos.Y; + int col = m_tx_basepos.X; + + // Animation goes downwards + row += m_anim_frame; + + const auto &tx = m_tx_size; + v2f t[4] = { // cf. vertices in GenericCAO::addToScene() + tx * v2f(col+1, row+1), + tx * v2f(col, row+1), + tx * v2f(col, row), + tx * v2f(col+1, row), + }; + auto mesh = m_meshnode->getMesh(); + setMeshBufferTextureCoords(mesh->getMeshBuffer(0), t, 4); + setMeshBufferTextureCoords(mesh->getMeshBuffer(1), t, 4); + } + } } // Do not pass by reference, see header. @@ -1253,7 +1324,7 @@ void GenericCAO::updateTextures(std::string mod) } } - if (m_animated_meshnode) { + else if (m_animated_meshnode) { if (m_prop.visual == "mesh") { for (u32 i = 0; i < m_prop.textures.size() && i < m_animated_meshnode->getMaterialCount(); ++i) { @@ -1302,8 +1373,8 @@ void GenericCAO::updateTextures(std::string mod) } } } - if(m_meshnode) - { + + else if (m_meshnode) { if(m_prop.visual == "cube") { for (u32 i = 0; i < 6; ++i) @@ -1395,6 +1466,9 @@ void GenericCAO::updateTextures(std::string mod) setMeshColor(mesh, m_prop.colors[0]); } } + // Prevent showing the player after changing texture + if (m_is_local_player) + updateMeshCulling(); } void GenericCAO::updateAnimation() @@ -1564,6 +1638,8 @@ void GenericCAO::processMessage(const std::string &data) u8 cmd = readU8(is); if (cmd == AO_CMD_SET_PROPERTIES) { ObjectProperties newprops; + newprops.show_on_minimap = m_is_player; // default + newprops.deSerialize(is); // Check what exactly changed @@ -1597,6 +1673,8 @@ void GenericCAO::processMessage(const std::string &data) if ((m_is_player && !m_is_local_player) && m_prop.nametag.empty()) m_prop.nametag = m_name; + if (m_is_local_player) + m_prop.show_on_minimap = false; if (expire_visuals) { expireVisuals(); @@ -1609,6 +1687,7 @@ void GenericCAO::processMessage(const std::string &data) updateTextures(m_current_texture_modifier); } updateNametag(); + updateMarker(); } } else if (cmd == AO_CMD_UPDATE_POSITION) { // Not sent by the server if this object is an attachment. @@ -1641,7 +1720,7 @@ void GenericCAO::processMessage(const std::string &data) rot_translator.update(m_rotation, false, update_interval); updateNodePos(); } else if (cmd == AO_CMD_SET_TEXTURE_MOD) { - std::string mod = deSerializeString(is); + std::string mod = deSerializeString16(is); // immediately reset a engine issued texture modifier if a mod sends a different one if (m_reset_textures_timer > 0) { @@ -1673,6 +1752,11 @@ void GenericCAO::processMessage(const std::string &data) if(m_is_local_player) { + Client *client = m_env->getGameDef(); + + if (client->modsLoaded() && client->getScript()->on_recieve_physics_override(override_speed, override_jump, override_gravity, sneak, sneak_glitch, new_move)) + return; + LocalPlayer *player = m_env->getLocalPlayer(); player->physics_override_speed = override_speed; player->physics_override_jump = override_jump; @@ -1719,7 +1803,7 @@ void GenericCAO::processMessage(const std::string &data) m_animation_speed = readF32(is); updateAnimationSpeed(); } else if (cmd == AO_CMD_SET_BONE_POSITION) { - std::string bone = deSerializeString(is); + std::string bone = deSerializeString16(is); v3f position = readV3F32(is); v3f rotation = readV3F32(is); m_bone_position[bone] = core::vector2d(position, rotation); @@ -1727,15 +1811,12 @@ void GenericCAO::processMessage(const std::string &data) // updateBonePosition(); now called every step } else if (cmd == AO_CMD_ATTACH_TO) { u16 parent_id = readS16(is); - std::string bone = deSerializeString(is); + std::string bone = deSerializeString16(is); v3f position = readV3F32(is); v3f rotation = readV3F32(is); + bool force_visible = readU8(is); // Returns false for EOF - setAttachment(parent_id, bone, position, rotation); - - // localplayer itself can't be attached to localplayer - if (!m_is_local_player) - m_is_visible = !m_attached_to_local; + setAttachment(parent_id, bone, position, rotation, force_visible); } else if (cmd == AO_CMD_PUNCHED) { u16 result_hp = readU16(is); @@ -1777,7 +1858,7 @@ void GenericCAO::processMessage(const std::string &data) int armor_groups_size = readU16(is); for(int i=0; i