Merge remote-tracking branch 'origin/master' into STK-helper-ItemWeights
253
INSTALL.md
Normal file
@ -0,0 +1,253 @@
|
||||
## Building from source
|
||||
|
||||
In order to build SuperTuxKart from source, you'll need both the code and the assets (See <https://supertuxkart.net/Source_control> for more information):
|
||||
|
||||
```bash
|
||||
git clone https://github.com/supertuxkart/stk-code stk-code
|
||||
svn co https://svn.code.sf.net/p/supertuxkart/code/stk-assets stk-assets
|
||||
```
|
||||
|
||||
## Building SuperTuxKart on Linux
|
||||
|
||||
### Dependencies
|
||||
|
||||
To build SuperTuxKart from source, you'll need to install the following packages:
|
||||
|
||||
* OpenGL (mesa)
|
||||
* OpenAL (recommended: openal-soft-devel)
|
||||
* Ogg (libogg-dev)
|
||||
* Vorbis (libvorbis-dev)
|
||||
* Freetype (libfreetype6-dev)
|
||||
* libcurl (libcurl-devel)
|
||||
* libbluetooth (bluez-devel)
|
||||
* libpng (libpng-devel)
|
||||
* zlib (zlib-devel)
|
||||
* jpeg (libjpeg-turbo-devel)
|
||||
|
||||
Ubuntu command:
|
||||
|
||||
```bash
|
||||
sudo apt-get install build-essential cmake libbluetooth-dev \
|
||||
libcurl4-openssl-dev libenet-dev libfreetype6-dev libfribidi-dev \
|
||||
libgl1-mesa-dev libglew-dev libjpeg-dev libogg-dev libopenal-dev libpng-dev \
|
||||
libssl-dev libvorbis-dev libxrandr-dev libx11-dev pkg-config zlib1g-dev
|
||||
```
|
||||
Fedora command:
|
||||
|
||||
```bash
|
||||
sudo dnf install @development-tools cmake bluez-libs-devel \
|
||||
openssl-devel libcurl-devel freetype-devel fribidi-devel mesa-libGL-devel \
|
||||
libjpeg-turbo-devel libogg-devel openal-soft-devel libpng-devel \
|
||||
libvorbis-devel libXrandr-devel libGLEW pkgconf zlib-devel
|
||||
```
|
||||
### In-game recorder
|
||||
|
||||
To build the in-game recorder for STK, you have to install
|
||||
libopenglrecorder from your distribution, or compile it yourself from [here](https://github.com/Benau/libopenglrecorder).
|
||||
Compilation instruction is explained there. If you don't need this feature, pass `-DBUILD_RECORDER=off` to cmake.
|
||||
|
||||
### Compiling
|
||||
|
||||
To compile SuperTuxKart, run the following commands inside `stk-code` directory:
|
||||
|
||||
```bash
|
||||
mkdir cmake_build
|
||||
cd cmake_build
|
||||
cmake ..
|
||||
make -j4
|
||||
```
|
||||
STK can then be run from the build directory with `bin/supertuxkart`
|
||||
|
||||
#### Keeping your build up to date
|
||||
|
||||
To recompile the latest code without redownloading the entire source, first run the ```svn up``` command inside the 'stk-assets' directory, then run the following commands inside the 'stk-code' directory:
|
||||
|
||||
```bash
|
||||
git pull
|
||||
cd cmake_build
|
||||
cmake ..
|
||||
make -j4
|
||||
```
|
||||
|
||||
##### Build Speed Optimization
|
||||
|
||||
"-j4" is an example, for a faster build, use "-jx" instead, where "x" is the amount of CPU threads you have, minus one. "-j$(nproc)" usually works.
|
||||
|
||||
### Further options
|
||||
|
||||
To create a debug version of STK, run:
|
||||
|
||||
```bash
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Debug
|
||||
```
|
||||
|
||||
You can install your build system-wide:
|
||||
|
||||
```bash
|
||||
sudo make install
|
||||
```
|
||||
|
||||
The default install location is `/usr/local`, i.e. the data files will
|
||||
be written to `/usr/local/share/games/supertuxkart`, the executable
|
||||
will be copied to `/usr/local/bin`. To change the default installation
|
||||
location, specify `CMAKE_INSTALL_PREFIX` when running cmake, e.g.:
|
||||
`cmake .. -DCMAKE_INSTALL_PREFIX=/opt/stk`
|
||||
|
||||
|
||||
|
||||
## Building SuperTuxKart on Windows
|
||||
To Build SuperTuxKart on Windows, follow these instructions:
|
||||
|
||||
1. Download and install Visual Studio from here: [Visual Studio - Download](https://www.visualstudio.com/downloads/). The free Visual Studio Community edition works fine.
|
||||
2. Download the SuperTuxKart source package from either [SuperTuxKart download area - SourceForge.net](https://sourceforge.net/projects/supertuxkart/files/SuperTuxKart/) or [SuperTuxKart.net - Source Control](https://supertuxkart.net/Source_control), and unpack it.
|
||||
*Note: If you downloaded the source package from here: [SuperTuxKart.net - Source Control](https://supertuxkart.net/Source_control), then both `stk-code` and `stk-assets` **must** be in the same directory, otherwise the build can result in failure*
|
||||
3. Download the Windows dependencies package from either [SuperTuxKart download area: Dependencies - SourceForge.net](https://sourceforge.net/projects/supertuxkart/files/SuperTuxKart%20Dependencies/Windows/)
|
||||
or [SuperTuxKart on GitHub - Dependencies](https://github.com/supertuxkart/dependencies), and unpack it; then, copy the `dependencies` directory from either the `windows` or the `windows_64bit` directories into the `stk-code` directory, rename it to `dependencies-64bit` if you want to compile a 64bit build.
|
||||
4. Download CMake from here: [CMake - download page](https://cmake.org/download/), install it; once CMake is installed, double click on the CMake icon on your desktop, and point it towards your `stk-code` directory in the 'Where is the source code' field, and point it to a directory called `build` or `bld` inside the stk-code directory.
|
||||
5. Press 'Configure'; CMake will ask you if it is OK to create the aforementioned directory, press `Yes`. CMake will then ask you about your version of Visual Studio.
|
||||
Confirm your selection; *Please look at the table below to avoid confusion between version numbers and releases of Visual Studio*;
|
||||
CMake will begin creating the required files for the build in the directory.
|
||||
6. Navigate to your build directory and open the `SuperTuxKart.sln` file; Visual Studio will now load the solution.
|
||||
7. In the 'Solution Explorer', right click on the `supertuxkart` project and select "Set as StartUp project"
|
||||
8. Open the 'Build' menu and select 'Build Solution'; or, press the default keyboard shortcut: `CTRL + SHIFT + B` to build the solution.
|
||||
|
||||
*Note: To avoid confusion between releases and versions, refer to this table:*
|
||||
|
||||
Visual Studio Release | Version
|
||||
----------------------|------------
|
||||
Visual Studio 2017| 15
|
||||
Visual Studio 2015| 14
|
||||
Visual Studio 2013| 13
|
||||
|
||||
## Building SuperTuxKart on Windows (from PowerShell/Command line)
|
||||
1. Download and install Visual Studio from here: [Visual Studio - Download](https://www.visualstudio.com/downloads/), the free Visual Studio Community edition works fine.
|
||||
|
||||
2. Download a source package from either [SuperTuxKart 0.9.2 download area - SourceForge.net](https://sourceforge.net/projects/supertuxkart/files/SuperTuxKart/0.9.2) or [SuperTuxKart.net - Source Control](https://supertuxkart.net/Source_control)
|
||||
NOTE: the `stk-code` and `stk-assets` directories **must** be in the same directory
|
||||
3. Download the Windows dependencies package from either [SuperTuxKart download area - SourceForge.net](https://sourceforge.net/projects/supertuxkart/files/SuperTuxKart%20Dependencies/Windows/)
|
||||
or [SuperTuxKart on GitHub - Dependencies](https://github.com/supertuxkart/dependencies)
|
||||
and unpack the archive; once unpacked, copy the `dependencies` directory from either the `windows` or the `windows_64bit` directories into the `stk-code` directory
|
||||
4. Download CMake from here: [CMake - download page](https://cmake.org/download/); and install it. Navigate to the `stk-code` directory; and create an directory called "build":
|
||||
```cmd
|
||||
mkdir build
|
||||
cd build
|
||||
```
|
||||
5. Once inside the build directory; run CMake to start the compilation process:
|
||||
```cmd
|
||||
cmake ..
|
||||
```
|
||||
6. Now that CMake finished configuring and creating the necessary files for the build, run the build command in the same directory:
|
||||
```cmd
|
||||
msbuild.exe SuperTuxKart.sln
|
||||
```
|
||||
SuperTuxKart can now be run as `bin\Debug\supertuxkart.exe` or `bin\Release\supertuxkart.exe`
|
||||
|
||||
## Building SuperTuxKart on macOS
|
||||
|
||||
### Getting Started
|
||||
|
||||
Install the developer tools, either from the OS X Install DVD or from Apple's website.
|
||||
|
||||
If you have never built anything before, you have create `/usr/local/include/` first:
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /usr/local/include/
|
||||
```
|
||||
|
||||
Symlink the `include`-folder of OpenGL framework to `/usr/local/include/GL` (Unix programs have an easier time finding it this way):
|
||||
|
||||
```bash
|
||||
sudo ln -s /System/Library/Frameworks/OpenGL.framework/Versions/A/Headers/ /usr/local/include/GL
|
||||
```
|
||||
|
||||
On OS X 10.9.5, you might need the following workaround:
|
||||
|
||||
```bash
|
||||
sudo ln -s `xcrun --show-sdk-path`/usr/include/ /usr/include
|
||||
sudo ln -s `xcrun --show-sdk-path`/System/Library/Frameworks/OpenGL.framework/Headers/ /usr/local/include/OpenGL
|
||||
```
|
||||
The first link is required in order to find libcurl, the second to find opengl.
|
||||
|
||||
### CMake
|
||||
|
||||
CMake is used to build STK. At this time CMake will not make a binary that is ready for distribution.
|
||||
|
||||
You'll have to run these commands inside your stk-code directory.
|
||||
|
||||
|
||||
### STK 0.9.4 or later (or latest git)
|
||||
|
||||
Install homebrew ( https://brew.sh/)
|
||||
Install all of the dependencies using homebrew :
|
||||
|
||||
```bash
|
||||
brew install libogg
|
||||
brew install libvorbis
|
||||
brew install openal-soft
|
||||
brew install freetype
|
||||
brew install curl
|
||||
brew install openssl@1.1
|
||||
brew install fribidi
|
||||
brew install glew
|
||||
```
|
||||
|
||||
Build STK
|
||||
```bash
|
||||
mkdir cmake_build
|
||||
cd cmake_build
|
||||
CMAKE_PREFIX_PATH=/usr/local/opt/freetype/:/usr/local/opt/curl/:/usr/local/opt/libogg/:/usr/local/opt/libogg/:/usr/local/opt/libvorbis/:/usr/local/opt/openssl\@1.1/:/usr/local/opt/glew/:/usr/local/opt/fribidi/ /usr/local/opt/cmake/bin/cmake .. -DFREETYPE_INCLUDE_DIRS=/usr/local/opt/freetype/include/freetype2/ -DUSE_SYSTEM_GLEW=1 -DOPENAL_INCLUDE_DIR=/usr/local/opt/openal-soft/include/ -DOPENAL_LIBRARY=/usr/local/opt/openal-soft/lib/libopenal.dylib
|
||||
make
|
||||
```
|
||||
|
||||
#### (Optional) packaging for distribution
|
||||
|
||||
By default, the executable that is produced is not ready for distribution. Install https://github.com/auriamg/macdylibbundler
|
||||
|
||||
```bash
|
||||
dylibbundler -od -b -x ./bin/SuperTuxKart.app/Contents/MacOS/supertuxkart -d ./bin/SuperTuxKart.app/Contents/libs/ -p @executable_path/../libs/
|
||||
```
|
||||
|
||||
then copy the datafiles into /SuperTuxKart.app/Contents/Resources/data
|
||||
|
||||
|
||||
### STK 0.9.3 or earlier
|
||||
|
||||
Download pre-built dependencies from [here](https://sourceforge.net/projects/supertuxkart/files/SuperTuxKart%20Dependencies/OSX/) and put the frameworks in [hard disk root]/Library/Frameworks
|
||||
|
||||
Building with clang:
|
||||
|
||||
```bash
|
||||
mkdir cmake_build
|
||||
cd cmake_build
|
||||
cmake ..
|
||||
make
|
||||
```
|
||||
|
||||
Building with GCC:
|
||||
```bash
|
||||
mkdir cmake_build
|
||||
cd cmake_build
|
||||
cmake .. -DCMAKE_CXX_COMPILER=/usr/bin/g++ -DCMAKE_C_COMPILER=/usr/bin/gcc
|
||||
make
|
||||
```
|
||||
|
||||
Building on 10.10 with 10.9 compatibility:
|
||||
```bash
|
||||
cmake .. -DCMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk -DCMAKE_OSX_DEPLOYMENT_TARGET=10.9
|
||||
```
|
||||
|
||||
#### Xcode
|
||||
|
||||
Place an additional copy of the dependencies into `Users/<YOUR_USERNAME>/Library/Frameworks`.
|
||||
Then cd to your cloned stk-code directory and execute the following commands:
|
||||
|
||||
```bash
|
||||
mkdir xcode_build && cd xcode_build
|
||||
cmake .. -GXcode
|
||||
```
|
||||
|
||||
Use Finder to navigate to your stk-code/xcode_build folder and open the newly generated Xcode project (`SuperTuxKart.xcodeproj`).
|
||||
|
||||
You can then build the project in Xcode using Product -> Build
|
||||
|
||||
Note: Xcode is much less well tested than makefiles, so there may be issues when trying to use Xcode.
|
244
README.md
@ -30,246 +30,4 @@ The export utilities perform the needed transformation, so in Blender you just
|
||||
|
||||
## Building from source
|
||||
|
||||
In order to build SuperTuxKart from source, you'll need both the code and the assets (See <https://supertuxkart.net/Source_control> for more information):
|
||||
|
||||
```bash
|
||||
git clone https://github.com/supertuxkart/stk-code stk-code
|
||||
svn co https://svn.code.sf.net/p/supertuxkart/code/stk-assets stk-assets
|
||||
```
|
||||
|
||||
## Building SuperTuxKart on Linux
|
||||
|
||||
### Dependencies
|
||||
|
||||
To build SuperTuxKart from source, you'll need to install the following packages:
|
||||
|
||||
* OpenGL (mesa)
|
||||
* OpenAL (recommended: openal-soft-devel)
|
||||
* Ogg (libogg-dev)
|
||||
* Vorbis (libvorbis-dev)
|
||||
* Freetype (libfreetype6-dev)
|
||||
* libcurl (libcurl-devel)
|
||||
* libbluetooth (bluez-devel)
|
||||
* libpng (libpng-devel)
|
||||
* zlib (zlib-devel)
|
||||
* jpeg (libjpeg-turbo-devel)
|
||||
|
||||
Ubuntu command:
|
||||
|
||||
```bash
|
||||
sudo apt-get install build-essential cmake libbluetooth-dev \
|
||||
libcurl4-gnutls-dev libfreetype6-dev libfribidi-dev libgl1-mesa-dev \
|
||||
libjpeg-dev libogg-dev libopenal-dev libpng-dev libvorbis-dev libxrandr-dev \
|
||||
mesa-common-dev pkg-config zlib1g-dev
|
||||
```
|
||||
### In-game recorder
|
||||
|
||||
To build the in-game recorder for STK, you have to install
|
||||
libopenglrecorder from your distribution, or compile it yourself from [here](https://github.com/Benau/libopenglrecorder).
|
||||
Compilation instruction is explained there. If you don't need this feature, pass `-DBUILD_RECORDER=off` to cmake.
|
||||
|
||||
### Compiling
|
||||
|
||||
To compile SuperTuxKart, run the following commands inside `stk-code` directory:
|
||||
|
||||
```bash
|
||||
mkdir cmake_build
|
||||
cd cmake_build
|
||||
cmake ..
|
||||
make -j4
|
||||
```
|
||||
STK can then be run from the build directory with `bin/supertuxkart`
|
||||
|
||||
#### Keeping your build up to date
|
||||
|
||||
To recompile the latest code without redownloading the entire source, first run the ```svn up``` command inside the 'stk-assets' directory, then run the following commands inside the 'stk-code' directory:
|
||||
|
||||
```bash
|
||||
git pull
|
||||
cd cmake_build
|
||||
cmake ..
|
||||
make -j4
|
||||
```
|
||||
|
||||
##### Build Speed Optimization
|
||||
|
||||
"-j4" is an example, for a faster build, use "-jx" instead, where "x" is the amount of CPU threads you have, minus one.
|
||||
|
||||
### Further options
|
||||
|
||||
To create a debug version of STK, run:
|
||||
|
||||
```bash
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Debug
|
||||
```
|
||||
|
||||
You can install your build system-wide:
|
||||
|
||||
```bash
|
||||
sudo make install
|
||||
```
|
||||
|
||||
The default install location is `/usr/local`, i.e. the data files will
|
||||
be written to `/usr/local/share/games/supertuxkart`, the executable
|
||||
will be copied to `/usr/local/bin`. To change the default installation
|
||||
location, specify `CMAKE_INSTALL_PREFIX` when running cmake, e.g.:
|
||||
`cmake .. -DCMAKE_INSTALL_PREFIX=/opt/stk`
|
||||
|
||||
|
||||
|
||||
## Building SuperTuxKart on Windows
|
||||
To Build SuperTuxKart on Windows, follow these instructions:
|
||||
|
||||
1. Download and install Visual Studio from here: [Visual Studio - Download](https://www.visualstudio.com/downloads/). The free Visual Studio Community edition works fine.
|
||||
2. Download the SuperTuxKart source package from either [SuperTuxKart download area - SourceForge.net](https://sourceforge.net/projects/supertuxkart/files/SuperTuxKart/) or [SuperTuxKart.net - Source Control](https://supertuxkart.net/Source_control), and unpack it.
|
||||
*Note: If you downloaded the source package from here: [SuperTuxKart.net - Source Control](https://supertuxkart.net/Source_control), then both `stk-code` and `stk-assets` **must** be in the same directory, otherwise the build can result in failure*
|
||||
3. Download the Windows dependencies package from either [SuperTuxKart download area: Dependencies - SourceForge.net](https://sourceforge.net/projects/supertuxkart/files/SuperTuxKart%20Dependencies/Windows/)
|
||||
or [SuperTuxKart on GitHub - Dependencies](https://github.com/supertuxkart/dependencies), and unpack it; then, copy the `dependencies` directory from either the `windows` or the `windows_64bit` directories into the `stk-code` directory, rename it to `dependencies-64bit` if you want to compile a 64bit build.
|
||||
4. Download CMake from here: [CMake - download page](https://cmake.org/download/), install it; once CMake is installed, double click on the CMake icon on your desktop, and point it towards your `stk-code` directory in the 'Where is the source code' field, and point it to a directory called `build` or `bld` inside the stk-code directory.
|
||||
5. Press 'Configure'; CMake will ask you if it is OK to create the aforementioned directory, press `Yes`. CMake will then ask you about your version of Visual Studio.
|
||||
Confirm your selection; *Please look at the table below to avoid confusion between version numbers and releases of Visual Studio*;
|
||||
CMake will begin creating the required files for the build in the directory.
|
||||
6. Navigate to your build directory and open the `SuperTuxKart.sln` file; Visual Studio will now load the solution.
|
||||
7. In the 'Solution Explorer', right click on the `supertuxkart` project and select "Set as StartUp project"
|
||||
8. Open the 'Build' menu and select 'Build Solution'; or, press the default keyboard shortcut: `CTRL + SHIFT + B` to build the solution.
|
||||
|
||||
*Note: To avoid confusion between releases and versions, refer to this table:*
|
||||
|
||||
Visual Studio Release | Version
|
||||
----------------------|------------
|
||||
Visual Studio 2017| 15
|
||||
Visual Studio 2015| 14
|
||||
Visual Studio 2013| 13
|
||||
|
||||
## Building SuperTuxKart on Windows (from PowerShell/Command line)
|
||||
1. Download and install Visual Studio from here: [Visual Studio - Download](https://www.visualstudio.com/downloads/), the free Visual Studio Community edition works fine.
|
||||
|
||||
2. Download a source package from either [SuperTuxKart 0.9.2 download area - SourceForge.net](https://sourceforge.net/projects/supertuxkart/files/SuperTuxKart/0.9.2) or [SuperTuxKart.net - Source Control](https://supertuxkart.net/Source_control)
|
||||
NOTE: the `stk-code` and `stk-assets` directories **must** be in the same directory
|
||||
3. Download the Windows dependencies package from either [SuperTuxKart download area - SourceForge.net](https://sourceforge.net/projects/supertuxkart/files/SuperTuxKart%20Dependencies/Windows/)
|
||||
or [SuperTuxKart on GitHub - Dependencies](https://github.com/supertuxkart/dependencies)
|
||||
and unpack the archive; once unpacked, copy the `dependencies` directory from either the `windows` or the `windows_64bit` directories into the `stk-code` directory
|
||||
4. Download CMake from here: [CMake - download page](https://cmake.org/download/); and install it. Navigate to the `stk-code` directory; and create an directory called "build":
|
||||
```cmd
|
||||
mkdir build
|
||||
cd build
|
||||
```
|
||||
5. Once inside the build directory; run CMake to start the compilation process:
|
||||
```cmd
|
||||
cmake ..
|
||||
```
|
||||
6. Now that CMake finished configuring and creating the necessary files for the build, run the build command in the same directory:
|
||||
```cmd
|
||||
msbuild.exe SuperTuxKart.sln
|
||||
```
|
||||
SuperTuxKart can now be run as `bin\Debug\supertuxkart.exe` or `bin\Release\supertuxkart.exe`
|
||||
|
||||
## Building SuperTuxKart on macOS
|
||||
|
||||
### Getting Started
|
||||
|
||||
Install the developer tools, either from the OS X Install DVD or from Apple's website.
|
||||
|
||||
If you have never built anything before, you have create `/usr/local/include/` first:
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /usr/local/include/
|
||||
```
|
||||
|
||||
Symlink the `include`-folder of OpenGL framework to `/usr/local/include/GL` (Unix programs have an easier time finding it this way):
|
||||
|
||||
```bash
|
||||
sudo ln -s /System/Library/Frameworks/OpenGL.framework/Versions/A/Headers/ /usr/local/include/GL
|
||||
```
|
||||
|
||||
On OS X 10.9.5, you might need the following workaround:
|
||||
|
||||
```bash
|
||||
sudo ln -s `xcrun --show-sdk-path`/usr/include/ /usr/include
|
||||
sudo ln -s `xcrun --show-sdk-path`/System/Library/Frameworks/OpenGL.framework/Headers/ /usr/local/include/OpenGL
|
||||
```
|
||||
The first link is required in order to find libcurl, the second to find opengl.
|
||||
|
||||
### CMake
|
||||
|
||||
CMake is used to build STK. At this time CMake will not make a binary that is ready for distribution.
|
||||
|
||||
You'll have to run these commands inside your stk-code directory.
|
||||
|
||||
|
||||
### STK 0.9.4 or later (or latest git)
|
||||
|
||||
Install homebrew ( https://brew.sh/)
|
||||
Install all of the dependencies using homebrew :
|
||||
|
||||
```bash
|
||||
brew install libogg
|
||||
brew install libvorbis
|
||||
brew install openal-soft
|
||||
brew install freetype
|
||||
brew install curl
|
||||
brew install openssl@1.1
|
||||
brew install fribidi
|
||||
brew install glew
|
||||
```
|
||||
|
||||
Build STK
|
||||
```bash
|
||||
mkdir cmake_build
|
||||
cd cmake_build
|
||||
CMAKE_PREFIX_PATH=/usr/local/opt/freetype/:/usr/local/opt/curl/:/usr/local/opt/libogg/:/usr/local/opt/libogg/:/usr/local/opt/libvorbis/:/usr/local/opt/openssl\@1.1/:/usr/local/opt/glew/:/usr/local/opt/fribidi/ /usr/local/opt/cmake/bin/cmake .. -DFREETYPE_INCLUDE_DIRS=/usr/local/opt/freetype/include/freetype2/ -DUSE_SYSTEM_GLEW=1 -DOPENAL_INCLUDE_DIR=/usr/local/opt/openal-soft/include/ -DOPENAL_LIBRARY=/usr/local/opt/openal-soft/lib/libopenal.dylib
|
||||
make
|
||||
```
|
||||
|
||||
#### (Optional) packaging for distribution
|
||||
|
||||
By default, the executable that is produced is not ready for distribution. Install https://github.com/auriamg/macdylibbundler
|
||||
|
||||
```bash
|
||||
dylibbundler -od -b -x ./bin/SuperTuxKart.app/Contents/MacOS/supertuxkart -d ./bin/SuperTuxKart.app/Contents/libs/ -p @executable_path/../libs/
|
||||
```
|
||||
|
||||
then copy the datafiles into /SuperTuxKart.app/Contents/Resources/data
|
||||
|
||||
|
||||
### STK 0.9.3 or earlier
|
||||
|
||||
Download pre-built dependencies from [here](https://sourceforge.net/projects/supertuxkart/files/SuperTuxKart%20Dependencies/OSX/) and put the frameworks in [hard disk root]/Library/Frameworks
|
||||
|
||||
Building with clang:
|
||||
|
||||
```bash
|
||||
mkdir cmake_build
|
||||
cd cmake_build
|
||||
cmake ..
|
||||
make
|
||||
```
|
||||
|
||||
Building with GCC:
|
||||
```bash
|
||||
mkdir cmake_build
|
||||
cd cmake_build
|
||||
cmake .. -DCMAKE_CXX_COMPILER=/usr/bin/g++ -DCMAKE_C_COMPILER=/usr/bin/gcc
|
||||
make
|
||||
```
|
||||
|
||||
Building on 10.10 with 10.9 compatibility:
|
||||
```bash
|
||||
cmake .. -DCMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk -DCMAKE_OSX_DEPLOYMENT_TARGET=10.9
|
||||
```
|
||||
|
||||
#### Xcode
|
||||
|
||||
Place an additional copy of the dependencies into `Users/<YOUR_USERNAME>/Library/Frameworks`.
|
||||
Then cd to your cloned stk-code directory and execute the following commands:
|
||||
|
||||
```bash
|
||||
mkdir xcode_build && cd xcode_build
|
||||
cmake .. -GXcode
|
||||
```
|
||||
|
||||
Use Finder to navigate to your stk-code/xcode_build folder and open the newly generated Xcode project (`SuperTuxKart.xcodeproj`).
|
||||
|
||||
You can then build the project in Xcode using Product -> Build
|
||||
|
||||
Note: Xcode is much less well tested than makefiles, so there may be issues when trying to use Xcode.
|
||||
Building instructions can be found in [`INSTALL.md`](/INSTALL.md)
|
||||
|
@ -9,7 +9,7 @@
|
||||
# FRIBIDI_LIBRARIES
|
||||
# Fribidi library list
|
||||
|
||||
if(UNIX)
|
||||
if(UNIX AND NOT APPLE)
|
||||
include(FindPkgConfig)
|
||||
pkg_check_modules(FRIBIDI fribidi)
|
||||
else()
|
||||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.8 KiB |
@ -2753,26 +2753,26 @@ namespace video
|
||||
|
||||
void COGLES2CallBridge::setBlendFunc(GLenum source, GLenum destination)
|
||||
{
|
||||
if(BlendSource != source || BlendDestination != destination)
|
||||
{
|
||||
//if(BlendSource != source || BlendDestination != destination)
|
||||
//{
|
||||
glBlendFunc(source, destination);
|
||||
|
||||
BlendSource = source;
|
||||
BlendDestination = destination;
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
void COGLES2CallBridge::setBlend(bool enable)
|
||||
{
|
||||
if(Blend != enable)
|
||||
{
|
||||
//if(Blend != enable)
|
||||
//{
|
||||
if (enable)
|
||||
glEnable(GL_BLEND);
|
||||
else
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
Blend = enable;
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
void COGLES2CallBridge::setCullFaceFunc(GLenum mode)
|
||||
|
@ -18,6 +18,8 @@
|
||||
\page addons Addons
|
||||
*/
|
||||
|
||||
#ifndef SERVER_ONLY
|
||||
|
||||
#include "addons/addons_manager.hpp"
|
||||
|
||||
#include "addons/news_manager.hpp"
|
||||
@ -614,3 +616,4 @@ void AddonsManager::saveInstalled()
|
||||
xml_installed.close();
|
||||
} // saveInstalled
|
||||
|
||||
#endif
|
||||
|
@ -19,6 +19,8 @@
|
||||
#ifndef HEADER_ADDONS_MANAGER_HPP
|
||||
#define HEADER_ADDONS_MANAGER_HPP
|
||||
|
||||
#ifndef SERVER_ONLY
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
@ -89,5 +91,6 @@ public:
|
||||
}; // class AddonsManager
|
||||
|
||||
extern AddonsManager *addons_manager;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -15,6 +15,8 @@
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#ifndef SERVER_ONLY
|
||||
|
||||
#include "addons/news_manager.hpp"
|
||||
|
||||
#include "config/user_config.hpp"
|
||||
@ -490,3 +492,5 @@ bool NewsManager::conditionFulfilled(const std::string &cond)
|
||||
} // conditionFulfilled
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#endif
|
||||
|
@ -18,6 +18,8 @@
|
||||
#ifndef HEADER_NEWS_MANAGER_HPP
|
||||
#define HEADER_NEWS_MANAGER_HPP
|
||||
|
||||
#ifndef SERVER_ONLY
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@ -143,4 +145,4 @@ public:
|
||||
extern NewsManager *news_manager;
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -31,6 +31,7 @@
|
||||
StoryModeStatus::StoryModeStatus(const XMLNode *node)
|
||||
{
|
||||
m_points = 0;
|
||||
m_points_before = 0;
|
||||
m_next_unlock_points = 0;
|
||||
m_first_time = true;
|
||||
m_easy_challenges = 0;
|
||||
@ -79,6 +80,7 @@ bool StoryModeStatus::isLocked(const std::string& feature)
|
||||
//-----------------------------------------------------------------------------
|
||||
void StoryModeStatus::computeActive()
|
||||
{
|
||||
int old_points = m_points;
|
||||
m_points = 0;
|
||||
m_next_unlock_points = 0;
|
||||
m_easy_challenges = 0;
|
||||
@ -182,6 +184,9 @@ void StoryModeStatus::computeActive()
|
||||
|
||||
// now we have the number of points.
|
||||
|
||||
if (old_points != m_points)
|
||||
m_points_before = old_points;
|
||||
|
||||
unlockFeatureByList();
|
||||
|
||||
//Actually lock the tracks
|
||||
|
@ -62,6 +62,7 @@ private:
|
||||
const ChallengeStatus *m_current_challenge;
|
||||
|
||||
int m_points;
|
||||
int m_points_before; // used for unlocks
|
||||
int m_next_unlock_points;
|
||||
|
||||
/** Set to false after the initial stuff (intro, select kart, etc.) */
|
||||
@ -101,6 +102,9 @@ public:
|
||||
/** Returns the number of points accumulated. */
|
||||
int getPoints () const { return m_points; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the number of points before the previous point increase */
|
||||
int getPointsBefore () const { return m_points_before; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the number of points needed by the next unlockable. 0 if none. */
|
||||
int getNextUnlockPoints () const { return m_next_unlock_points; }
|
||||
// ------------------------------------------------------------------------
|
||||
|
@ -230,6 +230,8 @@ public:
|
||||
// ------------------------------------------------------------------------
|
||||
unsigned int getPoints() const { return m_story_mode_status->getPoints(); }
|
||||
// ------------------------------------------------------------------------
|
||||
unsigned int getPointsBefore() const { return m_story_mode_status->getPointsBefore(); }
|
||||
// ------------------------------------------------------------------------
|
||||
unsigned int getNextUnlockPoints() const { return m_story_mode_status->getNextUnlockPoints(); }
|
||||
// ------------------------------------------------------------------------
|
||||
void setFirstTime(bool b) { m_story_mode_status->setFirstTime(b); }
|
||||
|
@ -994,6 +994,10 @@ namespace UserConfigParams
|
||||
PARAM_PREFIX BoolUserConfigParam m_everything_unlocked
|
||||
PARAM_DEFAULT( BoolUserConfigParam(false, "everything_unlocked",
|
||||
"Enable all karts and tracks") );
|
||||
|
||||
PARAM_PREFIX StringUserConfigParam m_commandline
|
||||
PARAM_DEFAULT( StringUserConfigParam("", "commandline",
|
||||
"Allows to set commandline args in config file") );
|
||||
|
||||
// TODO? implement blacklist for new irrlicht device and GUI
|
||||
PARAM_PREFIX std::vector<std::string> m_blacklist_res;
|
||||
|
@ -160,6 +160,9 @@ void IconButtonWidget::add()
|
||||
m_element = btn;
|
||||
m_id = m_element->getID();
|
||||
|
||||
if (!m_is_visible)
|
||||
m_element->setVisible(false);
|
||||
|
||||
// ---- label if any
|
||||
const stringw& message = getText();
|
||||
if (message.size() > 0)
|
||||
@ -222,6 +225,11 @@ void IconButtonWidget::add()
|
||||
m_label->setVisible(false);
|
||||
}
|
||||
|
||||
if (!m_is_visible)
|
||||
{
|
||||
m_label->setVisible(false);
|
||||
}
|
||||
|
||||
setLabelFont();
|
||||
|
||||
m_label->setRightToLeft(translations->isRTLText(message));
|
||||
|
@ -60,10 +60,18 @@ void AssetsAndroid::init()
|
||||
paths.push_back(getenv("SUPERTUXKART_DATADIR"));
|
||||
|
||||
if (global_android_app->activity->externalDataPath)
|
||||
{
|
||||
m_file_manager->checkAndCreateDirectoryP(
|
||||
global_android_app->activity->externalDataPath);
|
||||
paths.push_back(global_android_app->activity->externalDataPath);
|
||||
}
|
||||
|
||||
if (global_android_app->activity->internalDataPath)
|
||||
{
|
||||
m_file_manager->checkAndCreateDirectoryP(
|
||||
global_android_app->activity->internalDataPath);
|
||||
paths.push_back(global_android_app->activity->internalDataPath);
|
||||
}
|
||||
|
||||
if (getenv("EXTERNAL_STORAGE"))
|
||||
paths.push_back(getenv("EXTERNAL_STORAGE"));
|
||||
|
@ -146,6 +146,7 @@ public:
|
||||
} // ItemState(ItemType)
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
virtual ~ItemState() {}
|
||||
void setDisappearCounter();
|
||||
void update(int ticks);
|
||||
virtual void collected(const AbstractKart *kart);
|
||||
|
@ -543,6 +543,8 @@ void ItemManager::switchItems()
|
||||
|
||||
ItemState::ItemType new_type = m_switch_to[(*i)->getType()];
|
||||
|
||||
if (new_type == (*i)->getType())
|
||||
continue;
|
||||
if(m_switch_ticks<0)
|
||||
(*i)->switchTo(new_type, m_item_mesh[(int)new_type], m_item_lowres_mesh[(int)new_type]);
|
||||
else
|
||||
|
@ -30,10 +30,12 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class Kart;
|
||||
class STKPeer;
|
||||
|
||||
/**
|
||||
* \ingroup items
|
||||
@ -129,7 +131,8 @@ public:
|
||||
bool randomItemsForArena(const AlignedArray<btTransform>& pos);
|
||||
// ------------------------------------------------------------------------
|
||||
/** Only used in the NetworkItemManager. */
|
||||
virtual void setItemConfirmationTime(int host_id, int ticks)
|
||||
virtual void setItemConfirmationTime(std::weak_ptr<STKPeer> peer,
|
||||
int ticks)
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
|
@ -24,7 +24,7 @@
|
||||
#include "network/protocols/game_protocol.hpp"
|
||||
#include "network/rewind_manager.hpp"
|
||||
#include "network/stk_host.hpp"
|
||||
|
||||
#include "network/stk_peer.hpp"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Creates one instance of the item manager. */
|
||||
@ -42,16 +42,19 @@ NetworkItemManager::NetworkItemManager()
|
||||
: Rewinder(/*can be deleted*/false),
|
||||
ItemManager()
|
||||
{
|
||||
m_last_confirmed_item_ticks.lock();
|
||||
m_last_confirmed_item_ticks.getData().clear();
|
||||
m_last_confirmed_item_ticks.clear();
|
||||
|
||||
if (NetworkConfig::get()->isServer())
|
||||
{
|
||||
m_last_confirmed_item_ticks.getData()
|
||||
.resize(STKHost::get()->getPeerCount(), 0);
|
||||
auto peers = STKHost::get()->getPeers();
|
||||
for (auto& p : peers)
|
||||
{
|
||||
if (!p->isValidated())
|
||||
continue;
|
||||
m_last_confirmed_item_ticks[p] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
m_last_confirmed_item_ticks.unlock();
|
||||
} // NetworkItemManager
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -141,23 +144,32 @@ Item* NetworkItemManager::dropNewItem(ItemState::ItemType type,
|
||||
/** Called by the GameProtocol when a confirmation for an item event is
|
||||
* received by a host. Once all hosts have confirmed an event, it can be
|
||||
* deleted and won't be send to any clients again.
|
||||
* \param host_id Host identification of the host confirming the latest
|
||||
* event time received.
|
||||
* \param peer Peer confirming the latest event time received.
|
||||
* \param ticks Time at which the last event was received.
|
||||
*/
|
||||
void NetworkItemManager::setItemConfirmationTime(int host_id, int ticks)
|
||||
void NetworkItemManager::setItemConfirmationTime(std::weak_ptr<STKPeer> peer,
|
||||
int ticks)
|
||||
{
|
||||
assert(NetworkConfig::get()->isServer());
|
||||
m_last_confirmed_item_ticks.lock();
|
||||
if (ticks > m_last_confirmed_item_ticks.getData()[host_id])
|
||||
m_last_confirmed_item_ticks.getData()[host_id] = ticks;
|
||||
if (ticks > m_last_confirmed_item_ticks.at(peer))
|
||||
m_last_confirmed_item_ticks.at(peer) = ticks;
|
||||
|
||||
// Now discard unneeded events, i.e. all events that have
|
||||
// been confirmed by all clients:
|
||||
int min_time = 999999;
|
||||
for (auto i : m_last_confirmed_item_ticks.getData())
|
||||
if (i < min_time) min_time = i;
|
||||
m_last_confirmed_item_ticks.unlock();
|
||||
// Now discard unneeded events and expired (disconnected) peer, i.e. all
|
||||
// events that have been confirmed by all clients:
|
||||
int min_time = std::numeric_limits<int32_t>::max();
|
||||
for (auto it = m_last_confirmed_item_ticks.begin();
|
||||
it != m_last_confirmed_item_ticks.end();)
|
||||
{
|
||||
if (it->first.expired())
|
||||
{
|
||||
it = m_last_confirmed_item_ticks.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (it->second < min_time) min_time = it->second;
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the last entry before the minimal confirmed time.
|
||||
// Since the event list is sorted, all events up to this
|
||||
@ -169,7 +181,6 @@ void NetworkItemManager::setItemConfirmationTime(int host_id, int ticks)
|
||||
m_item_events.getData().erase(m_item_events.getData().begin(), p);
|
||||
m_item_events.unlock();
|
||||
|
||||
// TODO: Get informed when a client drops out!!!
|
||||
} // setItemConfirmationTime
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -290,7 +301,6 @@ void NetworkItemManager::restoreState(BareNetworkString *buffer, int count)
|
||||
if(iei.isItemCollection())
|
||||
{
|
||||
int index = iei.getIndex();
|
||||
ItemState *item_state = m_confirmed_state[index];
|
||||
// An item on the track was collected:
|
||||
AbstractKart *kart = World::getWorld()->getKart(iei.getKartId());
|
||||
m_confirmed_state[index]->collected(kart);
|
||||
@ -322,7 +332,8 @@ void NetworkItemManager::restoreState(BareNetworkString *buffer, int count)
|
||||
} // while count >0
|
||||
|
||||
// Inform the server which events have been received.
|
||||
GameProtocol::lock()->sendItemEventConfirmation(World::getWorld()->getTimeTicks());
|
||||
if (auto gp = GameProtocol::lock())
|
||||
gp->sendItemEventConfirmation(World::getWorld()->getTimeTicks());
|
||||
|
||||
// Forward the confirmed item state till the world time:
|
||||
int dt = World::getWorld()->getTimeTicks() - current_time;
|
||||
|
@ -25,6 +25,11 @@
|
||||
#include "utils/cpp2011.hpp"
|
||||
#include "utils/synchronised.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
class STKPeer;
|
||||
|
||||
/** \ingroup items
|
||||
* The network item manager is responsible for handling all network related
|
||||
* item manager tasks - synchronisation between clients and servers. It
|
||||
@ -47,7 +52,8 @@ private:
|
||||
int m_confirmed_state_time;
|
||||
|
||||
/** Stores on the server the latest confirmed tick from each client. */
|
||||
Synchronised< std::vector<int> > m_last_confirmed_item_ticks;
|
||||
std::map<std::weak_ptr<STKPeer>, int32_t,
|
||||
std::owner_less<std::weak_ptr<STKPeer> > > m_last_confirmed_item_ticks;
|
||||
|
||||
/** List of all items events. */
|
||||
Synchronised< std::vector<ItemEventInfo> > m_item_events;
|
||||
@ -64,8 +70,9 @@ public:
|
||||
void sendItemUpdate();
|
||||
void saveInitialState();
|
||||
|
||||
virtual void reset();
|
||||
virtual void setItemConfirmationTime(int host_id, int ticks) OVERRIDE;
|
||||
virtual void reset() OVERRIDE;
|
||||
virtual void setItemConfirmationTime(std::weak_ptr<STKPeer> peer,
|
||||
int ticks) OVERRIDE;
|
||||
virtual void collectedItem(Item *item, AbstractKart *kart) OVERRIDE;
|
||||
virtual Item* dropNewItem(ItemState::ItemType type, const AbstractKart *kart,
|
||||
const Vec3 *xyz=NULL) OVERRIDE;
|
||||
|
@ -1687,6 +1687,21 @@ void Kart::update(int ticks)
|
||||
|
||||
} // update
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void Kart::handleRewoundTransform()
|
||||
{
|
||||
if (!m_controller->isLocalPlayerController())
|
||||
{
|
||||
if (RewindManager::get()->isRewinding())
|
||||
m_rewound_transforms.push_back(getTrans());
|
||||
else if (!m_rewound_transforms.empty())
|
||||
{
|
||||
setTrans(m_rewound_transforms.front());
|
||||
m_rewound_transforms.pop_front();
|
||||
}
|
||||
}
|
||||
} // handleRewoundTransform
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Updates the local speed based on the current physical velocity. The value
|
||||
* is smoothed exponentially to avoid camera stuttering (camera distance
|
||||
@ -2765,7 +2780,8 @@ void Kart::loadData(RaceManager::KartType type, bool is_animated_model)
|
||||
m_skidmarks = new SkidMarks(*this);
|
||||
}
|
||||
|
||||
if (CVS->isGLSL() && !CVS->isShadowEnabled())
|
||||
if (CVS->isGLSL() && !CVS->isShadowEnabled() && m_kart_properties
|
||||
->getShadowMaterial()->getSamplerPath(0) != "unicolor_white")
|
||||
{
|
||||
m_shadow = new Shadow(m_kart_properties->getShadowMaterial(), *this);
|
||||
}
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "karts/abstract_kart.hpp"
|
||||
#include "utils/no_copy.hpp"
|
||||
|
||||
#include <deque>
|
||||
#include <SColor.h>
|
||||
|
||||
class AbstractKartAnimation;
|
||||
@ -250,6 +251,8 @@ protected:
|
||||
int m_ticks_last_crash;
|
||||
RaceManager::KartType m_type;
|
||||
|
||||
std::deque<btTransform> m_rewound_transforms;
|
||||
|
||||
/** To prevent using nitro in too short bursts */
|
||||
int m_min_nitro_ticks;
|
||||
|
||||
@ -547,6 +550,9 @@ public:
|
||||
virtual void playSound(SFXBuffer* buffer) OVERRIDE;
|
||||
// ------------------------------------------------------------------------
|
||||
virtual bool isVisible() OVERRIDE;
|
||||
// ------------------------------------------------------------------------
|
||||
void handleRewoundTransform();
|
||||
|
||||
}; // Kart
|
||||
|
||||
|
||||
|
@ -60,15 +60,39 @@ void KartRewinder::reset()
|
||||
void KartRewinder::saveTransform()
|
||||
{
|
||||
m_saved_transform = getTrans();
|
||||
m_rewound_transforms.clear();
|
||||
} // saveTransform
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void KartRewinder::computeError()
|
||||
{
|
||||
// Local player kart doesn't need showing in the past
|
||||
if (m_rewound_transforms.empty())
|
||||
return;
|
||||
|
||||
std::deque<btTransform> copied = m_rewound_transforms;
|
||||
// Find the closest position that matches previous rewound one
|
||||
Vec3 saved_position = m_saved_transform.getOrigin();
|
||||
while (!copied.empty())
|
||||
{
|
||||
Vec3 cur_position = copied.front().getOrigin();
|
||||
if ((cur_position - saved_position).length() < 0.25f)
|
||||
{
|
||||
setTrans(copied.front());
|
||||
copied.pop_front();
|
||||
std::swap(m_rewound_transforms, copied);
|
||||
return;
|
||||
}
|
||||
copied.pop_front();
|
||||
}
|
||||
|
||||
// Use newly rewound one if no matching transformation
|
||||
setTrans(m_rewound_transforms.front());
|
||||
m_rewound_transforms.pop_front();
|
||||
//btTransform error = getTrans() - m_saved_transform;
|
||||
Vec3 pos_error = getTrans().getOrigin() - m_saved_transform.getOrigin();
|
||||
btQuaternion rot_error(0, 0, 0, 1);
|
||||
Kart::addError(pos_error, rot_error);
|
||||
//Vec3 pos_error = getTrans().getOrigin() - m_saved_transform.getOrigin();
|
||||
//btQuaternion rot_error(0, 0, 0, 1);
|
||||
//Kart::addError(pos_error, rot_error);
|
||||
} // computeError
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -106,7 +106,7 @@ void Moveable::updateGraphics(float dt, const Vec3& offset_xyz,
|
||||
#endif
|
||||
}
|
||||
#ifndef SERVER_ONLY
|
||||
Vec3 xyz=getXYZ()+offset_xyz - m_positional_error;
|
||||
Vec3 xyz=getXYZ()+offset_xyz;
|
||||
m_node->setPosition(xyz.toIrrVector());
|
||||
btQuaternion r_all = getRotation()*rotation;
|
||||
if(btFuzzyZero(r_all.getX()) && btFuzzyZero(r_all.getY()-0.70710677f) &&
|
||||
|
17
src/main.cpp
@ -1574,7 +1574,9 @@ void initRest()
|
||||
// This only initialises the non-network part of the add-ons manager. The
|
||||
// online section of the add-ons manager will be initialised from a
|
||||
// separate thread running in network HTTP.
|
||||
#ifndef SERVER_ONLY
|
||||
addons_manager = new AddonsManager();
|
||||
#endif
|
||||
Online::ProfileManager::create();
|
||||
|
||||
// The request manager will start the login process in case of a saved
|
||||
@ -1583,7 +1585,9 @@ void initRest()
|
||||
// achievement managers to be created, which can only be created later).
|
||||
PlayerManager::create();
|
||||
Online::RequestManager::get()->startNetworkThread();
|
||||
#ifndef SERVER_ONLY
|
||||
NewsManager::get(); // this will create the news manager
|
||||
#endif
|
||||
|
||||
music_manager = new MusicManager();
|
||||
SFXManager::create();
|
||||
@ -1664,8 +1668,10 @@ void askForInternetPermission()
|
||||
Online::RequestManager::IPERM_ALLOWED;
|
||||
UserConfigParams::m_internet_status =
|
||||
Online::RequestManager::IPERM_ALLOWED;
|
||||
#ifndef SERVER_ONLY
|
||||
if (need_to_start_news_manager)
|
||||
NewsManager::get()->init(false);
|
||||
#endif
|
||||
GUIEngine::ModalDialog::dismiss();
|
||||
} // onConfirm
|
||||
// --------------------------------------------------------
|
||||
@ -1731,6 +1737,8 @@ int main(int argc, char *argv[] )
|
||||
// handle all command line options that do not need (or must
|
||||
// not have) other managers initialised:
|
||||
initUserConfig();
|
||||
|
||||
CommandLine::addArgsFromUserConfig();
|
||||
|
||||
handleCmdLinePreliminary();
|
||||
|
||||
@ -1806,6 +1814,7 @@ int main(int argc, char *argv[] )
|
||||
//handleCmdLine() needs InitTuxkart() so it can't be called first
|
||||
if(!handleCmdLine()) exit(0);
|
||||
|
||||
#ifndef SERVER_ONLY
|
||||
addons_manager->checkInstalledAddons();
|
||||
|
||||
// Load addons.xml to get info about add-ons even when not
|
||||
@ -1827,6 +1836,7 @@ int main(int argc, char *argv[] )
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if(UserConfigParams::m_unit_testing)
|
||||
{
|
||||
@ -1959,6 +1969,7 @@ int main(int argc, char *argv[] )
|
||||
StateManager::get()->enterGameState();
|
||||
}
|
||||
|
||||
#ifndef SERVER_ONLY
|
||||
// If an important news message exists it is shown in a popup dialog.
|
||||
const core::stringw important_message =
|
||||
NewsManager::get()->getImportantMessage();
|
||||
@ -1968,7 +1979,7 @@ int main(int argc, char *argv[] )
|
||||
MessageDialog::MESSAGE_DIALOG_OK,
|
||||
NULL, true);
|
||||
} // if important_message
|
||||
|
||||
#endif
|
||||
|
||||
// Replay a race
|
||||
// =============
|
||||
@ -2119,11 +2130,13 @@ static void cleanSuperTuxKart()
|
||||
// was deleted (in cleanUserConfig below), but before STK finishes and
|
||||
// the OS takes all threads down.
|
||||
|
||||
#ifndef SERVER_ONLY
|
||||
if(!NewsManager::get()->waitForReadyToDeleted(2.0f))
|
||||
{
|
||||
Log::info("Thread", "News manager not stopping, exiting anyway.");
|
||||
}
|
||||
NewsManager::deallocate();
|
||||
#endif
|
||||
|
||||
if(!Online::RequestManager::get()->waitForReadyToDeleted(5.0f))
|
||||
{
|
||||
@ -2144,7 +2157,9 @@ static void cleanSuperTuxKart()
|
||||
|
||||
// The add-ons manager might still be called from a currenty running request
|
||||
// in the request manager, so it can not be deleted earlier.
|
||||
#ifndef SERVER_ONLY
|
||||
if(addons_manager) delete addons_manager;
|
||||
#endif
|
||||
|
||||
ServersManager::deallocate();
|
||||
cleanUserConfig();
|
||||
|
@ -1056,6 +1056,16 @@ void World::update(int ticks)
|
||||
|
||||
Physics::getInstance()->update(ticks);
|
||||
|
||||
if (NetworkConfig::get()->isNetworking() &&
|
||||
NetworkConfig::get()->isClient())
|
||||
{
|
||||
for (int i = 0 ; i < kart_amount; i++)
|
||||
{
|
||||
if (!m_karts[i]->isEliminated())
|
||||
static_cast<Kart*>(m_karts[i])->handleRewoundTransform();
|
||||
}
|
||||
}
|
||||
|
||||
PROFILER_PUSH_CPU_MARKER("World::update (projectiles)", 0xa0, 0x7F, 0x00);
|
||||
projectile_manager->update(ticks);
|
||||
PROFILER_POP_CPU_MARKER();
|
||||
|
@ -322,6 +322,11 @@ void ClientLobby::update(int ticks)
|
||||
case PLAYING:
|
||||
break;
|
||||
case RACE_FINISHED:
|
||||
if (!RaceEventManager::getInstance()->protocolStopped() ||
|
||||
!GameProtocol::emptyInstance())
|
||||
return;
|
||||
if (!m_received_server_result)
|
||||
m_received_server_result = true;
|
||||
break;
|
||||
case DONE:
|
||||
m_state.store(EXITING);
|
||||
@ -762,16 +767,9 @@ void ClientLobby::raceFinished(Event* event)
|
||||
|
||||
// stop race protocols
|
||||
RaceEventManager::getInstance()->stop();
|
||||
|
||||
RaceEventManager::getInstance()->getProtocol()->requestTerminate();
|
||||
GameProtocol::lock()->requestTerminate();
|
||||
|
||||
while (!RaceEventManager::getInstance()->protocolStopped())
|
||||
StkTime::sleep(1);
|
||||
while (!GameProtocol::emptyInstance())
|
||||
StkTime::sleep(1);
|
||||
|
||||
m_received_server_result = true;
|
||||
m_state.store(RACE_FINISHED);
|
||||
} // raceFinished
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -101,9 +101,6 @@ bool GameProtocol::notifyEventAsynchronous(Event* event)
|
||||
|
||||
NetworkString &data = event->data();
|
||||
uint8_t message_type = data.getUInt8();
|
||||
if (message_type != GP_CONTROLLER_ACTION &&
|
||||
message_type != GP_STATE)
|
||||
printf("");
|
||||
switch (message_type)
|
||||
{
|
||||
case GP_CONTROLLER_ACTION: handleControllerAction(event); break;
|
||||
@ -298,10 +295,9 @@ void GameProtocol::sendItemEventConfirmation(int ticks)
|
||||
void GameProtocol::handleItemEventConfirmation(Event *event)
|
||||
{
|
||||
assert(NetworkConfig::get()->isServer());
|
||||
int host_id = event->getPeer()->getHostId();
|
||||
int ticks = event->data().getTime();
|
||||
NetworkItemManager::get()->setItemConfirmationTime(host_id, ticks);
|
||||
|
||||
int ticks = event->data().getTime();
|
||||
NetworkItemManager::get()->setItemConfirmationTime(event->getPeerSP(),
|
||||
ticks);
|
||||
} // handleItemEventConfirmation
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -99,6 +99,9 @@ ServerLobby::ServerLobby() : LobbyProtocol(NULL)
|
||||
"STK addons server, don't bother host one if you don't have the "
|
||||
"corresponding permission, they will be rejected if so.");
|
||||
}
|
||||
m_result_ns = getNetworkString();
|
||||
m_result_ns->setSynchronous(true);
|
||||
m_waiting_for_reset = false;
|
||||
} // ServerLobby
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -110,6 +113,7 @@ ServerLobby::~ServerLobby()
|
||||
{
|
||||
unregisterServer(true/*now*/);
|
||||
}
|
||||
delete m_result_ns;
|
||||
} // ~ServerLobby
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -142,6 +146,8 @@ void ServerLobby::setup()
|
||||
m_peers_votes.clear();
|
||||
m_server_delay = 0.0;
|
||||
m_timeout.store(std::numeric_limits<float>::max());
|
||||
m_waiting_for_reset = false;
|
||||
|
||||
Log::info("ServerLobby", "Reset server to initial state.");
|
||||
} // setup
|
||||
|
||||
@ -426,6 +432,21 @@ void ServerLobby::asynchronousUpdate()
|
||||
void ServerLobby::update(int ticks)
|
||||
{
|
||||
// Reset server to initial state if no more connected players
|
||||
if (m_waiting_for_reset)
|
||||
{
|
||||
if ((RaceEventManager::getInstance() &&
|
||||
!RaceEventManager::getInstance()->protocolStopped()) ||
|
||||
!GameProtocol::emptyInstance())
|
||||
return;
|
||||
|
||||
RaceResultGUI::getInstance()->backToLobby();
|
||||
std::lock_guard<std::mutex> lock(m_connection_mutex);
|
||||
m_game_setup->stopGrandPrix();
|
||||
m_state = NetworkConfig::get()->isLAN() ?
|
||||
ACCEPTING_CLIENTS : REGISTER_SELF_ADDRESS;
|
||||
setup();
|
||||
}
|
||||
|
||||
if ((m_state.load() > ACCEPTING_CLIENTS ||
|
||||
m_game_setup->isGrandPrixStarted()) &&
|
||||
STKHost::get()->getPeerCount() == 0 &&
|
||||
@ -434,13 +455,12 @@ void ServerLobby::update(int ticks)
|
||||
if (RaceEventManager::getInstance() &&
|
||||
RaceEventManager::getInstance()->isRunning())
|
||||
{
|
||||
stopCurrentRace();
|
||||
RaceEventManager::getInstance()->stop();
|
||||
RaceEventManager::getInstance()->getProtocol()->requestTerminate();
|
||||
GameProtocol::lock()->requestTerminate();
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(m_connection_mutex);
|
||||
m_game_setup->stopGrandPrix();
|
||||
m_state = NetworkConfig::get()->isLAN() ?
|
||||
ACCEPTING_CLIENTS : REGISTER_SELF_ADDRESS;
|
||||
setup();
|
||||
m_waiting_for_reset = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset for ranked server if in kart / track selection has only 1 player
|
||||
@ -502,6 +522,22 @@ void ServerLobby::update(int ticks)
|
||||
checkRaceFinished();
|
||||
}
|
||||
break;
|
||||
case WAIT_FOR_RACE_STOPPED:
|
||||
if (!RaceEventManager::getInstance()->protocolStopped() ||
|
||||
!GameProtocol::emptyInstance())
|
||||
return;
|
||||
|
||||
// This will go back to lobby in server (and exit the current race)
|
||||
RaceResultGUI::getInstance()->backToLobby();
|
||||
// Reset for next state usage
|
||||
resetPeersReady();
|
||||
// Set the delay before the server forces all clients to exit the race
|
||||
// result screen and go back to the lobby
|
||||
m_timeout.store((float)StkTime::getRealTime() + 15.0f);
|
||||
m_state = RESULT_DISPLAY;
|
||||
sendMessageToPeers(m_result_ns, /*reliable*/ true);
|
||||
Log::info("ServerLobby", "End of game message sent");
|
||||
break;
|
||||
case RESULT_DISPLAY:
|
||||
if (checkPeersReady() ||
|
||||
StkTime::getRealTime() > m_timeout.load())
|
||||
@ -811,28 +847,32 @@ void ServerLobby::checkRaceFinished()
|
||||
if (!RaceEventManager::getInstance()->isRaceOver()) return;
|
||||
|
||||
Log::info("ServerLobby", "The game is considered finish.");
|
||||
// notify the network world that it is stopped
|
||||
RaceEventManager::getInstance()->stop();
|
||||
|
||||
// Reset for next state usage
|
||||
resetPeersReady();
|
||||
NetworkString* total = getNetworkString();
|
||||
total->setSynchronous(true);
|
||||
total->addUInt8(LE_RACE_FINISHED);
|
||||
// stop race protocols before going back to lobby (end race)
|
||||
RaceEventManager::getInstance()->getProtocol()->requestTerminate();
|
||||
GameProtocol::lock()->requestTerminate();
|
||||
|
||||
// Save race result before delete the world
|
||||
m_result_ns->clear();
|
||||
m_result_ns->addUInt8(LE_RACE_FINISHED);
|
||||
if (m_game_setup->isGrandPrix())
|
||||
{
|
||||
// fastest lap
|
||||
int fastest_lap =
|
||||
static_cast<LinearWorld*>(World::getWorld())->getFastestLapTicks();
|
||||
total->addUInt32(fastest_lap);
|
||||
m_result_ns->addUInt32(fastest_lap);
|
||||
|
||||
// all gp tracks
|
||||
total->addUInt8((uint8_t)m_game_setup->getTotalGrandPrixTracks())
|
||||
m_result_ns->addUInt8((uint8_t)m_game_setup->getTotalGrandPrixTracks())
|
||||
.addUInt8((uint8_t)m_game_setup->getAllTracks().size());
|
||||
for (const std::string& gp_track : m_game_setup->getAllTracks())
|
||||
total->encodeString(gp_track);
|
||||
m_result_ns->encodeString(gp_track);
|
||||
|
||||
// each kart score and total time
|
||||
auto& players = m_game_setup->getPlayers();
|
||||
total->addUInt8((uint8_t)players.size());
|
||||
m_result_ns->addUInt8((uint8_t)players.size());
|
||||
for (unsigned i = 0; i < players.size(); i++)
|
||||
{
|
||||
int last_score = race_manager->getKartScore(i);
|
||||
@ -846,7 +886,7 @@ void ServerLobby::checkRaceFinished()
|
||||
player->setScore(cur_score);
|
||||
player->setOverallTime(overall_time);
|
||||
}
|
||||
total->addUInt32(last_score).addUInt32(cur_score)
|
||||
m_result_ns->addUInt32(last_score).addUInt32(cur_score)
|
||||
.addFloat(overall_time);
|
||||
}
|
||||
}
|
||||
@ -854,23 +894,14 @@ void ServerLobby::checkRaceFinished()
|
||||
{
|
||||
int fastest_lap =
|
||||
static_cast<LinearWorld*>(World::getWorld())->getFastestLapTicks();
|
||||
total->addUInt32(fastest_lap);
|
||||
m_result_ns->addUInt32(fastest_lap);
|
||||
}
|
||||
if (NetworkConfig::get()->isRankedServer())
|
||||
{
|
||||
computeNewRankings();
|
||||
submitRankingsToAddons();
|
||||
}
|
||||
|
||||
stopCurrentRace();
|
||||
// Set the delay before the server forces all clients to exit the race
|
||||
// result screen and go back to the lobby
|
||||
m_timeout.store((float)StkTime::getRealTime() + 15.0f);
|
||||
m_state = RESULT_DISPLAY;
|
||||
sendMessageToPeers(total, /*reliable*/ true);
|
||||
delete total;
|
||||
Log::info("ServerLobby", "End of game message sent");
|
||||
|
||||
m_state.store(WAIT_FOR_RACE_STOPPED);
|
||||
} // checkRaceFinished
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -940,13 +971,13 @@ void ServerLobby::computeNewRankings()
|
||||
{
|
||||
result = 0.0;
|
||||
ranking_importance = mode_factor *
|
||||
MAX_SCALING_TIME * MAX_POINTS_PER_SECOND * player_factors;
|
||||
scalingValueForTime(MAX_SCALING_TIME) * player_factors;
|
||||
}
|
||||
else if (!players[j])
|
||||
{
|
||||
result = 1.0;
|
||||
ranking_importance = mode_factor *
|
||||
MAX_SCALING_TIME * MAX_POINTS_PER_SECOND * player_factors;
|
||||
scalingValueForTime(MAX_SCALING_TIME) * player_factors;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -964,10 +995,11 @@ void ServerLobby::computeNewRankings()
|
||||
(player1_time - player2_time) / (player2_time / 20.0);
|
||||
result = std::max(0.0, 0.5 - result);
|
||||
}
|
||||
|
||||
double max_time = std::min(std::max(player1_time, player2_time),
|
||||
MAX_SCALING_TIME);
|
||||
ranking_importance = mode_factor *
|
||||
std::min(
|
||||
std::max(player1_time, player2_time), MAX_SCALING_TIME) *
|
||||
MAX_POINTS_PER_SECOND * player_factors;
|
||||
scalingValueForTime(max_time) * player_factors;
|
||||
}
|
||||
// Compute the ranking change
|
||||
scores_change[i] +=
|
||||
@ -1040,6 +1072,15 @@ double ServerLobby::getModeSpread()
|
||||
return 1.4;
|
||||
} // getModeSpread
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Compute the scaling value of a given time
|
||||
* Short races are more random, so we don't use strict proportionality
|
||||
*/
|
||||
double ServerLobby::scalingValueForTime(double time)
|
||||
{
|
||||
return time * sqrt(time / 120.0) * MAX_POINTS_PER_SECOND;
|
||||
} // scalingValueForTime
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Manages the distribution of the base points.
|
||||
* Gives half of the points progressively
|
||||
@ -1052,35 +1093,12 @@ double ServerLobby::distributeBasePoints(uint32_t online_id)
|
||||
unsigned num_races = m_num_ranked_races.at(online_id);
|
||||
if (num_races < 45)
|
||||
{
|
||||
return
|
||||
(BASE_RANKING_POINTS / 2000.0 * std::max((45u - num_races), 4u) *
|
||||
2.0);
|
||||
return BASE_RANKING_POINTS / 2000.0 * std::max((45u - num_races), 4u);
|
||||
}
|
||||
else
|
||||
return 0.0;
|
||||
} // distributeBasePoints
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Stop any race currently in server, should only be called in main thread.
|
||||
*/
|
||||
void ServerLobby::stopCurrentRace()
|
||||
{
|
||||
// notify the network world that it is stopped
|
||||
RaceEventManager::getInstance()->stop();
|
||||
|
||||
// stop race protocols before going back to lobby (end race)
|
||||
RaceEventManager::getInstance()->getProtocol()->requestTerminate();
|
||||
GameProtocol::lock()->requestTerminate();
|
||||
|
||||
while (!RaceEventManager::getInstance()->protocolStopped())
|
||||
StkTime::sleep(1);
|
||||
while (!GameProtocol::emptyInstance())
|
||||
StkTime::sleep(1);
|
||||
|
||||
// This will go back to lobby in server (and exit the current race)
|
||||
RaceResultGUI::getInstance()->backToLobby();
|
||||
} // stopCurrentRace
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Called when a client disconnects.
|
||||
* \param event The disconnect event.
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <tuple>
|
||||
|
||||
class BareNetworkString;
|
||||
class NetworkString;
|
||||
class NetworkPlayerProfile;
|
||||
class STKPeer;
|
||||
|
||||
@ -34,6 +35,7 @@ public:
|
||||
WAIT_FOR_RACE_STARTED, // Wait for all clients to have started the race
|
||||
DELAY_SERVER, // Additional server delay
|
||||
RACING, // racing
|
||||
WAIT_FOR_RACE_STOPPED, // Wait server for stopping all race protocols
|
||||
RESULT_DISPLAY, // Show result screen
|
||||
ERROR_LEAVE, // shutting down server
|
||||
EXITING
|
||||
@ -104,7 +106,7 @@ private:
|
||||
/* Ranking related variables */
|
||||
// If updating the base points, update the base points distribution in DB
|
||||
const double BASE_RANKING_POINTS = 4000.0;
|
||||
const double MAX_SCALING_TIME = 600.0;
|
||||
const double MAX_SCALING_TIME = 500.0;
|
||||
const double MAX_POINTS_PER_SECOND = 0.125;
|
||||
|
||||
/** Online id to profile map, handling disconnection in ranked server */
|
||||
@ -119,6 +121,10 @@ private:
|
||||
/** Number of ranked races done for each current players */
|
||||
std::map<uint32_t, unsigned> m_num_ranked_races;
|
||||
|
||||
bool m_waiting_for_reset;
|
||||
|
||||
NetworkString* m_result_ns;
|
||||
|
||||
// connection management
|
||||
void clientDisconnected(Event* event);
|
||||
void connectionRequested(Event* event);
|
||||
@ -183,8 +189,6 @@ private:
|
||||
uint32_t online_id,
|
||||
const irr::core::stringw& online_name);
|
||||
std::tuple<std::string, uint8_t, bool, bool> handleVote();
|
||||
void stopCurrentRace();
|
||||
|
||||
void getRankingForPlayer(std::shared_ptr<NetworkPlayerProfile> p);
|
||||
void submitRankingsToAddons();
|
||||
void computeNewRankings();
|
||||
@ -193,6 +197,8 @@ private:
|
||||
double distributeBasePoints(uint32_t online_id);
|
||||
double getModeFactor();
|
||||
double getModeSpread();
|
||||
double scalingValueForTime(double time);
|
||||
void checkRaceFinished();
|
||||
|
||||
public:
|
||||
ServerLobby();
|
||||
@ -207,7 +213,6 @@ public:
|
||||
void signalRaceStartToClients();
|
||||
void startSelection(const Event *event=NULL);
|
||||
void checkIncomingConnectionRequests();
|
||||
void checkRaceFinished();
|
||||
void finishedLoadingWorld() OVERRIDE;
|
||||
ServerState getCurrentState() const { return m_state.load(); }
|
||||
void updateBanList();
|
||||
|
@ -162,17 +162,20 @@ void RewindManager::addNetworkState(BareNetworkString *buffer, int ticks)
|
||||
void RewindManager::saveState(bool local_save)
|
||||
{
|
||||
PROFILER_PUSH_CPU_MARKER("RewindManager - save state", 0x20, 0x7F, 0x20);
|
||||
GameProtocol::lock()->startNewState(local_save);
|
||||
auto gp = GameProtocol::lock();
|
||||
if (!gp)
|
||||
return;
|
||||
gp->startNewState(local_save);
|
||||
AllRewinder::const_iterator rewinder;
|
||||
for (rewinder = m_all_rewinder.begin(); rewinder != m_all_rewinder.end(); ++rewinder)
|
||||
{
|
||||
// TODO: check if it's worth passing in a sufficiently large buffer from
|
||||
// GameProtocol - this would save the copy operation.
|
||||
BareNetworkString *buffer = (*rewinder)->saveState();
|
||||
if (buffer && buffer->size() >= 0)
|
||||
if (buffer)
|
||||
{
|
||||
m_overall_state_size += buffer->size();
|
||||
GameProtocol::lock()->addState(buffer);
|
||||
gp->addState(buffer);
|
||||
} // size >= 0
|
||||
delete buffer; // buffer can be freed
|
||||
}
|
||||
@ -186,10 +189,14 @@ void RewindManager::saveState(bool local_save)
|
||||
*/
|
||||
void RewindManager::saveLocalState()
|
||||
{
|
||||
auto gp = GameProtocol::lock();
|
||||
if (!gp)
|
||||
return;
|
||||
|
||||
int ticks = World::getWorld()->getTimeTicks();
|
||||
|
||||
saveState(/*local_state*/true);
|
||||
NetworkString *state = GameProtocol::lock()->getState();
|
||||
NetworkString *state = gp->getState();
|
||||
|
||||
// Copy the data to a new string, making the buffer in
|
||||
// GameProtocol availble for again.
|
||||
@ -208,7 +215,6 @@ void RewindManager::saveLocalState()
|
||||
void RewindManager::restoreState(BareNetworkString *data)
|
||||
{
|
||||
data->reset();
|
||||
int index = 0;
|
||||
|
||||
for (auto rewinder = m_all_rewinder.begin();
|
||||
rewinder != m_all_rewinder.end(); ++rewinder)
|
||||
@ -230,7 +236,6 @@ void RewindManager::update(int ticks_not_used)
|
||||
m_all_rewinder.size() == 0 ||
|
||||
m_is_rewinding) return;
|
||||
|
||||
float time = World::getWorld()->getTime();
|
||||
int ticks = World::getWorld()->getTimeTicks();
|
||||
|
||||
m_not_rewound_ticks.store(ticks, std::memory_order_relaxed);
|
||||
@ -245,7 +250,8 @@ void RewindManager::update(int ticks_not_used)
|
||||
// Save state
|
||||
saveState(/**allow_local_save*/false);
|
||||
PROFILER_PUSH_CPU_MARKER("RewindManager - send state", 0x20, 0x7F, 0x40);
|
||||
GameProtocol::lock()->sendState();
|
||||
if (auto gp = GameProtocol::lock())
|
||||
gp->sendState();
|
||||
PROFILER_POP_CPU_MARKER();
|
||||
m_last_saved_state = ticks;
|
||||
} // update
|
||||
|
@ -210,7 +210,6 @@ void RewindQueue::mergeNetworkData(int world_ticks, bool *needs_rewind,
|
||||
// Only a client ever rewinds. So the rewind time should be the latest
|
||||
// received state before current world time (if any)
|
||||
*rewind_ticks = -9999;
|
||||
bool adjust_next = false;
|
||||
|
||||
// FIXME: making m_network_events sorted would prevent the need to
|
||||
// go through the whole list of events
|
||||
@ -261,7 +260,8 @@ void RewindQueue::mergeNetworkData(int world_ticks, bool *needs_rewind,
|
||||
// any server message should be in the client's past - but it can
|
||||
// happen during debugging) we need to rewind to getTicks (in order
|
||||
// to get the latest state).
|
||||
if (NetworkConfig::get()->isClient() && (*i)->getTicks() <= world_ticks)
|
||||
if (NetworkConfig::get()->isClient() && (*i)->getTicks() <= world_ticks
|
||||
&& (*i)->isState())
|
||||
{
|
||||
// We need rewind if we receive an event in the past. This will
|
||||
// then trigger a rewind later. Note that we only rewind to the
|
||||
|
@ -255,16 +255,18 @@ bool ProfileManager::inPersistent(const uint32_t id)
|
||||
*/
|
||||
OnlineProfile* ProfileManager::addPersistent(OnlineProfile * profile)
|
||||
{
|
||||
if (inPersistent(profile->getID()))
|
||||
uint32_t profile_id = profile->getID();
|
||||
|
||||
if (inPersistent(profile_id))
|
||||
{
|
||||
m_profiles_persistent[profile->getID()]->merge(profile);
|
||||
m_profiles_persistent[profile_id]->merge(profile);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_profiles_persistent[profile->getID()] = profile;
|
||||
m_profiles_persistent[profile_id] = profile;
|
||||
}
|
||||
|
||||
return m_profiles_persistent[profile->getID()];
|
||||
return m_profiles_persistent[profile_id];
|
||||
} // addPersistent
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
12
src/physics/btKart.cpp
Normal file → Executable file
@ -353,17 +353,17 @@ void btKart::getVisualContactPoint(float visual_rotation,
|
||||
m_visual_wheels_touch_ground = true;
|
||||
|
||||
short int old_group = 0;
|
||||
if (m_chassisBody->getBroadphaseHandle())
|
||||
{
|
||||
old_group = m_chassisBody->getBroadphaseHandle()
|
||||
->m_collisionFilterGroup;
|
||||
m_chassisBody->getBroadphaseHandle()->m_collisionFilterGroup = 0;
|
||||
}
|
||||
for (int index = 2; index <= 3; index++)
|
||||
{
|
||||
// Map index 0-1 to wheel 2-3 (which are the rear wheels)
|
||||
btWheelInfo &wheel = m_wheelInfo[index];
|
||||
updateWheelTransformsWS(wheel, false);
|
||||
if (m_chassisBody->getBroadphaseHandle())
|
||||
{
|
||||
old_group = m_chassisBody->getBroadphaseHandle()
|
||||
->m_collisionFilterGroup;
|
||||
m_chassisBody->getBroadphaseHandle()->m_collisionFilterGroup = 0;
|
||||
}
|
||||
btScalar max_susp_len = wheel.getSuspensionRestLength()
|
||||
+ wheel.m_maxSuspensionTravel;
|
||||
|
||||
|
@ -234,7 +234,7 @@ void AddonsScreen::tearDown()
|
||||
*/
|
||||
void AddonsScreen::loadList()
|
||||
{
|
||||
|
||||
#ifndef SERVER_ONLY
|
||||
// Get the filter by words.
|
||||
GUIEngine::TextBoxWidget* w_filter_name =
|
||||
getWidget<GUIEngine::TextBoxWidget>("filter_name");
|
||||
@ -416,6 +416,7 @@ void AddonsScreen::loadList()
|
||||
else
|
||||
getWidget<GUIEngine::RibbonWidget>("category")->select("tab_update",
|
||||
PLAYER_ID_GAME_MASTER);
|
||||
#endif
|
||||
} // loadList
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -452,6 +453,7 @@ void AddonsScreen::onColumnClicked(int column_id)
|
||||
void AddonsScreen::eventCallback(GUIEngine::Widget* widget,
|
||||
const std::string& name, const int playerID)
|
||||
{
|
||||
#ifndef SERVER_ONLY
|
||||
if (name == "back")
|
||||
{
|
||||
StateManager::get()->escapePressed();
|
||||
@ -509,7 +511,7 @@ void AddonsScreen::eventCallback(GUIEngine::Widget* widget,
|
||||
{
|
||||
loadList();
|
||||
}
|
||||
|
||||
#endif
|
||||
} // eventCallback
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -534,6 +536,7 @@ void AddonsScreen::setLastSelected()
|
||||
|
||||
void AddonsScreen::onUpdate(float dt)
|
||||
{
|
||||
#ifndef SERVER_ONLY
|
||||
if (m_reloading)
|
||||
{
|
||||
if(UserConfigParams::m_internet_status!=RequestManager::IPERM_ALLOWED)
|
||||
@ -579,5 +582,5 @@ void AddonsScreen::onUpdate(float dt)
|
||||
m_show_tips = false;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
} // onUpdate
|
||||
|
@ -46,7 +46,6 @@ class AddonsScreen : public GUIEngine::Screen,
|
||||
friend class GUIEngine::ScreenSingleton<AddonsScreen>;
|
||||
private:
|
||||
AddonsScreen();
|
||||
AddonsManager *m_addons;
|
||||
AddonsLoading *m_load;
|
||||
void loadInformations();
|
||||
/** Icon for installed addon, which can be updated. */
|
||||
|
@ -45,8 +45,10 @@ using namespace irr::gui;
|
||||
*/
|
||||
|
||||
AddonsLoading::AddonsLoading(const std::string &id)
|
||||
: ModalDialog(0.8f, 0.8f),
|
||||
m_addon(*(addons_manager->getAddon(id)) )
|
||||
: ModalDialog(0.8f, 0.8f)
|
||||
#ifndef SERVER_ONLY
|
||||
, m_addon(*(addons_manager->getAddon(id)) )
|
||||
#endif
|
||||
{
|
||||
|
||||
m_icon_shown = false;
|
||||
@ -82,6 +84,7 @@ AddonsLoading::~AddonsLoading()
|
||||
|
||||
void AddonsLoading::beforeAddingWidgets()
|
||||
{
|
||||
#ifndef SERVER_ONLY
|
||||
/* Init the icon here to be able to load a single image*/
|
||||
m_icon = getWidget<IconButtonWidget> ("icon" );
|
||||
m_progress = getWidget<ProgressBarWidget>("progress");
|
||||
@ -180,6 +183,7 @@ void AddonsLoading::beforeAddingWidgets()
|
||||
unit = _LTR("%s KB", 1);
|
||||
core::stringw size = _("Size: %s", unit.c_str());
|
||||
getWidget<LabelWidget>("size")->setText(size, false);
|
||||
#endif
|
||||
} // AddonsLoading
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -205,6 +209,7 @@ bool AddonsLoading::onEscapePressed()
|
||||
|
||||
GUIEngine::EventPropagation AddonsLoading::processEvent(const std::string& event_source)
|
||||
{
|
||||
#ifndef SERVER_ONLY
|
||||
GUIEngine::RibbonWidget* actions_ribbon =
|
||||
getWidget<GUIEngine::RibbonWidget>("actions");
|
||||
|
||||
@ -252,12 +257,14 @@ GUIEngine::EventPropagation AddonsLoading::processEvent(const std::string& event
|
||||
voteClicked();
|
||||
return GUIEngine::EVENT_BLOCK;
|
||||
}
|
||||
#endif
|
||||
return GUIEngine::EVENT_LET;
|
||||
} // processEvent
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void AddonsLoading::voteClicked()
|
||||
{
|
||||
#ifndef SERVER_ONLY
|
||||
if (PlayerManager::isCurrentLoggedIn())
|
||||
{
|
||||
// We need to keep a copy of the addon id, since dismiss() will
|
||||
@ -266,11 +273,13 @@ void AddonsLoading::voteClicked()
|
||||
dismiss();
|
||||
new VoteDialog(addon_id);
|
||||
}
|
||||
#endif
|
||||
} // voteClicked
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void AddonsLoading::onUpdate(float delta)
|
||||
{
|
||||
#ifndef SERVER_ONLY
|
||||
if(m_progress->isVisible())
|
||||
{
|
||||
float progress = m_download_request->getProgress();
|
||||
@ -308,6 +317,7 @@ void AddonsLoading::onUpdate(float delta)
|
||||
}
|
||||
m_icon_shown = true;
|
||||
}
|
||||
#endif
|
||||
} // onUpdate
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -316,13 +326,14 @@ void AddonsLoading::onUpdate(float delta)
|
||||
**/
|
||||
void AddonsLoading::startDownload()
|
||||
{
|
||||
#ifndef SERVER_ONLY
|
||||
std::string save = "tmp/"
|
||||
+ StringUtils::getBasename(m_addon.getZipFileName());
|
||||
m_download_request = new Online::HTTPRequest(save, /*manage mem*/false,
|
||||
/*priority*/5);
|
||||
m_download_request->setURL(m_addon.getZipFileName());
|
||||
m_download_request->queue();
|
||||
|
||||
#endif
|
||||
} // startDownload
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -353,6 +364,7 @@ void AddonsLoading::stopDownload()
|
||||
*/
|
||||
void AddonsLoading::doInstall()
|
||||
{
|
||||
#ifndef SERVER_ONLY
|
||||
delete m_download_request;
|
||||
m_download_request = NULL;
|
||||
|
||||
@ -383,12 +395,14 @@ void AddonsLoading::doInstall()
|
||||
}
|
||||
|
||||
track_manager->loadTrackList();
|
||||
#endif
|
||||
} // doInstall
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void AddonsLoading::doUninstall()
|
||||
{
|
||||
#ifndef SERVER_ONLY
|
||||
delete m_download_request;
|
||||
m_download_request = NULL;
|
||||
bool error = !addons_manager->uninstall(m_addon);
|
||||
@ -418,4 +432,5 @@ void AddonsLoading::doUninstall()
|
||||
AddonsScreen::getInstance()->loadList();
|
||||
dismiss();
|
||||
}
|
||||
#endif
|
||||
} // doUninstall
|
||||
|
@ -41,7 +41,9 @@ private:
|
||||
GUIEngine::IconButtonWidget *m_icon;
|
||||
|
||||
/** The addon to load. */
|
||||
#ifndef SERVER_ONLY
|
||||
Addon m_addon;
|
||||
#endif
|
||||
void startDownload();
|
||||
void stopDownload();
|
||||
void doInstall();
|
||||
|
@ -93,6 +93,7 @@ bool VoteDialog::onEscapePressed()
|
||||
*/
|
||||
void VoteDialog::sendVote()
|
||||
{
|
||||
#ifndef SERVER_ONLY
|
||||
/** A vote request. The callback will update the addon manager with the
|
||||
* new average. The VoteDialog polls this request till it is finished
|
||||
* to inform the user about the new average.
|
||||
@ -125,7 +126,7 @@ void VoteDialog::sendVote()
|
||||
|
||||
m_rating_widget->setActive(false);
|
||||
m_cancel_widget->setActive(false);
|
||||
|
||||
#endif
|
||||
} // sendVote
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -165,10 +165,6 @@ FeatureUnlockedCutScene::FeatureUnlockedCutScene()
|
||||
: CutsceneScreen("feature_unlocked.stkgui")
|
||||
{
|
||||
m_key_angle = 0;
|
||||
|
||||
#ifdef USE_IRRLICHT_BUG_WORKAROUND
|
||||
m_avoid_irrlicht_bug = NULL;
|
||||
#endif
|
||||
} // FeatureUnlockedCutScene
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -181,12 +177,6 @@ void FeatureUnlockedCutScene::loadedFromFile()
|
||||
|
||||
void FeatureUnlockedCutScene::onCutsceneEnd()
|
||||
{
|
||||
#ifdef USE_IRRLICHT_BUG_WORKAROUND
|
||||
if (m_avoid_irrlicht_bug)
|
||||
irr_driver->removeNode(m_avoid_irrlicht_bug);
|
||||
m_avoid_irrlicht_bug = NULL;
|
||||
#endif
|
||||
|
||||
m_unlocked_stuff.clearAndDeleteAll();
|
||||
#ifndef SERVER_ONLY
|
||||
if (CVS->isGLSL())
|
||||
@ -205,8 +195,10 @@ void FeatureUnlockedCutScene::onCutsceneEnd()
|
||||
void FeatureUnlockedCutScene::findWhatWasUnlocked(RaceManager::Difficulty difficulty,std::vector<const ChallengeData*>& unlocked)
|
||||
{
|
||||
PlayerProfile *player = PlayerManager::getCurrentPlayer();
|
||||
int points_before = player->getPoints();
|
||||
int points_now = points_before + CHALLENGE_POINTS[difficulty];
|
||||
|
||||
// The number of points is updated before this function is called
|
||||
int points_before = player->getPointsBefore();
|
||||
int points_now = player->getPoints();
|
||||
|
||||
std::vector<std::string> tracks;
|
||||
std::vector<std::string> gps;
|
||||
@ -362,18 +354,6 @@ void FeatureUnlockedCutScene::init()
|
||||
|
||||
#ifdef DEBUG
|
||||
m_unlocked_stuff[n].m_root_gift_node->setName("unlocked kart");
|
||||
#endif
|
||||
#ifdef USE_IRRLICHT_BUG_WORKAROUND
|
||||
// If a mesh with this material is added, irrlicht will
|
||||
// display the 'continue' text (otherwise the text is
|
||||
// not visible). This is a terrible work around, but allows
|
||||
// stk to be released without waiting for the next
|
||||
// irrlicht version.
|
||||
video::SMaterial m;
|
||||
m.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
|
||||
scene::IMesh* mesh =
|
||||
irr_driver->createTexturedQuadMesh(&m, 0, 0);
|
||||
m_avoid_irrlicht_bug = irr_driver->addMesh(mesh);
|
||||
#endif
|
||||
}
|
||||
#ifndef SERVER_ONLY
|
||||
@ -475,6 +455,7 @@ void FeatureUnlockedCutScene::onUpdate(float dt)
|
||||
m_global_time += dt;
|
||||
const int unlockedStuffCount = m_unlocked_stuff.size();
|
||||
|
||||
// When the chest has opened but the items are not yet at their final position
|
||||
if (m_global_time > GIFT_EXIT_FROM && m_global_time < GIFT_EXIT_TO)
|
||||
{
|
||||
float progress_factor = (m_global_time - GIFT_EXIT_FROM) / (GIFT_EXIT_TO - GIFT_EXIT_FROM);
|
||||
@ -489,30 +470,21 @@ void FeatureUnlockedCutScene::onUpdate(float dt)
|
||||
|
||||
// when there are more than 1 unlocked items, make sure they each
|
||||
// have their own path when they move
|
||||
if (unlockedStuffCount > 1)
|
||||
{
|
||||
if (n == 1) pos.X -= 1.0f*dt*float( int((n + 1)/2) );
|
||||
else if (n > 1) pos.X += 1.0f*dt*(n - 0.3f);
|
||||
// and that they won't end offscreen in usual situations
|
||||
|
||||
//else pos.X += 6.2f*dt*float( int((n + 1)/2) );
|
||||
//Log::info("FeatureUnlockedCutScene", "Object %d moving by %f", n,
|
||||
// (n % 2 == 0 ? -4.0f : 4.0f)*float( n/2 + 1 ));
|
||||
}
|
||||
else
|
||||
{
|
||||
//pos.X -= 2.0f*dt;
|
||||
}
|
||||
|
||||
//if (m_global_time > GIFT_EXIT_FROM + 2.0f) pos.Z -= 2.0f*dt;
|
||||
// Put the trophy in center
|
||||
float pos_value = (n == 0) ? unlockedStuffCount/2 :
|
||||
(n == unlockedStuffCount/2) ? 0 : n;
|
||||
float offset = (float) pos_value - ((float) unlockedStuffCount)/2.0f + 0.5f;
|
||||
offset *= (unlockedStuffCount <= 3) ? 1.4f :
|
||||
(unlockedStuffCount <= 5) ? 1.2f : 1.0f;
|
||||
pos.X += offset*dt;
|
||||
|
||||
pos.Z = smoothed_progress_factor * -4.0f;
|
||||
|
||||
m_unlocked_stuff[n].m_root_gift_node->setPosition(pos);
|
||||
}
|
||||
}
|
||||
else if (m_global_time < GIFT_EXIT_FROM)
|
||||
{
|
||||
}
|
||||
|
||||
for (int n=0; n<unlockedStuffCount; n++)
|
||||
{
|
||||
|
@ -116,10 +116,6 @@ class FeatureUnlockedCutScene : public GUIEngine::CutsceneScreen, public GUIEngi
|
||||
/** Angle of the key (from 0 to 1, simply traces progression) */
|
||||
float m_key_angle;
|
||||
|
||||
#ifdef USE_IRRLICHT_BUG_WORKAROUND
|
||||
scene::IMeshSceneNode *m_avoid_irrlicht_bug;
|
||||
#endif
|
||||
|
||||
void continueButtonPressed();
|
||||
|
||||
public:
|
||||
|
@ -140,6 +140,7 @@ void MainMenuScreen::init()
|
||||
// the key bindings for the first player the default again.
|
||||
input_manager->getDeviceManager()->clearLatestUsedDevice();
|
||||
|
||||
#ifndef SERVER_ONLY
|
||||
if (addons_manager->isLoading())
|
||||
{
|
||||
IconButtonWidget* w = getWidget<IconButtonWidget>("addons");
|
||||
@ -152,6 +153,7 @@ void MainMenuScreen::init()
|
||||
const core::stringw &news_text = NewsManager::get()->getNextNewsMessage();
|
||||
w->setText(news_text, true);
|
||||
w->update(0.01f);
|
||||
#endif
|
||||
|
||||
RibbonWidget* r = getWidget<RibbonWidget>("menu_bottomrow");
|
||||
// FIXME: why do I need to do this manually
|
||||
@ -174,6 +176,7 @@ void MainMenuScreen::init()
|
||||
|
||||
void MainMenuScreen::onUpdate(float delta)
|
||||
{
|
||||
#ifndef SERVER_ONLY
|
||||
PlayerProfile *player = PlayerManager::getCurrentPlayer();
|
||||
if(PlayerManager::getCurrentOnlineState() == PlayerProfile::OS_GUEST ||
|
||||
PlayerManager::getCurrentOnlineState() == PlayerProfile::OS_SIGNED_IN)
|
||||
@ -222,6 +225,7 @@ void MainMenuScreen::onUpdate(float delta)
|
||||
const core::stringw &news_text = NewsManager::get()->getNextNewsMessage();
|
||||
w->setText(news_text, true);
|
||||
}
|
||||
#endif
|
||||
} // onUpdate
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -229,6 +233,7 @@ void MainMenuScreen::onUpdate(float delta)
|
||||
void MainMenuScreen::eventCallback(Widget* widget, const std::string& name,
|
||||
const int playerID)
|
||||
{
|
||||
#ifndef SERVER_ONLY
|
||||
if(name=="user-id")
|
||||
{
|
||||
UserScreen::getInstance()->push();
|
||||
@ -534,6 +539,7 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name,
|
||||
{
|
||||
OnlineProfileAchievements::getInstance()->push();
|
||||
}
|
||||
#endif
|
||||
} // eventCallback
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -546,6 +552,7 @@ void MainMenuScreen::tearDown()
|
||||
|
||||
void MainMenuScreen::onDisabledItemClicked(const std::string& item)
|
||||
{
|
||||
#ifndef SERVER_ONLY
|
||||
if (item == "addons")
|
||||
{
|
||||
if (UserConfigParams::m_internet_status != RequestManager::IPERM_ALLOWED)
|
||||
@ -565,4 +572,5 @@ void MainMenuScreen::onDisabledItemClicked(const std::string& item)
|
||||
new MessageDialog( _("Please wait while the add-ons are loading"));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} // onDisabledItemClicked
|
||||
|
@ -239,6 +239,7 @@ void OptionsScreenUI::init()
|
||||
|
||||
void OptionsScreenUI::eventCallback(Widget* widget, const std::string& name, const int playerID)
|
||||
{
|
||||
#ifndef SERVER_ONLY
|
||||
if (name == "options_choice")
|
||||
{
|
||||
std::string selection = ((RibbonWidget*)widget)->getSelectionIDString(PLAYER_ID_GAME_MASTER);
|
||||
@ -387,7 +388,7 @@ void OptionsScreenUI::eventCallback(Widget* widget, const std::string& name, con
|
||||
|
||||
OptionsScreenUI::getInstance()->push();
|
||||
}
|
||||
|
||||
#endif
|
||||
} // eventCallback
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -107,6 +107,15 @@ void RegisterScreen::init()
|
||||
makeEntryFieldsVisible();
|
||||
|
||||
local_username->setFocusForPlayer(PLAYER_ID_GAME_MASTER);
|
||||
|
||||
// The behaviour of the screen is slightly different at startup, i.e.
|
||||
// when it is the first screen: cancel will exit the game, and in
|
||||
// this case no 'back' error should be shown.
|
||||
bool has_player_profile = (PlayerManager::get()->getNumPlayers() > 0);
|
||||
getWidget<IconButtonWidget>("back")->setVisible(has_player_profile);
|
||||
getWidget<IconButtonWidget>("cancel")->setLabel(has_player_profile
|
||||
? _("Cancel")
|
||||
: _("Exit game"));
|
||||
} // init
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@ -236,6 +245,13 @@ void RegisterScreen::doRegister()
|
||||
{
|
||||
stringw local_name = getWidget<TextBoxWidget>("local_username")
|
||||
->getText().trim();
|
||||
|
||||
if (local_name.empty())
|
||||
{
|
||||
m_info_widget->setErrorColor();
|
||||
m_info_widget->setText(_("User name cannot be empty."), false);
|
||||
return;
|
||||
}
|
||||
|
||||
handleLocalName(local_name);
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
#include "states_screens/user_screen.hpp"
|
||||
|
||||
#include "addons/news_manager.hpp"
|
||||
#include "audio/sfx_manager.hpp"
|
||||
#include "challenges/unlock_manager.hpp"
|
||||
#include "config/player_manager.hpp"
|
||||
@ -26,6 +27,7 @@
|
||||
#include "guiengine/widgets/label_widget.hpp"
|
||||
#include "guiengine/widgets/list_widget.hpp"
|
||||
#include "guiengine/widgets/text_box_widget.hpp"
|
||||
#include "online/request_manager.hpp"
|
||||
#include "states_screens/dialogs/message_dialog.hpp"
|
||||
#include "states_screens/dialogs/kart_color_slider_dialog.hpp"
|
||||
#include "states_screens/dialogs/recovery_dialog.hpp"
|
||||
@ -326,18 +328,49 @@ void BaseUserScreen::eventCallback(Widget* widget,
|
||||
}
|
||||
else if (name == "online")
|
||||
{
|
||||
// If online access is not allowed, do not accept an online account
|
||||
// but advice the user where to enable this option.
|
||||
// If online access is not allowed,
|
||||
// give the player the choice to enable this option.
|
||||
if (m_online_cb->getState())
|
||||
{
|
||||
if (UserConfigParams::m_internet_status ==
|
||||
Online::RequestManager::IPERM_NOT_ALLOWED)
|
||||
{
|
||||
m_info_widget->setText(
|
||||
_("Internet access is disabled, please enable it in the options"),
|
||||
true);
|
||||
irr::core::stringw message =
|
||||
_("Internet access is disabled. Do you want to enable it ?");
|
||||
|
||||
class ConfirmInternet : public MessageDialog::IConfirmDialogListener
|
||||
{
|
||||
BaseUserScreen *m_parent_screen;
|
||||
private:
|
||||
GUIEngine::CheckBoxWidget *m_cb;
|
||||
public:
|
||||
virtual void onConfirm()
|
||||
{
|
||||
UserConfigParams::m_internet_status =
|
||||
Online::RequestManager::IPERM_ALLOWED;
|
||||
#ifndef SERVER_ONLY
|
||||
NewsManager::get()->init(false);
|
||||
#endif
|
||||
m_parent_screen->makeEntryFieldsVisible();
|
||||
ModalDialog::dismiss();
|
||||
} // onConfirm
|
||||
virtual void onCancel()
|
||||
{
|
||||
m_cb->setState(false);
|
||||
m_parent_screen->makeEntryFieldsVisible();
|
||||
ModalDialog::dismiss();
|
||||
} // onCancel
|
||||
// ------------------------------------------------------------
|
||||
ConfirmInternet(BaseUserScreen *parent, GUIEngine::CheckBoxWidget *online_cb)
|
||||
{
|
||||
m_parent_screen = parent;
|
||||
m_cb = online_cb;
|
||||
}
|
||||
}; // ConfirmInternet
|
||||
|
||||
SFXManager::get()->quickSound( "anvil" );
|
||||
m_online_cb->setState(false);
|
||||
new MessageDialog(message, MessageDialog::MESSAGE_DIALOG_CONFIRM,
|
||||
new ConfirmInternet(this, m_online_cb), true);
|
||||
}
|
||||
}
|
||||
makeEntryFieldsVisible();
|
||||
|
@ -32,12 +32,28 @@ std::string CommandLine::m_exec_name="";
|
||||
void CommandLine::init(unsigned int argc, char *argv[])
|
||||
{
|
||||
if (argc > 0)
|
||||
{
|
||||
m_exec_name = argv[0];
|
||||
}
|
||||
|
||||
for(unsigned int i=1; i<argc; i++)
|
||||
for (unsigned int i = 1; i < argc; i++)
|
||||
{
|
||||
m_argv.push_back(argv[i]);
|
||||
}
|
||||
} // CommandLine
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void CommandLine::addArgsFromUserConfig()
|
||||
{
|
||||
std::vector<std::string> config_args;
|
||||
config_args = StringUtils::split(UserConfigParams::m_commandline, ' ');
|
||||
|
||||
for (std::string config_arg : config_args)
|
||||
{
|
||||
m_argv.push_back(config_arg);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
bool CommandLine::has(const std::string &option)
|
||||
{
|
||||
|
@ -79,6 +79,7 @@ private:
|
||||
|
||||
public:
|
||||
static void init(unsigned int argc, char *argv[]);
|
||||
static void addArgsFromUserConfig();
|
||||
static void reportInvalidParameters();
|
||||
static bool has(const std::string &option);
|
||||
// ------------------------------------------------------------------------
|
||||
|