diff --git a/.gitignore b/.gitignore index ff48685..aeda464 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,12 @@ bin # Ignore MacOS **/.DS_Store .idea/ -run/ + +# Ignore the dedicated working directory +run/* + +# ... Except the dummy file inside to make sure that the directory exists +!run/.dummy # Ignore package building artifacts build_packages/* diff --git a/README.md b/README.md index b6b96f1..4c6cbc5 100644 --- a/README.md +++ b/README.md @@ -1,46 +1,58 @@ # Progressia -A free, open source sandbox survival game currently in early development. +A free, open-source sandbox survival game currently in early development. + +## Description + +The game has barely begun development so much of its features are yet to be implemented. + +In broader terms, Progressia is a challenging game about survival, exploration and +engineering in a realistic voxel sandbox environment. The game is heavily inspired by +Minecraft technology mods, Factorio, Vintage Story and Minetest. Progressia's main unique +features will include highly composite items and blocks, a realistically-scaled world, +temperature mechanics and a parallelism-capable server. + +## System requirements + +- GNU/Linux (x64, arm32 or arm64), Windows XP or later (x64 or x86) or MacOS (x64) +- Java 8 or later +- OpenGL 2.1 or later +- Probably at least 4 GiB RAM +- Less than 1 GiB of storage space + +See [Build Guide](docs/building/BuildGuide.md) for compilation requirements. ## Contributing -For now, contact @Javapony in Telegram for details. Contributing is completely allowed, but we don't have any set guidelines yet. +All contributors welcome. Please contact Javapony in [Telegram](https://t.me/javapony) +or join our [Discord server](https://discord.gg/M4ukyPYgGP) for details or help. ## Building +On GNU/Linux and MacOS: + 1. `$ git clone https://github.com/OLEGSHA/Progressia.git` -2. `$ gradlew build` +2. `$ chmod +x gradlew` +3. `$ ./gradlew buildLocal` -### Additional setup for Eclipse IDE +On Windows: -If you have Buildship plugin installed, use File - Import - Gradle - Existing Gradle Project. Main class is `ru.windcorp.progressia.client.ProgressiaClientMain`. +1. `git clone https://github.com/OLEGSHA/Progressia.git` +2. `gradlew.bat buildLocal` -Alternatively do the following: +Alternatively use Linux/MacOS steps in a Bash shell. -1. Add `id 'eclipse'` into `build.gradle` inside `plugins { ... }`: -``` -plugins { - // Apply the java-library plugin to add support for Java Library - id 'java-library' - id 'eclipse' -} -``` -2. `$ gradlew eclipse` -3. Import the project with File - Import - Existing Projects into Workspace -4. On Windows, make sure the project has UTF-8 encoding with - Properties - Resource - Text file encoding - Other - UTF-8 - -### Additional setup for IntelliJ IDEA - -1. Open the project with File - Open Project -2. Press button 'Add configuration...' and open 'Application' in templates list -3. Add `ru.windcorp.progressia.client.ProgressiaClientMain` in 'Main class' text field -4. Choose `Progressia.main` in 'Use classpath of module' drop-down list -5. Click 'Create configuration' and press 'Apply' +For a more in-depth explanation, solutions for common problems and tips for IDE configuration +please see the [Build Guide](docs/building/BuildGuide.md). ## Libraries -* LWJGL - OpenGL, OpenAL, GLFW and several more libraries ported to Java -* Google Guava -* Trove4j -* java-graphics/glm - GLM ported to Java. _Maven Central contains an outdated version, a custom repository used instead_ -* OpenSimplex2 -* log4j +- [LWJGL](https://www.lwjgl.org/) ([GitHub](https://github.com/LWJGL/lwjgl3)) – OpenGL, OpenAL, GLFW and STB libraries ported to Java + - [OpenGL](https://en.wikipedia.org/wiki/OpenGL) – a low-level graphics interface + - [OpenAL](https://en.wikipedia.org/wiki/OpenAL) – a low-level audio interface + - [GLFW](https://www.glfw.org/) ([GitHub](https://github.com/glfw/glfw)) – a minimalistic OpenGL-capable windowing library + - [STB (GitHub)](https://github.com/nothings/stb) – a collection of various algorithms. `stb_vorbis` is used +- [Guava (GitHub)](https://github.com/google/guava) – a generic utilities library +- [Trove4j (BitBucket)](https://bitbucket.org/trove4j/trove) – optimized primitive collections +- [java-graphics/glm (GitHub)](https://github.com/java-graphics/glm) – GLM ported to Java. _Maven Central contains an outdated version, a custom repository used instead_ +- [OpenSimplex2 (GitHub)](https://github.com/KdotJPG/OpenSimplex2) – a minimalistic highly optimized noise generator +- [Log4j](https://logging.apache.org/log4j/2.x/) [(GitHub)](https://github.com/apache/logging-log4j2) – a logging library diff --git a/build.gradle b/build.gradle index d70458b..5adae10 100644 --- a/build.gradle +++ b/build.gradle @@ -1,31 +1,90 @@ +/* + * build.gradle for Progressia + */ + plugins { // Apply the java-library plugin to add support for Java Library id 'java-library' + + /* + * Uncomment the following line to enable the Eclipse plugin. + * This is only necessary if you don't use Buildship plugin from the IDE + */ + //id 'eclipse' } +java { + /* + * We're Java 8 for now. + * Why? As of 2020, most users have Oracle Java, which only supports Java 8. + */ + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +compileJava { + /* + * We want to compile for Java 8. + * If we are using JDK 8, no further action is required. + * However, on JDK 9 and later versions, '--release' option is required, + * which is missing on JDK 8. + */ + if (JavaVersion.current() != JavaVersion.VERSION_1_8) { + options.compilerArgs.addAll(['--release', '8']) + } +} + +/* + * Dependencies + */ + repositories { mavenCentral() jcenter() - maven { url 'http://windcorp.ru/./maven' } + /* + * Specify Windcorp Maven repository + * Currently used by: + * - ru.windcorp.fork.io.github.java-graphics:glm:1.0.1 + */ + maven { url 'https://windcorp.ru/./maven' } } dependencies { - implementation 'org.apache.commons:commons-math3:3.6.1' + // Google Guava + // A generic utilities library implementation 'com.google.guava:guava:30.0-jre' + + // Trove4j + // Provides optimized Collections for primitive types implementation 'net.sf.trove4j:trove4j:3.0.3' + // java-graphics + // A GLM (OpenGL Mathematics) port to Java + // Unfortunately, Maven Central Repository provides an outdated version of this library, which contains several critical bugs implementation 'ru.windcorp.fork.io.github.java-graphics:glm:1.0.1' - // log4j + // Log4j + // A logging library implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.13.3' implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.13.3' + // JUnit + // A unit-testing library testImplementation 'junit:junit:4.12' - // See also LWJGL dependencies below + // See LWJGL dependencies below } +/* + * Progressia uses the following LWJGL libraries: + * - Core libraries + * - OpenGL + * - OpenAL + * - GLFW + * - STB + */ + /* * LWJGL * (auto-generated script) @@ -55,94 +114,202 @@ dependencies { implementation platform("org.lwjgl:lwjgl-bom:$lwjglVersion") implementation "org.lwjgl:lwjgl" - implementation "org.lwjgl:lwjgl-assimp" - implementation "org.lwjgl:lwjgl-bgfx" implementation "org.lwjgl:lwjgl-glfw" - implementation "org.lwjgl:lwjgl-nanovg" - implementation "org.lwjgl:lwjgl-nuklear" implementation "org.lwjgl:lwjgl-openal" implementation "org.lwjgl:lwjgl-opengl" - implementation "org.lwjgl:lwjgl-par" implementation "org.lwjgl:lwjgl-stb" - implementation "org.lwjgl:lwjgl-vulkan" + runtimeOnly "org.lwjgl:lwjgl::$lwjglNatives" - runtimeOnly "org.lwjgl:lwjgl-assimp::$lwjglNatives" - runtimeOnly "org.lwjgl:lwjgl-bgfx::$lwjglNatives" - runtimeOnly "org.lwjgl:lwjgl-glfw::$lwjglNatives" - runtimeOnly "org.lwjgl:lwjgl-nanovg::$lwjglNatives" - runtimeOnly "org.lwjgl:lwjgl-nuklear::$lwjglNatives" - runtimeOnly "org.lwjgl:lwjgl-openal::$lwjglNatives" - runtimeOnly "org.lwjgl:lwjgl-opengl::$lwjglNatives" - runtimeOnly "org.lwjgl:lwjgl-par::$lwjglNatives" - runtimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives" - if (lwjglNatives == "natives-macos") runtimeOnly "org.lwjgl:lwjgl-vulkan::$lwjglNatives" + runtimeOnly "org.lwjgl:lwjgl-glfw::$lwjglNatives" + runtimeOnly "org.lwjgl:lwjgl-openal::$lwjglNatives" + runtimeOnly "org.lwjgl:lwjgl-opengl::$lwjglNatives" + runtimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives" } // LWJGL END -configurations { - packageOnly - packageLibraries.extendsFrom runtimeClasspath - packageLibraries.extendsFrom packageOnly +/* + * Tasks + */ + +/* + * Additional native libraries specification + */ + +project.ext.platforms = new HashSet<>() + +task addNativeDependencies { + doFirst { + def archs = project.ext.platforms + + switch (archs.size()) { + case 0: + println "Adding LWJGL native dependencies for local platform only:\n\t$lwjglNatives" + archs.add project.ext.lwjglNatives + break + case 1: + println "Adding LWJGL native dependencies for platform\n\t" + archs.get(0) + break + default: + println "Adding LWJGL native dependencies for platforms:\n\t" + archs.join("\n\t") + } + + if (project.ext.lwjglNatives.isEmpty()) println "WTF" + + dependencies { + archs.each { arch -> + runtimeOnly "org.lwjgl:lwjgl::$arch" + runtimeOnly "org.lwjgl:lwjgl-glfw::$arch" + runtimeOnly "org.lwjgl:lwjgl-openal::$arch" + runtimeOnly "org.lwjgl:lwjgl-opengl::$arch" + runtimeOnly "org.lwjgl:lwjgl-stb::$arch" + } + } + } } -dependencies { - def archs = ['natives-linux', 'natives-linux-arm64', 'natives-linux-arm32', 'natives-macos', 'natives-windows', 'natives-windows-x86'] - - archs.each { arch -> - packageOnly "org.lwjgl:lwjgl::$arch" - packageOnly "org.lwjgl:lwjgl-assimp::$arch" - packageOnly "org.lwjgl:lwjgl-bgfx::$arch" - packageOnly "org.lwjgl:lwjgl-glfw::$arch" - packageOnly "org.lwjgl:lwjgl-nanovg::$arch" - packageOnly "org.lwjgl:lwjgl-nuklear::$arch" - packageOnly "org.lwjgl:lwjgl-openal::$arch" - packageOnly "org.lwjgl:lwjgl-opengl::$arch" - packageOnly "org.lwjgl:lwjgl-par::$arch" - packageOnly "org.lwjgl:lwjgl-stb::$arch" - } - - packageOnly "org.lwjgl:lwjgl-vulkan::natives-macos" +compileJava.mustRunAfter addNativeDependencies // Make sure runtimeOnly has not been resolved + +task requestLinuxDependencies { + description 'Adds linux, linux-arm64 and linux-arm32 native libraries to built artifacts.' + doFirst { + project.ext.platforms.addAll(['natives-linux', 'natives-linux-arm64', 'natives-linux-arm32']) + } +} +task requestWindowsDependencies { + description 'Adds windows and windows-x86 native libraries to built artifacts.' + doFirst { + project.ext.platforms.addAll(['natives-windows', 'natives-windows-x86']) + } +} +task requestMacOSDependencies { + description 'Adds macos native libraries to built artifacts.' + doFirst { + project.ext.platforms.addAll(['natives-macos']) + } } -jar { - manifest { - attributes( - "Main-Class": "ru.windcorp.progressia.client.ProgressiaClientMain", - "Class-Path": configurations.packageLibraries.collect { "lib/" + it.getName() }.join(' ') - ) - } +def dependencySpecificationTasks = tasks.findAll { task -> task.name.startsWith('request') && task.name.endsWith('Dependencies') } + +task requestCrossPlatformDependencies { + description 'Adds native libraries for all available platforms to built artifacts.' + dependsOn dependencySpecificationTasks +} + +addNativeDependencies.mustRunAfter dependencySpecificationTasks + +/* + * Determines if the provided dependency should be packaged + */ +def isDependencyRequested(String dep) { + if (dep.endsWith(".jar")) { + dep = dep.substring(0, dep.length() - ".jar".length()) + } + + return !dep.contains("natives-") || + project.ext.platforms.contains(dep.substring(dep.indexOf("natives-"), dep.length())) } /* - * Copies runtime dependencies to a prespecified location so they can be packaged properly. + * Manifest specification */ -task copyLibs(type: Copy) { - into "${libsDir}/lib" + +task specifyLocalManifest { + dependsOn addNativeDependencies // Make sure all native dependencies are specified + + doFirst { + def classPath = [] + + configurations.runtimeClasspath.each { + if (isDependencyRequested(it.getName())) { + classPath.add("lib/" + it.getName()) + } else { + println "\tRemoving from JAR classpath (not requested): " + it.getName() + } + } + + if (classPath.size() == configurations.runtimeClasspath.size()) { + println "Nothing removed from JAR classpath" + } + + jar { + manifest { + attributes( + "Main-Class": "ru.windcorp.progressia.client.ProgressiaClientMain", + "Class-Path": configurations.runtimeClasspath.collect { "lib/" + it.getName() } .findAll { isDependencyRequested(it) } .join(' ') + ) + } + } + } +} + +jar.dependsOn specifyLocalManifest + +/* + * Library export + */ + +task exportLibs(type: Sync) { + mustRunAfter addNativeDependencies + + into libsDirectory.get().getAsFile().getPath() + "/lib" + exclude { !isDependencyRequested(it.getName()) } from configurations.runtimeClasspath } -build.dependsOn(copyLibs) +jar.dependsOn(exportLibs) -task copyLibsForPackaging(type: Copy) { - into "${libsDir}/lib" - from configurations.packageLibraries -} +/* + * Packaging + */ -task createPackages(type: Exec) { - commandLine './buildPackages.sh' -} - -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} - -compileJava { - if (JavaVersion.current() != JavaVersion.VERSION_1_8) { - options.compilerArgs.addAll(['--release', '8']) +task packageDebian(type: Exec) { + description 'Builds the project and creates a Debain package.' + group 'Progressia' + + dependsOn build + dependsOn requestLinuxDependencies + + commandLine './buildPackages.sh', 'debian' + + doLast { + println "Debian package available in build_packages/" } } -createPackages.dependsOn(build) -createPackages.dependsOn(copyLibsForPackaging) +task packageWindows(type: Exec) { + description 'Builds the project and creates a Windows installer.' + group 'Progressia' + + dependsOn build + dependsOn requestWindowsDependencies + + commandLine './buildPackages.sh', 'windows' + + doLast { + println "Windows installer available in build_packages/" + } +} + +task buildCrossPlatform { + description 'Builds the project including native libraries for all available platforms.' + group 'Progressia' + + dependsOn requestCrossPlatformDependencies + dependsOn build + + doLast { + println "Native libraries for all platforms have been added" + } +} + +task buildLocal { + description "Builds the project including only native libraries for current platform ($lwjglNatives)." + group 'Progressia' + + dependsOn build + + doLast { + println "Native libraries only for platform $lwjglNatives have been added" + } +} diff --git a/buildPackages.sh b/buildPackages.sh index 5c7c898..7a0f0dd 100755 --- a/buildPackages.sh +++ b/buildPackages.sh @@ -1,38 +1,161 @@ #!/bin/bash -lst="nsis" +echoerr() { echo "$@" 1>&2; } -user=`whoami` +buildDebianPackage() { -dpkg -l 2>/dev/null > ls.tmp + # Commands that must be available to execute this action + requiredCommands='dpkg-deb fakeroot' + + # Version that the package will receive + version='0.1_all' + + directory="build_packages/DEB/progressia-$version" + + # .deb control file that must be present + configurationFile="$directory/DEBIAN/control" + + outputFile="build_packages/DEB/progressia-$version.deb" -for items in $lst -do - cmd=$(grep "\ $items\ " ls.tmp) - if [ $? == 0 ] - then - echo "$items installed!" - echo "Building..." + echo "Checking environment to build Debian package" + + for item in $requiredCommands; do + if command -v "$item" &> /dev/null; then + echo "- $item found" + else + echoerr "Command $item not found, cannot package" + exit 100 + fi + done + + if ! [ -r "$configurationFile" ]; then + echoerr "$configurationFile is missing or not readable, cannot package" + exit 101 else - echo "Package $items not found! Please install $items." - rm ls.tmp - exit 1 - fi -done -rm ls.tmp + echo "- $configurationFile is present and readable" + fi + + echo "Environment OK; packaging Debian package" + exitCode=0 + + { + user=`whoami` + homeDir="$directory/home/$user/Progressia/" + + mkdir -p "$homeDir" && + cp -r 'build/libs/lib' "$homeDir/lib" && + cp 'build/libs/Progressia.jar' "$homeDir/Progressia.jar" && + echo "------ DPKG-DEB ------" && + fakeroot dpkg-deb --build "$directory" && + echo "---- DPKG-DEB END ----" && + mv "$outputFile" build_packages + } || { + echoerr "Could not create Debian package" + exitCode=1 + } + + { + if [ -d "$homeDir" ]; then + rm -r "$homeDir" + fi + echo "Cleaned up" + } || { + echoerr "Could not clean up after packaging Debian package" + exitCode=2 + } + + exit "$exitCode" +} -cd build_packages/DEB/progressia-0.1_all/ -mkdir -p home/$user/Progressia +buildWindowsInstaller() { -cd ../../.. + # Commands that must be available to execute this action + requiredCommands='makensis' + + # NSIS configuration file that must be present + configurationFile='build_packages/NSIS/ProgressiaInstaller.nsi' + + # File that will be output + outputFile='build_packages/NSIS/ProgressiaInstaller.exe' -cp -r build/libs/lib build_packages/DEB/progressia-0.1_all/home/$user/Progressia/ -cp build/libs/Progressia.jar build_packages/DEB/progressia-0.1_all/home/$user/Progressia/ -cp -r build/libs/lib build_packages/NSIS -cp build/libs/Progressia.jar build_packages/NSIS + echo "Checking environment to build Windows installer" + + for item in $requiredCommands; do + if command -v "$item" &> /dev/null; then + echo "- $item found" + else + echoerr "Command $item not found, cannot build" + exit 100 + fi + done + + if ! [ -r "$configurationFile" ]; then + echoerr "$configurationFile is missing or not readable, cannot build" + exit 101 + else + echo "- $configurationFile is present and readable" + fi + + echo "Environment OK; building Windows installer" + exitCode=0 + + { + cp -r 'build/libs/lib' 'build_packages/NSIS/lib' && + cp 'build/libs/Progressia.jar' 'build_packages/NSIS/Progressia.jar' && + echo "------ NSIS ------" && + makensis "$configurationFile" && + echo "---- NSIS END ----" && + mv "$outputFile" build_packages + } || { + echoerr "Could not build Windows installer" + exitCode=1 + } + + { + if [ -d 'build_packages/NSIS/lib' ]; then + rm -r 'build_packages/NSIS/lib' + fi + if [ -e 'build_packages/NSIS/Progressia.jar' ]; then + rm 'build_packages/NSIS/Progressia.jar' + fi + echo "Cleaned up" + } || { + echoerr "Could not clean up after building Windows installer" + exitCode=2 + } + + exit "$exitCode" +} -makensis build_packages/NSIS/ProgressiaInstaller.nsi -mv build_packages/NSIS/ProgressiaInstaller.exe build_packages/Progressia.exe -fakeroot dpkg-deb --build build_packages/DEB/progressia-0.1_all -mv build_packages/DEB/progressia-0.1_all.deb build_packages/progressia-0.1_all.deb -echo "Build done!" +printUsage() { + echoerr "Usage: $0 TARGET" + echoerr " where TARGET is 'debian' or 'windows'" +} + +if [ -n "$2" ]; then + echoerr "Too many arguments." + printUsage + exit 202 +fi + +case "$1" in +"debian") + buildDebianPackage + ;; +"windows") + buildWindowsInstaller + ;; +"") + echoerr "No action specified" + printUsage + exit 200 + ;; +"--help" | "-help" | "help" | "?") + printUsage + ;; +*) + echoerr "Unknown action '$1'" + printUsage + exit 201 + ;; +esac diff --git a/docs/building/BuildGuide.md b/docs/building/BuildGuide.md new file mode 100644 index 0000000..23833e9 --- /dev/null +++ b/docs/building/BuildGuide.md @@ -0,0 +1,203 @@ +# Build Guide + +This document is a guide to building Progressia from source. + +Compilation should be possible on all platforms that support JDK 8 or later, however, packaging scripts require Bash. + +This guide assumes you are familiar with using a terminal or Windows Command Prompt or PowerShell. + +See [Eclipse Guide](EclipseGuide.md) and [IntelliJ IDEA Guide](IntelliJIDEAGuide.md) for IDE-specific configuration. + +## Basic compilation + +### Installing prerequisites + +Compiling Progressia requires that a JDK (Java Development Kit) 8 or later is available in your system path. Please +note that JDK is not the same as a JRE (Java Runtime Environment), the software required to execute Java programs; +you may be able to run Progressia but not compile it. + +To check whether you have the correct JDK installed please run + `javac -version`. +If this command fails or outputs version 1.7 or lower, then you need to (re-)install JDK. (javac is the Java +Compiler.) + +Progressia developers recommend [OpenJDK](https://openjdk.java.net/) as the free, open-source Java implementation. +Confusion may arise from the project's name: OpenJDK issues both JRE and JDK; an OpenJDK JRE is not enough to compile +Progressia. Another popular choice is [Oracle Java](https://www.oracle.com/java/technologies/), which is proprietary +and thus not recommended. + +To install OpenJDK JDK on GNU/Linux, install the appropriately named package with your package manager (on Debian, +Ubuntu and other dpkg-based distributions, run + `apt-get install default-jdk` +with root privileges). + +To install OpenJDK JDK on MacOS or Windows, Progressia developers recommend [AdoptOpenJDK](https://adoptopenjdk.net/), +which creates and distributes OpenJDK installers and packages. Please confirm that JDK installed correctly using +the aforementioned test. + +### Getting the source code + +Progressia source code is managed with [git](https://git-scm.com/), a popular open-source tool. It is a program that +automatically keeps track of changes in a set of source code and allows synchronization of those changes between +different computers. A copy of the source code is called a repository. This guide will assume that +[Progressia's GitHub repository](https://github.com/OLEGSHA/Progressia/) is accessible; if it is not, please look up +the relevant instructions. + +If git is installed on your system, perform a clone of + `https://github.com/OLEGSHA/Progressia.git`. +The details may vary depending on your git frontend; if you are not using a frontend, issue the following command: + +``` +git clone https://github.com/OLEGSHA/Progressia.git +``` + +Run the command in your directory of choice, git will create a subfolder. After the action completes you should +have a local copy of the source code. + +If you do not have git installed, use GitHub's 'Download ZIP' option (available through the green 'Code' button at +the time of writing this). Unpack the downloaded archive into any directory. + +### Compiling + +Compilation and related tasks are managed by [Gradle](https://gradle.org/), another popular open-source tool. The repository +contains a Gradle Wrapper which automatically detects and installs the appropriate Gradle version. Gradle is most +conveniently controlled with one of two scripts, `gradlew` or `gradlew.bat` for Bash and Windows Command Prompt respectively, +which are distributed within the repository as well. Gradle itself uses another file, `build.gradle`, that contains the +relevant instructions in Groovy. + +On GNU/Linux and MacOS, the script may need to receive the execution permission before being run: + +``` +chmod +x gradlew +``` + +A most basic compilation is achieved with: + +``` +./gradlew buildLocal +``` + +This instructs Gradle to compile, check and test a JAR archive with the game, as well as to download all necessary +dependencies. The resulting JAR file will only reference libraries required to run the game on your current platform. + +The compiled JAR and all necessary libraries can be found in `build/libs/`. The `lib` directory next to `Progressia.jar` +contains all runtime dependencies; the game may be run with a simple + `java -jar Progressia.jar` +if `lib` is located in the same directory as the JAR file. (On many systems double-clicking `Progressia.jar` has the +same effect.) Otherwise, if there is no `lib` directory next to the JAR, classpath must be set manually with command +line options for `java`. + +A more conventional command, `./gradlew build`, may be used to the same effect; however, Progressia developers recommend +against using it as their effects might start to differ with future updates. + +## Building for other platforms + +### Native libraries and why platforms matter + +Although Java is well-known for its portability, Progressia has a limited set of architectures that it supports. +This is a consequence of the fact that Progressia uses [OpenGL](https://en.wikipedia.org/wiki/OpenGL), +[OpenAL](https://en.wikipedia.org/wiki/OpenAL), [GLFW](https://www.glfw.org) and [STB](https://github.com/nothings/stb), +four libraries originally implemented in the C language. In order to access them, a Java wrapper library, +[LWJGL](https://www.lwjgl.org/), is used. Unfortunately such wrappers need to be hand-written for each platform +and require complicated compilation. + +In practice this means that along with the pure Java libraries, such as Guava or GLM, Progressia carries several +LWJGL libraries that each consist of a Java interface and a compiled binary implementation (called a native library). +A separate version of a single native library is required to run Progressia on different platforms; if a game only +contains natives for Windows, it cannot be run on GNU/Linux. + +### How Gradle is set up + +Gradle builds Progressia by first choosing which platform or platforms should be supported. It then downloads, +if necessary, and processes only dependencies that are relevant to the current configuration. When creating the JAR +file, Gradle appends only selected libraries to the JAR's manifest file. It finally copies only the libraries included +in the manifest to the `build/libs/lib` directory. + +Why not package all natives all the time? This is inefficient when producing temporary builds for development purposes +because unnecessary dependencies need to be downloaded and processed. This mechanism is also used to trim down the +size of various system-specific installers and packages since, for example, a Windows Installer is not expected to +produce a installation that runs on anything other than Windows, so GNU/Linux and MacOS natives need not be packaged +with it. + +Unless instructed otherwise, Gradle only chooses the local platform. Thus the procedure described in 'Basic Compilation' +produces an output that only contains the natives for the platform that performed the compilation. It is unsuitable for +publication and may be generally inconvenient. + +### Building cross-platform + +Fortunately it is possible to produce a maximally cross-platform version by running Gradle with a different task: + +``` +./gradlew buildCrossPlatform +``` + +This command performs the same actions as the `buildLocal` command, except that all possible native libraries are +included in the JAR manifest and copied into `build/libs/lib`. The result may be run on any supported platform. +However, because of the large amount of libraries, it has significantly greater size than a platform-specific +build. + +### Building for a specific platform + +Some users might find the need to build for a specific set of platforms. Inclusion of GNU/Linux, Windows or MacOS +dependencies individually can be controlled with the following arguments to the `./gradlew build` command: +- `requestLinuxDependencies` requests that `natives-linux`, `natives-linux-arm32` and `natives-linux-arm64` binaries are included; +- `requestWindowsDependencies` requests that `natives-windows` and `natives-windows-x86` binaries are included; +- `requestMacOSDependencies` requests that `natives-macos` binaries are included. +These requests can be applied in any combination. For example, the following command produces a build containing only +GNU/Linux and Windows natives: + +``` +./gradlew build requestLinuxDependencies requestWindowsDependencies +``` + +For finer control please edit `build.gradle` manually by adding the desired natives to the `project.ext.platforms` set like so: + +``` +project.ext.platforms = new HashSet<>() +project.ext.platforms.add 'natives-windows-x86' +``` + +## Packaging + +A Debian package and a Windows installer can be created automatically on systems that support Bash. These tasks are delegated +by Gradle to `buildPackages.sh` in repository root. This script checks the environment and assembles the requested output; the +resulting files are moved into `build_packages`. + +### Creating a Debian package + +A Debian package can be created with the following Gradle task: + +``` +./gradlew packageDebian +``` + +Gradle will then build all artifacts necessary to run the game on GNU/Linux (all three architectures) and invoke +`./buildPackages.sh debian`. Commands `dpkg-deb` and `fakeroot` must be available in system path in order to build the package. + +### Creating a Windows installer + +A Windows installer can be created with the following Gradle task: + +``` +./gradlew packageWindows +``` + +Gradle will then build all artifacts necessary to run the game on Windows (both x64 and x86 architectures) and invoke +`./buildPackages.sh windows`. + +Windows installers are implemented with [NSIS](https://nsis.sourceforge.io/). Command `makensis` must be available in system +path in order to build the installer. + +## Gradle tasks summary + +- `buildLocal` – creates a build optimized for current platform. Use this to quickly build the game during development. +- `buildCrossPlatform` – creates a build that supports all known architectures. Use this to build a universal version of the game. +- `build` – currently a synonym of `buildLocal`; creates a default build. +- `packageDebian` – creates a Debian package. Do not invoke together with `packageWindows`. +- `packageWindows` – creates a Windows installer. Do not invoke together with `packageDebian`. +- `requestLinuxDependencies` – requests that `natives-linux`, `natives-linux-arm32` and `natives-linux-arm64` binaries are included when building. +- `requestWindowsDependencies` – requests that `natives-windows` and `natives-windows-x86` binaries are included when building. +- `requestMacOSDependencies` – requests that `natives-macos` binaries are included when building. +- `requestCrossPlatformDependencies` – requests that all binaries are included when building. + +All other basic and Java-related Gradle tasks are available as well. \ No newline at end of file diff --git a/docs/building/EclipseGuide.md b/docs/building/EclipseGuide.md new file mode 100644 index 0000000..5d20427 --- /dev/null +++ b/docs/building/EclipseGuide.md @@ -0,0 +1,113 @@ +### Eclipse Guide + +This document is a guide to configuring Eclipse IDE for developing Progressia. + +## Setup + +### Downloading project + +Although the project may be downloaded manually or using git directly (as described in the +[Build Guide](BuildGuide.md)), it may be more convenient to use Eclipse's EGit. If you +choose not to, skip the following subsection. + +#### Using EGit + +[EGit](https://www.eclipse.org/egit/) is a git interface for Eclipse. It is currently shipped +with Eclipse IDE. + +1. Open Git perspective. (Git perspective is not visible by default. To open it, use +'Window' menu > 'Perspective' > 'Open Perspective' > 'Other' > select Git.) +2. In 'Git Repositories' view, click 'Clone a Git Repository and add the clone to this view' +button. +3. Paste +`https://github.com/OLEGSHA/Progressia.git` +or the appropriate git URI into the 'URI' field and click 'Next'. +4. Review the branches and click 'Next'. +5. Edit the local repository path if necessary and click 'Finish'. + +Note: avoid importing the project from the Git Clone Wizard as doing so will fail to specify +Gradle dependencies. + +### Importing project + +Gradle dependencies need to be imported into the IDE for proper code analysis and launching. + +#### Using Buildship plugin for Eclipse + +[Buildship](https://projects.eclipse.org/projects/tools.buildship) is an Eclipse plugin +that integrates Gradle into the IDE. This is the recommended method. + +1. In 'File' menu, select 'Import...'. +2. In the Import Wizard, select 'Gradle' > 'Existing Gradle Project'. Click 'Next'. +3. In the Gradle Import Wizard, click 'Next' to arrive at directory selection. +4. Select the directory that contains `build.gradle` file in 'Project root directory' field. +5. Click 'Finish' and allow the plugin to import the project. + +When using this method, any changes to the `build.gradle` file must be followed by a Gradle +refresh ((Project context menu) > 'Gradle' > 'Refresh Gradle Project'). + + +#### Using Eclipse plugin for Gradle + +Gradle features a plugin for Eclipse that can generate project specifications for the IDE. +It is deactivated by default. + +1. Enable the Eclipse plugin by uncommenting the `id 'eclipse'` line in `build.gradle` +(note the disappearance of `//`). Please make sure not to accidentally commit this change in git! + +``` +plugins { + // Apply the java-library plugin to add support for Java Library + id 'java-library' + + /* + * Uncomment the following line to enable the Eclipse plugin. + * This is only necessary if you don't use Buildship plugin from the IDE + */ + id 'eclipse' +} +``` + +2. Run + `./gradlew eclipse` +in the directory that contains `build.gradle`. This command will +generate Eclipse project files. +3. In 'File' menu in the Eclipse IDE, select 'Import...'. +4. In the Import Wizard, select 'General' > 'Existing Projects into Workspace'. Click 'Next'. +5. Select the directory that contains `build.gradle` file in 'Select root directory' field. +6. Click 'Finish' and allow the IDE to import the project. + +When using this method, any changes to the `build.gradle` file must be followed by +`./gradlew eclipse` command and a project refresh ((Project context menu) > 'Refresh'). + + +### Creating a Run Configuration + +Run configurations are used by Eclipse IDE to specify how a project must be run. + +1. In 'Run' menu, select 'Run Configurations...'. +2. In the Run Configurations, select 'Java Application', then click 'New launch configuration'. +3. Specify the project and the name of the new configuration. +4. Put +`ru.windcorp.progressia.client.ProgressiaClientMain` +into 'Main Class' field. +5. In the 'Arguments' tab, put +`${workspace_loc:Progressia}/run` +into 'Working directory:' > 'Other' field. Replace `Progressia` with your name of the project. +Alternatively specify another location outside of the project's root directory. +6. Click 'Apply' to save changes. Exit Run Configurations. + +Step 5 is required to specify that the game must run in some directory other than the project root, +which is the default in Eclipse. + +## Common problems + +### Buildship plugin fails with a cryptic message + +This may be caused by a lack of Java in your system path. Eclipse stores the path to the JVM it +uses in its settings and is thus not affected by the changes to the system path. However, Gradle +searches for Java installations in system path regardless and may fail independently of Eclipse. + +__Solution:__ the simplest solution is to reinstall JDK making sure that system path is affected. +See [Build Guide](BuildGuide.md) for details. Another course of action is to manually append the +Java installation directory (specifically its `bin` folder) to the system `PATH` variable. \ No newline at end of file diff --git a/pictures/jetbrains_ide.png b/pictures/jetbrains_ide.png deleted file mode 100644 index 75706e6..0000000 Binary files a/pictures/jetbrains_ide.png and /dev/null differ diff --git a/run/.dummy b/run/.dummy new file mode 100644 index 0000000..d32d10a --- /dev/null +++ b/run/.dummy @@ -0,0 +1 @@ +This is a dummy file to make sure run/ directory is stored and created by git. \ No newline at end of file diff --git a/src/main/java/ru/windcorp/progressia/ProgressiaLauncher.java b/src/main/java/ru/windcorp/progressia/ProgressiaLauncher.java index b313255..dfda8c5 100644 --- a/src/main/java/ru/windcorp/progressia/ProgressiaLauncher.java +++ b/src/main/java/ru/windcorp/progressia/ProgressiaLauncher.java @@ -39,7 +39,6 @@ public class ProgressiaLauncher { CrashReports.registerProvider(new OpenALContextProvider()); CrashReports.registerProvider(new ArgsContextProvider()); CrashReports.registerProvider(new LanguageContextProvider()); - CrashReports.registerProvider(new StackTraceProvider()); // Analyzers CrashReports.registerAnalyzer(new OutOfMemoryAnalyzer()); diff --git a/src/main/java/ru/windcorp/progressia/client/ClientProxy.java b/src/main/java/ru/windcorp/progressia/client/ClientProxy.java index 6f59223..86d1302 100644 --- a/src/main/java/ru/windcorp/progressia/client/ClientProxy.java +++ b/src/main/java/ru/windcorp/progressia/client/ClientProxy.java @@ -18,6 +18,7 @@ package ru.windcorp.progressia.client; import ru.windcorp.progressia.Proxy; +import ru.windcorp.progressia.client.audio.AudioSystem; import ru.windcorp.progressia.client.graphics.backend.GraphicsBackend; import ru.windcorp.progressia.client.graphics.backend.RenderTaskQueue; import ru.windcorp.progressia.client.graphics.flat.FlatRenderProgram; @@ -47,6 +48,8 @@ public class ClientProxy implements Proxy { Atlases.loadAllAtlases(); + AudioSystem.initialize(); + ServerState.startServer(); ClientState.connectToLocalServer(); } diff --git a/src/main/java/ru/windcorp/progressia/client/ProgressiaClientMain.java b/src/main/java/ru/windcorp/progressia/client/ProgressiaClientMain.java index 25e19e9..f44cb9b 100644 --- a/src/main/java/ru/windcorp/progressia/client/ProgressiaClientMain.java +++ b/src/main/java/ru/windcorp/progressia/client/ProgressiaClientMain.java @@ -18,12 +18,10 @@ package ru.windcorp.progressia.client; import ru.windcorp.progressia.ProgressiaLauncher; -import ru.windcorp.progressia.test.ALTest; public class ProgressiaClientMain { public static void main(String[] args) { - ALTest.execute(); ProgressiaLauncher.launch(args, new ClientProxy()); } diff --git a/src/main/java/ru/windcorp/progressia/client/audio/AudioSystem.java b/src/main/java/ru/windcorp/progressia/client/audio/AudioSystem.java new file mode 100644 index 0000000..4834275 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/audio/AudioSystem.java @@ -0,0 +1,16 @@ +package ru.windcorp.progressia.client.audio; + +public class AudioSystem { + static public void initialize() { + AudioManager.initAL(); + Thread shutdownHook = new Thread(AudioManager::closeAL, "AL Shutdown Hook"); + Runtime.getRuntime().addShutdownHook(shutdownHook); + loadAudioData(); + } + + static void loadAudioData() { + AudioManager.loadSound("assets/sounds/block_destroy_clap.ogg", + "Progressia:BlockDestroy", + AudioFormat.MONO); + } +} \ No newline at end of file diff --git a/src/main/java/ru/windcorp/progressia/common/util/FloatMathUtils.java b/src/main/java/ru/windcorp/progressia/common/util/FloatMathUtils.java index 89449ab..e18e2d2 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/FloatMathUtils.java +++ b/src/main/java/ru/windcorp/progressia/common/util/FloatMathUtils.java @@ -1,13 +1,11 @@ package ru.windcorp.progressia.common.util; -import org.apache.commons.math3.util.FastMath; - public class FloatMathUtils { public static final float PI_F = (float) Math.PI; public static float floor(float x) { - return (float) FastMath.floor(x); + return (float) Math.floor(x); } public static float normalizeAngle(float a) { diff --git a/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReports.java b/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReports.java index 9aa2e32..658c80d 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReports.java +++ b/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReports.java @@ -2,14 +2,11 @@ package ru.windcorp.progressia.common.util.crash; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.core.util.StringBuilderWriter; import ru.windcorp.jputil.chars.StringUtil; import java.io.BufferedWriter; import java.io.IOException; -import java.io.PrintWriter; -import java.io.Writer; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -88,6 +85,8 @@ public class CrashReports { * @return {@code null}, although this method never returns normally. Provided for convenience. */ public static RuntimeException crash(Throwable throwable, String messageFormat, Object... args) { + final StackTraceElement[] reportStackTrace; + if (throwable instanceof ReportedException) { ReportedException reportedException = (ReportedException) throwable; @@ -95,6 +94,10 @@ public class CrashReports { throwable = reportedException.getCause(); messageFormat = reportedException.getMessageFormat(); args = reportedException.getArgs(); + + reportStackTrace = reportedException.getStackTrace(); + } else { + reportStackTrace = getCurrentStackTrace(); } StringBuilder output = new StringBuilder(); @@ -127,7 +130,9 @@ public class CrashReports { appendMessageFormat(output, messageFormat, args); - appendStackTrace(output, throwable); + appendStackTrace(output, reportStackTrace, "Reported at:"); + output.append('\n'); + appendThrowable(output, throwable); export(output.toString()); @@ -135,7 +140,14 @@ public class CrashReports { return null; } - private static void appendContextProviders(StringBuilder output) { + private static StackTraceElement[] getCurrentStackTrace() { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + final int trim = 3; + + return Arrays.copyOfRange(stackTrace, trim, stackTrace.length); + } + + private static void appendContextProviders(StringBuilder output) { // Do a local copy to avoid deadlocks -OLEGSHA ContextProvider[] localProvidersCopy = PROVIDERS.toArray(new ContextProvider[PROVIDERS.size()]); @@ -225,22 +237,22 @@ public class CrashReports { addSeparator(output); } - private static void appendStackTrace(StringBuilder output, Throwable throwable) { - output.append("Stacktrace: \n"); - + private static void appendThrowable(StringBuilder output, Throwable throwable) { if (throwable == null) { - output.append("no Throwable provided").append("\n"); + output.append("No Throwable provided").append("\n"); return; } - - // Formatting to a human-readable string - Writer sink = new StringBuilderWriter(output); - try { - throwable.printStackTrace(new PrintWriter(sink)); - } catch (Exception e) { - // PLAK + + output.append("Reported Throwable:\n"); + appendStackTrace(output, throwable.getStackTrace(), throwable.toString()); + } + + private static void appendStackTrace(StringBuilder output, StackTraceElement[] stackTrace, String header) { + output.append(header).append('\n'); + + for (StackTraceElement element : stackTrace) { + output.append('\t').append(element).append('\n'); } - output.append("\n"); } private static void export(String report) { diff --git a/src/main/java/ru/windcorp/progressia/common/util/crash/providers/StackTraceProvider.java b/src/main/java/ru/windcorp/progressia/common/util/crash/providers/StackTraceProvider.java deleted file mode 100644 index 083ac13..0000000 --- a/src/main/java/ru/windcorp/progressia/common/util/crash/providers/StackTraceProvider.java +++ /dev/null @@ -1,24 +0,0 @@ -package ru.windcorp.progressia.common.util.crash.providers; - -import ru.windcorp.progressia.common.util.crash.ContextProvider; - -import java.util.Map; - -public class StackTraceProvider implements ContextProvider { - @Override - public void provideContext(Map output) { - StackTraceElement[] stackTraceBuffer = Thread.currentThread().getStackTrace(); - StringBuilder sb = new StringBuilder(); - sb.append("\n"); - for (int i = 4; i < stackTraceBuffer.length; i++) { - sb.append('\t').append(stackTraceBuffer[i].toString()).append("\n"); - } - - output.put("Reported from " + Thread.currentThread().getName(), sb.toString()); - } - - @Override - public String getName() { - return "Stack Trace Context Provider"; - } -} diff --git a/src/main/java/ru/windcorp/progressia/test/ALTest.java b/src/main/java/ru/windcorp/progressia/test/ALTest.java deleted file mode 100644 index 45777c5..0000000 --- a/src/main/java/ru/windcorp/progressia/test/ALTest.java +++ /dev/null @@ -1,30 +0,0 @@ -package ru.windcorp.progressia.test; - -import ru.windcorp.progressia.client.audio.AudioFormat; -import ru.windcorp.progressia.client.audio.AudioManager; -import ru.windcorp.progressia.client.audio.Music; - -public class ALTest { - static private void initializeAL() { - AudioManager.initAL(); - } - - static void loadALData() { - AudioManager.loadSound("assets/sounds/sample_stereo.ogg", - "Progressia:SampleStereo", - AudioFormat.STEREO); - AudioManager.loadSound("assets/sounds/block_destroy_clap.ogg", - "Progressia:BlockDestroy", - AudioFormat.MONO); - Music music = new Music("Progressia:SampleStereo"); - music.setGain(0.5f); - //music.play(false); - } - - public static void execute() { - initializeAL(); - Thread shutdownHook = new Thread(AudioManager::closeAL, "AL Shutdown Hook"); - Runtime.getRuntime().addShutdownHook(shutdownHook); - loadALData(); - } -} \ No newline at end of file diff --git a/src/main/resources/assets/sounds/sample_mono.ogg b/src/main/resources/assets/sounds/sample_mono.ogg deleted file mode 100644 index 640167f..0000000 Binary files a/src/main/resources/assets/sounds/sample_mono.ogg and /dev/null differ diff --git a/src/main/resources/assets/sounds/sample_stereo.ogg b/src/main/resources/assets/sounds/sample_stereo.ogg deleted file mode 100644 index f51caa2..0000000 Binary files a/src/main/resources/assets/sounds/sample_stereo.ogg and /dev/null differ