├── .gitignore ├── LICENSE ├── README.md ├── RefreshEnv.cmd ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── requirements.txt ├── rlbot.cfg ├── run-bot.bat ├── run.bat ├── run.py └── src └── main ├── java └── rlbotexample │ ├── JavaExample.java │ ├── SampleBot.java │ ├── SamplePythonInterface.java │ ├── boost │ ├── BoostManager.java │ └── BoostPad.java │ ├── input │ ├── DataPacket.java │ ├── ball │ │ ├── BallData.java │ │ └── BallTouch.java │ └── car │ │ ├── CarData.java │ │ └── CarOrientation.java │ ├── output │ └── ControlsOutput.java │ ├── prediction │ └── BallPredictionHelper.java │ ├── util │ └── PortReader.java │ └── vector │ ├── Vector2.java │ └── Vector3.java ├── python ├── README_Tournament.md ├── javaExample.cfg ├── javaExample.py └── javaExampleAppearance.cfg └── resources └── icon.png /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | out/ 3 | build/ 4 | .gradle 5 | __pycache__ 6 | *.iml -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RLBotJavaExample 2 | An example bot implemented in Java 3 | 4 | ## Video Guide 5 | 6 | https://youtu.be/mPfYqKe_KRs (slightly outdated because it uses the old GUI) 7 | 8 | ## Usage Instructions: 9 | 10 | 1. Make sure you've installed the Java 8 JDK or newer. Here's the [Java 8 JDK](https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html). 11 | 1. Make sure you've [set the JAVA_HOME environment variable](https://javatutorial.net/set-java-home-windows-10). 12 | 1. Download this repository 13 | 1. Double click on run-bot.bat and leave it running. It's supposed to stay 14 | open and it's OK if it says something like "75%". 15 | - Alternatively you can launch the bot from inside an IDE 16 | 1. Get RLBotGUI (see https://youtu.be/lPkID_IH88U for instructions). 17 | 1. Use Add -> Load folder in RLBotGUI on the current directory. This bot should appear in the list. 18 | 1. In RLBotGUI, put the bot on a team and start the match. 19 | 20 | - Bot behavior is controlled by `src/main/java/rlbotexample/SampleBot.java` 21 | 22 | See the [wiki](https://github.com/RLBot/RLBotJavaExample/wiki) 23 | for tips to improve your programming experience. 24 | -------------------------------------------------------------------------------- /RefreshEnv.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | :: This file is taken from chocolatey: 3 | :: https://github.com/chocolatey/choco/blob/master/src/chocolatey.resources/redirects/RefreshEnv.cmd 4 | :: 5 | :: RefreshEnv.cmd 6 | :: 7 | :: Batch file to read environment variables from registry and 8 | :: set session variables to these values. 9 | :: 10 | :: With this batch file, there should be no need to reload command 11 | :: environment every time you want environment changes to propagate 12 | 13 | ::echo "RefreshEnv.cmd only works from cmd.exe, please install the Chocolatey Profile to take advantage of refreshenv from PowerShell" 14 | echo | set /p dummy="Refreshing environment variables from registry for cmd.exe. Please wait..." 15 | 16 | goto main 17 | 18 | :: Set one environment variable from registry key 19 | :SetFromReg 20 | "%WinDir%\System32\Reg" QUERY "%~1" /v "%~2" > "%TEMP%\_envset.tmp" 2>NUL 21 | for /f "usebackq skip=2 tokens=2,*" %%A IN ("%TEMP%\_envset.tmp") do ( 22 | echo/set "%~3=%%B" 23 | ) 24 | goto :EOF 25 | 26 | :: Get a list of environment variables from registry 27 | :GetRegEnv 28 | "%WinDir%\System32\Reg" QUERY "%~1" > "%TEMP%\_envget.tmp" 29 | for /f "usebackq skip=2" %%A IN ("%TEMP%\_envget.tmp") do ( 30 | if /I not "%%~A"=="Path" ( 31 | call :SetFromReg "%~1" "%%~A" "%%~A" 32 | ) 33 | ) 34 | goto :EOF 35 | 36 | :main 37 | echo/@echo off >"%TEMP%\_env.cmd" 38 | 39 | :: Slowly generating final file 40 | call :GetRegEnv "HKLM\System\CurrentControlSet\Control\Session Manager\Environment" >> "%TEMP%\_env.cmd" 41 | call :GetRegEnv "HKCU\Environment">>"%TEMP%\_env.cmd" >> "%TEMP%\_env.cmd" 42 | 43 | :: Special handling for PATH - mix both User and System 44 | call :SetFromReg "HKLM\System\CurrentControlSet\Control\Session Manager\Environment" Path Path_HKLM >> "%TEMP%\_env.cmd" 45 | call :SetFromReg "HKCU\Environment" Path Path_HKCU >> "%TEMP%\_env.cmd" 46 | 47 | :: Caution: do not insert space-chars before >> redirection sign 48 | echo/set "Path=%%Path_HKLM%%;%%Path_HKCU%%" >> "%TEMP%\_env.cmd" 49 | 50 | :: Cleanup 51 | del /f /q "%TEMP%\_envset.tmp" 2>nul 52 | del /f /q "%TEMP%\_envget.tmp" 2>nul 53 | 54 | :: capture user / architecture 55 | SET "OriginalUserName=%USERNAME%" 56 | SET "OriginalArchitecture=%PROCESSOR_ARCHITECTURE%" 57 | 58 | :: Set these variables 59 | call "%TEMP%\_env.cmd" 60 | 61 | :: reset user / architecture 62 | SET "USERNAME=%OriginalUserName%" 63 | SET "PROCESSOR_ARCHITECTURE=%OriginalArchitecture%" 64 | 65 | echo | set /p dummy="Finished." 66 | echo . -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // This file controls gradle, which we are using to install and update the RLBot framework used by this example bot, 2 | // and also compile and run the java code used by this bot. 3 | 4 | apply plugin: 'java' 5 | apply plugin: 'application' 6 | 7 | sourceCompatibility = 1.8 8 | 9 | repositories { 10 | mavenCentral() 11 | } 12 | 13 | mainClassName = 'rlbotexample.JavaExample' 14 | 15 | // This directory will be created and the interface dll copied into it at runtime. 16 | // The end result is that the interface dll will be available for loading. 17 | def dllDirectory = 'build/dll' 18 | applicationDefaultJvmArgs = ["-Djna.library.path=" + dllDirectory] 19 | 20 | dependencies { 21 | // Fetch the framework jar file 22 | compile 'org.rlbot.commons:framework:2.+' 23 | 24 | // This is makes it easy to find the dll when running in intellij, where JVM args don't get passed from gradle. 25 | runtime files(dllDirectory) 26 | } 27 | 28 | 29 | task createDllDirectory { 30 | mkdir dllDirectory 31 | } 32 | 33 | run.dependsOn createDllDirectory 34 | 35 | applicationDistribution.exclude(dllDirectory) 36 | 37 | // You can run gradew.bat distZip to generate a zip file suitable for tournament submissions. 38 | // It will be generated in build/distributions 39 | distZip { 40 | into ('python') { 41 | from fileTree('src/main/python') { 42 | exclude '__pycache__' 43 | } 44 | } 45 | } 46 | 47 | // This is the same as distZip, but not zipped. Handy for testing your tournament submission more rapidly. 48 | installDist { 49 | into ('../python') { 50 | from fileTree('src/main/python') { 51 | exclude '__pycache__' 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RLBot/RLBotJavaExample/ffed34b232f5349fb81dd05c66fd63839ce8f5cd/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # Include everything the framework requires 2 | # You will automatically get updates for all versions starting with "1.". 3 | rlbot==1.* 4 | 5 | # Used to allow python to send commands to java, e.g. start running the bot 6 | py4j 7 | -------------------------------------------------------------------------------- /rlbot.cfg: -------------------------------------------------------------------------------- 1 | [RLBot Configuration] 2 | # Visit https://github.com/RLBot/RLBot/wiki/Config-File-Documentation to see what you can put here. 3 | 4 | [Team Configuration] 5 | # Visit https://github.com/RLBot/RLBot/wiki/Config-File-Documentation to see what you can put here. 6 | 7 | [Match Configuration] 8 | # Visit https://github.com/RLBot/RLBot/wiki/Config-File-Documentation to see what you can put here. 9 | # Number of bots/players which will be spawned. We support up to max 64. 10 | num_participants = 2 11 | game_mode = Soccer 12 | game_map = Mannfield 13 | enable_rendering = True 14 | enable_state_setting = True 15 | 16 | [Mutator Configuration] 17 | # Visit https://github.com/RLBot/RLBot/wiki/Config-File-Documentation to see what you can put here. 18 | 19 | [Participant Configuration] 20 | # Put the name of your bot config file here. Only num_participants config files will be read! 21 | # Everything needs a config, even players and default bots. We still set loadouts and names from config! 22 | participant_config_0 = src/main/python/javaExample.cfg 23 | participant_config_1 = src/main/python/javaExample.cfg 24 | participant_config_2 = src/main/python/javaExample.cfg 25 | participant_config_3 = src/main/python/javaExample.cfg 26 | participant_config_4 = src/main/python/javaExample.cfg 27 | participant_config_5 = src/main/python/javaExample.cfg 28 | participant_config_6 = src/main/python/javaExample.cfg 29 | participant_config_7 = src/main/python/javaExample.cfg 30 | participant_config_8 = src/main/python/javaExample.cfg 31 | participant_config_9 = src/main/python/javaExample.cfg 32 | 33 | # team 0 shoots on positive goal, team 1 shoots on negative goal 34 | participant_team_0 = 0 35 | participant_team_1 = 1 36 | participant_team_2 = 0 37 | participant_team_3 = 1 38 | participant_team_4 = 0 39 | participant_team_5 = 1 40 | participant_team_6 = 0 41 | participant_team_7 = 1 42 | participant_team_8 = 0 43 | participant_team_9 = 1 44 | 45 | # Accepted values are "human", "rlbot", "psyonix", and "party_member_bot" 46 | # You can have up to 4 local players and they must be activated in game or it will crash. 47 | # If no player is specified you will be spawned in as spectator! 48 | # human - not controlled by the framework 49 | # rlbot - controlled by the framework 50 | # psyonix - default bots (skill level can be changed with participant_bot_skill 51 | # party_member_bot - controlled by the framework but the game detects it as a human 52 | participant_type_0 = rlbot 53 | participant_type_1 = rlbot 54 | participant_type_2 = rlbot 55 | participant_type_3 = rlbot 56 | participant_type_4 = rlbot 57 | participant_type_5 = rlbot 58 | participant_type_6 = rlbot 59 | participant_type_7 = rlbot 60 | participant_type_8 = rlbot 61 | participant_type_9 = rlbot 62 | 63 | 64 | # If participant is a bot and not RLBot controlled, this value will be used to set bot skill. 65 | # 0.0 is Rookie, 0.5 is pro, 1.0 is all-star. You can set values in-between as well. 66 | # Please leave a value here even if it isn't used :) 67 | participant_bot_skill_0 = 1.0 68 | participant_bot_skill_1 = 1.0 69 | participant_bot_skill_2 = 1.0 70 | participant_bot_skill_3 = 1.0 71 | participant_bot_skill_4 = 1.0 72 | participant_bot_skill_5 = 1.0 73 | participant_bot_skill_6 = 1.0 74 | participant_bot_skill_7 = 1.0 75 | participant_bot_skill_8 = 1.0 76 | participant_bot_skill_9 = 1.0 77 | -------------------------------------------------------------------------------- /run-bot.bat: -------------------------------------------------------------------------------- 1 | @rem Change the working directory to the location of this file so that relative paths will work 2 | cd /D "%~dp0" 3 | 4 | @rem Start running the bot. 5 | call ./gradlew.bat --no-daemon run 6 | 7 | pause 8 | -------------------------------------------------------------------------------- /run.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | @rem Change the working directory to the location of this file so that relative paths will work 4 | cd /D "%~dp0" 5 | 6 | @rem Make sure the environment variables are up-to-date. This is useful if the user installed python a moment ago. 7 | call ./RefreshEnv.cmd 8 | 9 | python run.py 10 | 11 | pause 12 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import sys 3 | 4 | DEFAULT_LOGGER = 'rlbot' 5 | 6 | if __name__ == '__main__': 7 | 8 | try: 9 | from rlbot.utils import public_utils, logging_utils 10 | 11 | logger = logging_utils.get_logger(DEFAULT_LOGGER) 12 | if not public_utils.have_internet(): 13 | logger.log(logging_utils.logging_level, 14 | 'Skipping upgrade check for now since it looks like you have no internet') 15 | elif public_utils.is_safe_to_upgrade(): 16 | subprocess.call([sys.executable, "-m", "pip", "install", '-r', 'requirements.txt']) 17 | subprocess.call([sys.executable, "-m", "pip", "install", 'rlbot', '--upgrade']) 18 | 19 | # https://stackoverflow.com/a/44401013 20 | rlbots = [module for module in sys.modules if module.startswith('rlbot')] 21 | for rlbot_module in rlbots: 22 | sys.modules.pop(rlbot_module) 23 | 24 | except ImportError: 25 | subprocess.call([sys.executable, "-m", "pip", "install", '-r', 'requirements.txt', '--upgrade', '--upgrade-strategy=eager']) 26 | 27 | try: 28 | from rlbot import runner 29 | runner.main() 30 | except Exception as e: 31 | print("Encountered exception: ", e) 32 | print("Press enter to close.") 33 | input() 34 | -------------------------------------------------------------------------------- /src/main/java/rlbotexample/JavaExample.java: -------------------------------------------------------------------------------- 1 | package rlbotexample; 2 | 3 | import rlbot.manager.BotManager; 4 | import rlbotexample.util.PortReader; 5 | 6 | import javax.swing.*; 7 | import javax.swing.border.EmptyBorder; 8 | import java.awt.*; 9 | import java.awt.event.ActionListener; 10 | import java.net.URL; 11 | import java.util.Set; 12 | import java.util.stream.Collectors; 13 | 14 | /** 15 | * See JavaAgent.py for usage instructions. 16 | * 17 | * Look inside SampleBot.java for the actual bot logic! 18 | */ 19 | public class JavaExample { 20 | 21 | private static final int DEFAULT_PORT = 17357; 22 | 23 | public static void main(String[] args) { 24 | 25 | BotManager botManager = new BotManager(); 26 | int port = PortReader.readPortFromArgs(args).orElseGet(() -> { 27 | System.out.println("Could not read port from args, using default!"); 28 | return DEFAULT_PORT; 29 | }); 30 | 31 | SamplePythonInterface pythonInterface = new SamplePythonInterface(port, botManager); 32 | new Thread(pythonInterface::start).start(); 33 | 34 | displayWindow(botManager, port); 35 | } 36 | 37 | private static void displayWindow(BotManager botManager, int port) { 38 | JFrame frame = new JFrame("Java Bot"); 39 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 40 | 41 | JPanel panel = new JPanel(); 42 | panel.setBorder(new EmptyBorder(10, 10, 10, 10)); 43 | BorderLayout borderLayout = new BorderLayout(); 44 | panel.setLayout(borderLayout); 45 | JPanel dataPanel = new JPanel(); 46 | dataPanel.setLayout(new BoxLayout(dataPanel, BoxLayout.Y_AXIS)); 47 | dataPanel.setBorder(new EmptyBorder(0, 10, 0, 0)); 48 | dataPanel.add(new JLabel("Listening on port " + port), BorderLayout.CENTER); 49 | dataPanel.add(new JLabel("I'm the thing controlling the Java bot, keep me open :)"), BorderLayout.CENTER); 50 | JLabel botsRunning = new JLabel("Bots running: "); 51 | dataPanel.add(botsRunning, BorderLayout.CENTER); 52 | panel.add(dataPanel, BorderLayout.CENTER); 53 | frame.add(panel); 54 | 55 | URL url = JavaExample.class.getClassLoader().getResource("icon.png"); 56 | Image image = Toolkit.getDefaultToolkit().createImage(url); 57 | panel.add(new JLabel(new ImageIcon(image)), BorderLayout.WEST); 58 | frame.setIconImage(image); 59 | 60 | frame.pack(); 61 | frame.setVisible(true); 62 | 63 | ActionListener myListener = e -> { 64 | Set runningBotIndices = botManager.getRunningBotIndices(); 65 | 66 | String botsStr; 67 | if (runningBotIndices.isEmpty()) { 68 | botsStr = "None"; 69 | } else { 70 | botsStr = runningBotIndices.stream() 71 | .sorted() 72 | .map(i -> "#" + i) 73 | .collect(Collectors.joining(", ")); 74 | } 75 | botsRunning.setText("Bots indices running: " + botsStr); 76 | }; 77 | 78 | new Timer(1000, myListener).start(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/rlbotexample/SampleBot.java: -------------------------------------------------------------------------------- 1 | package rlbotexample; 2 | 3 | import rlbot.Bot; 4 | import rlbot.ControllerState; 5 | import rlbot.cppinterop.RLBotDll; 6 | import rlbot.cppinterop.RLBotInterfaceException; 7 | import rlbot.flat.BallPrediction; 8 | import rlbot.flat.GameTickPacket; 9 | import rlbot.flat.QuickChatSelection; 10 | import rlbot.manager.BotLoopRenderer; 11 | import rlbot.render.Renderer; 12 | import rlbotexample.boost.BoostManager; 13 | import rlbotexample.input.DataPacket; 14 | import rlbotexample.input.car.CarData; 15 | import rlbotexample.output.ControlsOutput; 16 | import rlbotexample.prediction.BallPredictionHelper; 17 | import rlbotexample.vector.Vector2; 18 | 19 | import java.awt.*; 20 | 21 | public class SampleBot implements Bot { 22 | 23 | private final int playerIndex; 24 | 25 | public SampleBot(int playerIndex) { 26 | this.playerIndex = playerIndex; 27 | } 28 | 29 | /** 30 | * This is where we keep the actual bot logic. This function shows how to chase the ball. 31 | * Modify it to make your bot smarter! 32 | */ 33 | private ControlsOutput processInput(DataPacket input) { 34 | 35 | Vector2 ballPosition = input.ball.position.flatten(); 36 | CarData myCar = input.car; 37 | Vector2 carPosition = myCar.position.flatten(); 38 | Vector2 carDirection = myCar.orientation.noseVector.flatten(); 39 | 40 | // Subtract the two positions to get a vector pointing from the car to the ball. 41 | Vector2 carToBall = ballPosition.minus(carPosition); 42 | 43 | // How far does the car need to rotate before it's pointing exactly at the ball? 44 | double steerCorrectionRadians = carDirection.correctionAngle(carToBall); 45 | 46 | boolean goLeft = steerCorrectionRadians > 0; 47 | 48 | // This is optional! 49 | drawDebugLines(input, myCar, goLeft); 50 | 51 | // This is also optional! 52 | if (input.ball.position.z > 300) { 53 | RLBotDll.sendQuickChat(playerIndex, false, QuickChatSelection.Compliments_NiceOne); 54 | } 55 | 56 | return new ControlsOutput() 57 | .withSteer(goLeft ? -1 : 1) 58 | .withThrottle(1); 59 | } 60 | 61 | /** 62 | * This is a nice example of using the rendering feature. 63 | */ 64 | private void drawDebugLines(DataPacket input, CarData myCar, boolean goLeft) { 65 | // Here's an example of rendering debug data on the screen. 66 | Renderer renderer = BotLoopRenderer.forBotLoop(this); 67 | 68 | // Draw a line from the car to the ball 69 | renderer.drawLine3d(Color.LIGHT_GRAY, myCar.position, input.ball.position); 70 | 71 | // Draw a line that points out from the nose of the car. 72 | renderer.drawLine3d(goLeft ? Color.BLUE : Color.RED, 73 | myCar.position.plus(myCar.orientation.noseVector.scaled(150)), 74 | myCar.position.plus(myCar.orientation.noseVector.scaled(300))); 75 | 76 | renderer.drawString3d(goLeft ? "left" : "right", Color.WHITE, myCar.position, 2, 2); 77 | 78 | if(input.ball.hasBeenTouched) { 79 | float lastTouchTime = myCar.elapsedSeconds - input.ball.latestTouch.gameSeconds; 80 | Color touchColor = input.ball.latestTouch.team == 0 ? Color.BLUE : Color.ORANGE; 81 | renderer.drawString3d((int)lastTouchTime + "s", touchColor, input.ball.position, 2, 2); 82 | } 83 | 84 | try { 85 | // Draw 3 seconds of ball prediction 86 | BallPrediction ballPrediction = RLBotDll.getBallPrediction(); 87 | BallPredictionHelper.drawTillMoment(ballPrediction, myCar.elapsedSeconds + 3, Color.CYAN, renderer); 88 | } catch (RLBotInterfaceException e) { 89 | e.printStackTrace(); 90 | } 91 | } 92 | 93 | 94 | @Override 95 | public int getIndex() { 96 | return this.playerIndex; 97 | } 98 | 99 | /** 100 | * This is the most important function. It will automatically get called by the framework with fresh data 101 | * every frame. Respond with appropriate controls! 102 | */ 103 | @Override 104 | public ControllerState processInput(GameTickPacket packet) { 105 | 106 | if (packet.playersLength() <= playerIndex || packet.ball() == null || !packet.gameInfo().isRoundActive()) { 107 | // Just return immediately if something looks wrong with the data. This helps us avoid stack traces. 108 | return new ControlsOutput(); 109 | } 110 | 111 | // Update the boost manager and tile manager with the latest data 112 | BoostManager.loadGameTickPacket(packet); 113 | 114 | // Translate the raw packet data (which is in an unpleasant format) into our custom DataPacket class. 115 | // The DataPacket might not include everything from GameTickPacket, so improve it if you need to! 116 | DataPacket dataPacket = new DataPacket(packet, playerIndex); 117 | 118 | // Do the actual logic using our dataPacket. 119 | ControlsOutput controlsOutput = processInput(dataPacket); 120 | 121 | return controlsOutput; 122 | } 123 | 124 | @Override 125 | public void retire() { 126 | System.out.println("Retiring sample bot " + playerIndex); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/rlbotexample/SamplePythonInterface.java: -------------------------------------------------------------------------------- 1 | package rlbotexample; 2 | 3 | import rlbot.Bot; 4 | import rlbot.manager.BotManager; 5 | import rlbot.pyinterop.SocketServer; 6 | 7 | public class SamplePythonInterface extends SocketServer { 8 | 9 | public SamplePythonInterface(int port, BotManager botManager) { 10 | super(port, botManager); 11 | } 12 | 13 | @Override 14 | protected Bot initBot(int index, String botType, int team) { 15 | return new SampleBot(index); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/rlbotexample/boost/BoostManager.java: -------------------------------------------------------------------------------- 1 | package rlbotexample.boost; 2 | 3 | import rlbot.cppinterop.RLBotDll; 4 | import rlbot.flat.BoostPadState; 5 | import rlbot.flat.FieldInfo; 6 | import rlbot.flat.GameTickPacket; 7 | import rlbotexample.vector.Vector3; 8 | 9 | import java.io.IOException; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | /** 14 | * Information about where boost pads are located on the field and what status they have. 15 | * 16 | * This class is here for your convenience, it is NOT part of the framework. You can change it as much 17 | * as you want, or delete it. 18 | */ 19 | public class BoostManager { 20 | 21 | private static final List orderedBoosts = new ArrayList<>(); 22 | private static final List fullBoosts = new ArrayList<>(); 23 | private static final List smallBoosts = new ArrayList<>(); 24 | 25 | public static List getFullBoosts() { 26 | return fullBoosts; 27 | } 28 | 29 | public static List getSmallBoosts() { 30 | return smallBoosts; 31 | } 32 | 33 | private static void loadFieldInfo(FieldInfo fieldInfo) { 34 | 35 | synchronized (orderedBoosts) { 36 | 37 | orderedBoosts.clear(); 38 | fullBoosts.clear(); 39 | smallBoosts.clear(); 40 | 41 | for (int i = 0; i < fieldInfo.boostPadsLength(); i++) { 42 | rlbot.flat.BoostPad flatPad = fieldInfo.boostPads(i); 43 | BoostPad ourPad = new BoostPad(new Vector3(flatPad.location()), flatPad.isFullBoost()); 44 | orderedBoosts.add(ourPad); 45 | if (ourPad.isFullBoost()) { 46 | fullBoosts.add(ourPad); 47 | } else { 48 | smallBoosts.add(ourPad); 49 | } 50 | } 51 | } 52 | } 53 | 54 | public static void loadGameTickPacket(GameTickPacket packet) { 55 | 56 | if (packet.boostPadStatesLength() > orderedBoosts.size()) { 57 | try { 58 | loadFieldInfo(RLBotDll.getFieldInfo()); 59 | } catch (IOException e) { 60 | e.printStackTrace(); 61 | return; 62 | } 63 | } 64 | 65 | for (int i = 0; i < packet.boostPadStatesLength(); i++) { 66 | BoostPadState boost = packet.boostPadStates(i); 67 | BoostPad existingPad = orderedBoosts.get(i); // existingPad is also referenced from the fullBoosts and smallBoosts lists 68 | existingPad.setActive(boost.isActive()); 69 | } 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/rlbotexample/boost/BoostPad.java: -------------------------------------------------------------------------------- 1 | package rlbotexample.boost; 2 | 3 | 4 | import rlbotexample.vector.Vector3; 5 | 6 | /** 7 | * Representation of one of the boost pads on the field. 8 | * 9 | * This class is here for your convenience, it is NOT part of the framework. You can change it as much 10 | * as you want, or delete it. 11 | */ 12 | public class BoostPad { 13 | 14 | private final Vector3 location; 15 | private final boolean isFullBoost; 16 | private boolean isActive; 17 | 18 | public BoostPad(Vector3 location, boolean isFullBoost) { 19 | this.location = location; 20 | this.isFullBoost = isFullBoost; 21 | } 22 | 23 | public void setActive(boolean active) { 24 | isActive = active; 25 | } 26 | 27 | public Vector3 getLocation() { 28 | return location; 29 | } 30 | 31 | public boolean isFullBoost() { 32 | return isFullBoost; 33 | } 34 | 35 | public boolean isActive() { 36 | return isActive; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/rlbotexample/input/DataPacket.java: -------------------------------------------------------------------------------- 1 | package rlbotexample.input; 2 | 3 | import rlbot.flat.GameTickPacket; 4 | import rlbotexample.input.ball.BallData; 5 | import rlbotexample.input.car.CarData; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | /** 11 | * This class is here for your convenience, it is NOT part of the framework. You can change it as much 12 | * as you want, or delete it. The benefits of using this instead of rlbot.flat.GameTickPacket are: 13 | * 1. You end up with nice custom Vector3 objects that you can call methods on. 14 | * 2. If the framework changes its data format, you can just update the code here 15 | * and leave your bot logic alone. 16 | */ 17 | public class DataPacket { 18 | 19 | /** Your own car, based on the playerIndex */ 20 | public final CarData car; 21 | 22 | public final List allCars; 23 | 24 | public final BallData ball; 25 | public final int team; 26 | 27 | /** The index of your player */ 28 | public final int playerIndex; 29 | 30 | public DataPacket(GameTickPacket request, int playerIndex) { 31 | 32 | this.playerIndex = playerIndex; 33 | this.ball = new BallData(request.ball()); 34 | 35 | allCars = new ArrayList<>(); 36 | for (int i = 0; i < request.playersLength(); i++) { 37 | allCars.add(new CarData(request.players(i), request.gameInfo().secondsElapsed())); 38 | } 39 | 40 | this.car = allCars.get(playerIndex); 41 | this.team = this.car.team; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/rlbotexample/input/ball/BallData.java: -------------------------------------------------------------------------------- 1 | package rlbotexample.input.ball; 2 | 3 | 4 | import rlbot.flat.BallInfo; 5 | import rlbotexample.vector.Vector3; 6 | 7 | /** 8 | * Basic information about the ball. 9 | * 10 | * This class is here for your convenience, it is NOT part of the framework. You can change it as much 11 | * as you want, or delete it. 12 | */ 13 | public class BallData { 14 | public final Vector3 position; 15 | public final Vector3 velocity; 16 | public final Vector3 spin; 17 | public final BallTouch latestTouch; 18 | public final boolean hasBeenTouched; 19 | 20 | public BallData(final BallInfo ball) { 21 | this.position = new Vector3(ball.physics().location()); 22 | this.velocity = new Vector3(ball.physics().velocity()); 23 | this.spin = new Vector3(ball.physics().angularVelocity()); 24 | this.hasBeenTouched = ball.latestTouch() != null; 25 | this.latestTouch = this.hasBeenTouched ? new BallTouch(ball.latestTouch()) : null; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/rlbotexample/input/ball/BallTouch.java: -------------------------------------------------------------------------------- 1 | package rlbotexample.input.ball; 2 | 3 | 4 | import rlbot.flat.Touch; 5 | import rlbotexample.vector.Vector3; 6 | 7 | /** 8 | * Basic information about the ball's latest touch. 9 | * 10 | * This class is here for your convenience, it is NOT part of the framework. You can change it as much 11 | * as you want, or delete it. 12 | */ 13 | public class BallTouch { 14 | public final Vector3 position; 15 | public final Vector3 normal; 16 | public final String playerName; 17 | public final float gameSeconds; 18 | public final int playerIndex; 19 | public final int team; 20 | 21 | public BallTouch(final Touch touch) { 22 | this.position = new Vector3(touch.location()); 23 | this.normal = new Vector3(touch.normal()); 24 | this.playerName = touch.playerName(); 25 | this.gameSeconds = touch.gameSeconds(); 26 | this.playerIndex = touch.playerIndex(); 27 | this.team = touch.team(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/rlbotexample/input/car/CarData.java: -------------------------------------------------------------------------------- 1 | package rlbotexample.input.car; 2 | 3 | 4 | import rlbotexample.vector.Vector3; 5 | 6 | /** 7 | * Basic information about the car. 8 | * 9 | * This class is here for your convenience, it is NOT part of the framework. You can change it as much 10 | * as you want, or delete it. 11 | */ 12 | public class CarData { 13 | 14 | /** The location of the car on the field. (0, 0, 0) is center field. */ 15 | public final Vector3 position; 16 | 17 | /** The velocity of the car. */ 18 | public final Vector3 velocity; 19 | 20 | /** The orientation of the car */ 21 | public final CarOrientation orientation; 22 | 23 | /** Boost ranges from 0 to 100 */ 24 | public final double boost; 25 | 26 | /** True if the car is driving on the ground, the wall, etc. In other words, true if you can steer. */ 27 | public final boolean hasWheelContact; 28 | 29 | /** 30 | * True if the car is showing the supersonic and can demolish enemies on contact. 31 | * This is a close approximation for whether the car is at max speed. 32 | */ 33 | public final boolean isSupersonic; 34 | 35 | /** 36 | * 0 for blue team, 1 for orange team. 37 | */ 38 | public final int team; 39 | 40 | /** 41 | * This is not really a car-specific attribute, but it's often very useful to know. It's included here 42 | * so you don't need to pass around DataPacket everywhere. 43 | */ 44 | public final float elapsedSeconds; 45 | 46 | public CarData(rlbot.flat.PlayerInfo playerInfo, float elapsedSeconds) { 47 | this.position = new Vector3(playerInfo.physics().location()); 48 | this.velocity = new Vector3(playerInfo.physics().velocity()); 49 | this.orientation = CarOrientation.fromFlatbuffer(playerInfo); 50 | this.boost = playerInfo.boost(); 51 | this.isSupersonic = playerInfo.isSupersonic(); 52 | this.team = playerInfo.team(); 53 | this.hasWheelContact = playerInfo.hasWheelContact(); 54 | this.elapsedSeconds = elapsedSeconds; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/rlbotexample/input/car/CarOrientation.java: -------------------------------------------------------------------------------- 1 | package rlbotexample.input.car; 2 | 3 | 4 | import rlbot.flat.PlayerInfo; 5 | import rlbotexample.vector.Vector3; 6 | 7 | /** 8 | * The car's orientation in space, a.k.a. what direction it's pointing. 9 | * 10 | * This class is here for your convenience, it is NOT part of the framework. You can change it as much 11 | * as you want, or delete it. 12 | */ 13 | public class CarOrientation { 14 | 15 | /** The direction that the front of the car is facing */ 16 | public final Vector3 noseVector; 17 | 18 | /** The direction the roof of the car is facing. (0, 0, 1) means the car is upright. */ 19 | public final Vector3 roofVector; 20 | 21 | /** The direction that the right side of the car is facing. */ 22 | public final Vector3 rightVector; 23 | 24 | public CarOrientation(Vector3 noseVector, Vector3 roofVector) { 25 | 26 | this.noseVector = noseVector; 27 | this.roofVector = roofVector; 28 | this.rightVector = noseVector.crossProduct(roofVector); 29 | } 30 | 31 | public static CarOrientation fromFlatbuffer(PlayerInfo playerInfo) { 32 | return convert( 33 | playerInfo.physics().rotation().pitch(), 34 | playerInfo.physics().rotation().yaw(), 35 | playerInfo.physics().rotation().roll()); 36 | } 37 | 38 | /** 39 | * All params are in radians. 40 | */ 41 | private static CarOrientation convert(double pitch, double yaw, double roll) { 42 | 43 | double noseX = -1 * Math.cos(pitch) * Math.cos(yaw); 44 | double noseY = Math.cos(pitch) * Math.sin(yaw); 45 | double noseZ = Math.sin(pitch); 46 | 47 | double roofX = Math.cos(roll) * Math.sin(pitch) * Math.cos(yaw) + Math.sin(roll) * Math.sin(yaw); 48 | double roofY = Math.cos(yaw) * Math.sin(roll) - Math.cos(roll) * Math.sin(pitch) * Math.sin(yaw); 49 | double roofZ = Math.cos(roll) * Math.cos(pitch); 50 | 51 | return new CarOrientation(new Vector3(noseX, noseY, noseZ), new Vector3(roofX, roofY, roofZ)); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/rlbotexample/output/ControlsOutput.java: -------------------------------------------------------------------------------- 1 | package rlbotexample.output; 2 | 3 | import rlbot.ControllerState; 4 | 5 | /** 6 | * A helper class for returning controls for your bot. 7 | * 8 | * This class is here for your convenience, it is NOT part of the framework. You can change it as much 9 | * as you want, or delete it. 10 | */ 11 | public class ControlsOutput implements ControllerState { 12 | 13 | // 0 is straight, -1 is hard left, 1 is hard right. 14 | private float steer; 15 | 16 | // -1 for front flip, 1 for back flip 17 | private float pitch; 18 | 19 | // 0 is straight, -1 is hard left, 1 is hard right. 20 | private float yaw; 21 | 22 | // 0 is straight, -1 is hard left, 1 is hard right. 23 | private float roll; 24 | 25 | // 0 is none, -1 is backwards, 1 is forwards 26 | private float throttle; 27 | 28 | private boolean jumpDepressed; 29 | private boolean boostDepressed; 30 | private boolean slideDepressed; 31 | private boolean useItemDepressed; 32 | 33 | public ControlsOutput() { 34 | } 35 | 36 | public ControlsOutput withSteer(float steer) { 37 | this.steer = clamp(steer); 38 | return this; 39 | } 40 | 41 | public ControlsOutput withPitch(float pitch) { 42 | this.pitch = clamp(pitch); 43 | return this; 44 | } 45 | 46 | public ControlsOutput withYaw(float yaw) { 47 | this.yaw = clamp(yaw); 48 | return this; 49 | } 50 | 51 | public ControlsOutput withRoll(float roll) { 52 | this.roll = clamp(roll); 53 | return this; 54 | } 55 | 56 | public ControlsOutput withThrottle(float throttle) { 57 | this.throttle = clamp(throttle); 58 | return this; 59 | } 60 | 61 | public ControlsOutput withJump(boolean jumpDepressed) { 62 | this.jumpDepressed = jumpDepressed; 63 | return this; 64 | } 65 | 66 | public ControlsOutput withBoost(boolean boostDepressed) { 67 | this.boostDepressed = boostDepressed; 68 | return this; 69 | } 70 | 71 | public ControlsOutput withSlide(boolean slideDepressed) { 72 | this.slideDepressed = slideDepressed; 73 | return this; 74 | } 75 | 76 | public ControlsOutput withUseItem(boolean useItemDepressed) { 77 | this.useItemDepressed = useItemDepressed; 78 | return this; 79 | } 80 | 81 | public ControlsOutput withJump() { 82 | this.jumpDepressed = true; 83 | return this; 84 | } 85 | 86 | public ControlsOutput withBoost() { 87 | this.boostDepressed = true; 88 | return this; 89 | } 90 | 91 | public ControlsOutput withSlide() { 92 | this.slideDepressed = true; 93 | return this; 94 | } 95 | 96 | public ControlsOutput withUseItem() { 97 | this.useItemDepressed = true; 98 | return this; 99 | } 100 | 101 | private float clamp(float value) { 102 | return Math.max(-1, Math.min(1, value)); 103 | } 104 | 105 | @Override 106 | public float getSteer() { 107 | return steer; 108 | } 109 | 110 | @Override 111 | public float getThrottle() { 112 | return throttle; 113 | } 114 | 115 | @Override 116 | public float getPitch() { 117 | return pitch; 118 | } 119 | 120 | @Override 121 | public float getYaw() { 122 | return yaw; 123 | } 124 | 125 | @Override 126 | public float getRoll() { 127 | return roll; 128 | } 129 | 130 | @Override 131 | public boolean holdJump() { 132 | return jumpDepressed; 133 | } 134 | 135 | @Override 136 | public boolean holdBoost() { 137 | return boostDepressed; 138 | } 139 | 140 | @Override 141 | public boolean holdHandbrake() { 142 | return slideDepressed; 143 | } 144 | 145 | @Override 146 | public boolean holdUseItem() { 147 | return useItemDepressed; 148 | } 149 | } -------------------------------------------------------------------------------- /src/main/java/rlbotexample/prediction/BallPredictionHelper.java: -------------------------------------------------------------------------------- 1 | package rlbotexample.prediction; 2 | 3 | import rlbot.flat.BallPrediction; 4 | import rlbot.flat.PredictionSlice; 5 | import rlbot.render.Renderer; 6 | import rlbotexample.vector.Vector3; 7 | 8 | import java.awt.*; 9 | 10 | /** 11 | * This class can help you get started with ball prediction. Feel free to change it as much as you want, 12 | * this is part of your bot, not part of the framework! 13 | */ 14 | public class BallPredictionHelper { 15 | 16 | public static void drawTillMoment(BallPrediction ballPrediction, float gameSeconds, Color color, Renderer renderer) { 17 | Vector3 previousLocation = null; 18 | for (int i = 0; i < ballPrediction.slicesLength(); i += 4) { 19 | PredictionSlice slice = ballPrediction.slices(i); 20 | if (slice.gameSeconds() > gameSeconds) { 21 | break; 22 | } 23 | Vector3 location = new Vector3(slice.physics().location()); 24 | if (previousLocation != null) { 25 | renderer.drawLine3d(color, previousLocation, location); 26 | } 27 | previousLocation = location; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/rlbotexample/util/PortReader.java: -------------------------------------------------------------------------------- 1 | package rlbotexample.util; 2 | 3 | import java.util.Optional; 4 | 5 | /** 6 | * Utility for reading a network port out of a command line arguments. 7 | * 8 | * This class is here for your convenience, it is NOT part of the framework. You can add to it as much 9 | * as you want, or delete it. 10 | */ 11 | public class PortReader { 12 | 13 | public static Optional readPortFromArgs(String[] args) { 14 | if (args.length == 0) { 15 | return Optional.empty(); 16 | } 17 | try { 18 | return Optional.of(Integer.parseInt(args[0])); 19 | } catch (NumberFormatException e) { 20 | return Optional.empty(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/rlbotexample/vector/Vector2.java: -------------------------------------------------------------------------------- 1 | package rlbotexample.vector; 2 | 3 | /** 4 | * A vector that only knows about x and y components. 5 | * 6 | * This class is here for your convenience, it is NOT part of the framework. You can add to it as much 7 | * as you want, or delete it. 8 | */ 9 | public class Vector2 { 10 | 11 | public final double x; 12 | public final double y; 13 | 14 | public Vector2(double x, double y) { 15 | this.x = x; 16 | this.y = y; 17 | } 18 | 19 | public Vector2 plus(Vector2 other) { 20 | return new Vector2(x + other.x, y + other.y); 21 | } 22 | 23 | public Vector2 minus(Vector2 other) { 24 | return new Vector2(x - other.x, y - other.y); 25 | } 26 | 27 | public Vector2 scaled(double scale) { 28 | return new Vector2(x * scale, y * scale); 29 | } 30 | 31 | /** 32 | * If magnitude is negative, we will return a vector facing the opposite direction. 33 | */ 34 | public Vector2 scaledToMagnitude(double magnitude) { 35 | if (isZero()) { 36 | throw new IllegalStateException("Cannot scale up a vector with length zero!"); 37 | } 38 | double scaleRequired = magnitude / magnitude(); 39 | return scaled(scaleRequired); 40 | } 41 | 42 | public double distance(Vector2 other) { 43 | double xDiff = x - other.x; 44 | double yDiff = y - other.y; 45 | return Math.sqrt(xDiff * xDiff + yDiff * yDiff); 46 | } 47 | 48 | /** 49 | * This is the length of the vector. 50 | */ 51 | public double magnitude() { 52 | return Math.sqrt(magnitudeSquared()); 53 | } 54 | 55 | public double magnitudeSquared() { 56 | return x * x + y * y; 57 | } 58 | 59 | public Vector2 normalized() { 60 | 61 | if (isZero()) { 62 | throw new IllegalStateException("Cannot normalize a vector with length zero!"); 63 | } 64 | return this.scaled(1 / magnitude()); 65 | } 66 | 67 | public double dotProduct(Vector2 other) { 68 | return x * other.x + y * other.y; 69 | } 70 | 71 | public boolean isZero() { 72 | return x == 0 && y == 0; 73 | } 74 | 75 | /** 76 | * The correction angle is how many radians you need to rotate this vector to make it line up with the "ideal" 77 | * vector. This is very useful for deciding which direction to steer. 78 | */ 79 | public double correctionAngle(Vector2 ideal) { 80 | double currentRad = Math.atan2(y, x); 81 | double idealRad = Math.atan2(ideal.y, ideal.x); 82 | 83 | if (Math.abs(currentRad - idealRad) > Math.PI) { 84 | if (currentRad < 0) { 85 | currentRad += Math.PI * 2; 86 | } 87 | if (idealRad < 0) { 88 | idealRad += Math.PI * 2; 89 | } 90 | } 91 | 92 | return idealRad - currentRad; 93 | } 94 | 95 | /** 96 | * Will always return a positive value <= Math.PI 97 | */ 98 | public static double angle(Vector2 a, Vector2 b) { 99 | return Math.abs(a.correctionAngle(b)); 100 | } 101 | 102 | @Override 103 | public String toString() { 104 | return String.format("(%s, %s)", x, y); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/rlbotexample/vector/Vector3.java: -------------------------------------------------------------------------------- 1 | package rlbotexample.vector; 2 | 3 | import com.google.flatbuffers.FlatBufferBuilder; 4 | 5 | /** 6 | * A simple 3d vector class with the most essential operations. 7 | * 8 | * This class is here for your convenience, it is NOT part of the framework. You can add to it as much 9 | * as you want, or delete it. 10 | */ 11 | public class Vector3 extends rlbot.vector.Vector3 { 12 | 13 | public Vector3(double x, double y, double z) { 14 | super((float) x, (float) y, (float) z); 15 | } 16 | 17 | public Vector3() { 18 | this(0, 0, 0); 19 | } 20 | 21 | public Vector3(rlbot.flat.Vector3 vec) { 22 | // Invert the X value so that the axes make more sense. 23 | this(-vec.x(), vec.y(), vec.z()); 24 | } 25 | 26 | public int toFlatbuffer(FlatBufferBuilder builder) { 27 | // Invert the X value again so that rlbot sees the format it expects. 28 | return rlbot.flat.Vector3.createVector3(builder, -x, y, z); 29 | } 30 | 31 | public Vector3 plus(Vector3 other) { 32 | return new Vector3(x + other.x, y + other.y, z + other.z); 33 | } 34 | 35 | public Vector3 minus(Vector3 other) { 36 | return new Vector3(x - other.x, y - other.y, z - other.z); 37 | } 38 | 39 | public Vector3 scaled(double scale) { 40 | return new Vector3(x * scale, y * scale, z * scale); 41 | } 42 | 43 | /** 44 | * If magnitude is negative, we will return a vector facing the opposite direction. 45 | */ 46 | public Vector3 scaledToMagnitude(double magnitude) { 47 | if (isZero()) { 48 | throw new IllegalStateException("Cannot scale up a vector with length zero!"); 49 | } 50 | double scaleRequired = magnitude / magnitude(); 51 | return scaled(scaleRequired); 52 | } 53 | 54 | public double distance(Vector3 other) { 55 | double xDiff = x - other.x; 56 | double yDiff = y - other.y; 57 | double zDiff = z - other.z; 58 | return Math.sqrt(xDiff * xDiff + yDiff * yDiff + zDiff * zDiff); 59 | } 60 | 61 | public double magnitude() { 62 | return Math.sqrt(magnitudeSquared()); 63 | } 64 | 65 | public double magnitudeSquared() { 66 | return x * x + y * y + z * z; 67 | } 68 | 69 | public Vector3 normalized() { 70 | 71 | if (isZero()) { 72 | throw new IllegalStateException("Cannot normalize a vector with length zero!"); 73 | } 74 | return this.scaled(1 / magnitude()); 75 | } 76 | 77 | public double dotProduct(Vector3 other) { 78 | return x * other.x + y * other.y + z * other.z; 79 | } 80 | 81 | public boolean isZero() { 82 | return x == 0 && y == 0 && z == 0; 83 | } 84 | 85 | public Vector2 flatten() { 86 | return new Vector2(x, y); 87 | } 88 | 89 | public double angle(Vector3 v) { 90 | double mag2 = magnitudeSquared(); 91 | double vmag2 = v.magnitudeSquared(); 92 | double dot = dotProduct(v); 93 | return Math.acos(dot / Math.sqrt(mag2 * vmag2)); 94 | } 95 | 96 | public Vector3 crossProduct(Vector3 v) { 97 | double tx = y * v.z - z * v.y; 98 | double ty = z * v.x - x * v.z; 99 | double tz = x * v.y - y * v.x; 100 | return new Vector3(tx, ty, tz); 101 | } 102 | 103 | @Override 104 | public String toString() { 105 | return String.format("(%s, %s, %s)", x, y, z); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/python/README_Tournament.md: -------------------------------------------------------------------------------- 1 | These instructions are intended for a tournament organizer. In theory, you're reading this because 2 | you've just extracted a zip file submitted to a tournament. If that's not your situation, go look at README.md 3 | instead. 4 | 5 | 1. Install Java 8 or newer. 6 | 2. Reference the included `.cfg` file as you would do with a plain python bot. 7 | 3. Before running the match, double click on the `.bat` file included in 8 | this submission. It should stay open and manage the java bot. 9 | 10 | 11 | You only need to run a single instance of the `.bat`, and it can handle all the bots in this zip 12 | in the whole match, even on opposite teams. 13 | 14 | 15 | Advanced: 16 | 17 | - It's fine to close and restart `.bat` while the framework is active. 18 | - If there is a port conflict, you can modify the port in the bot's python file. 19 | to use a different one. There's one here and one in the bin folder. 20 | -------------------------------------------------------------------------------- /src/main/python/javaExample.cfg: -------------------------------------------------------------------------------- 1 | [Locations] 2 | # Path to loadout config from runner 3 | looks_config = javaExampleAppearance.cfg 4 | 5 | # Path to module from runner 6 | python_file = javaExample.py 7 | 8 | # Name that will be displayed in game 9 | name = MyJavaExample 10 | 11 | [Bot Parameters] 12 | java_executable_path = ../RLBotJavaExample/bin/RLBotJavaExample.bat 13 | 14 | [Details] 15 | # These values are optional but useful metadata for helper programs 16 | # Name of the bot's creator/developer 17 | developer = The RLBot community 18 | 19 | # Short description of the bot 20 | description = This is a multi-line description 21 | of the official java example bot 22 | 23 | # Fun fact about the bot 24 | fun_fact = 25 | 26 | # Link to github repository 27 | github = https://github.com/RLBot/RLBotJavaExample 28 | 29 | # Programming language 30 | language = java -------------------------------------------------------------------------------- /src/main/python/javaExample.py: -------------------------------------------------------------------------------- 1 | from rlbot.agents.base_agent import BOT_CONFIG_AGENT_HEADER 2 | from rlbot.agents.executable_with_socket_agent import ExecutableWithSocketAgent 3 | from rlbot.parsing.custom_config import ConfigHeader, ConfigObject 4 | 5 | 6 | class JavaExample(ExecutableWithSocketAgent): 7 | def get_port(self) -> int: 8 | return 17357 9 | 10 | def load_config(self, config_header: ConfigHeader): 11 | self.executable_path = config_header.getpath('java_executable_path') 12 | self.logger.info("Java executable is configured as {}".format(self.executable_path)) 13 | 14 | @staticmethod 15 | def create_agent_configurations(config: ConfigObject): 16 | params = config.get_header(BOT_CONFIG_AGENT_HEADER) 17 | params.add_value('java_executable_path', str, default=None, 18 | description='Relative path to the executable that runs java.') 19 | -------------------------------------------------------------------------------- /src/main/python/javaExampleAppearance.cfg: -------------------------------------------------------------------------------- 1 | [Bot Loadout] 2 | team_color_id = 60 3 | custom_color_id = 0 4 | car_id = 23 5 | decal_id = 0 6 | wheels_id = 1565 7 | boost_id = 35 8 | antenna_id = 0 9 | hat_id = 0 10 | paint_finish_id = 1681 11 | custom_finish_id = 1681 12 | engine_audio_id = 0 13 | trails_id = 3220 14 | goal_explosion_id = 3018 15 | 16 | [Bot Loadout Orange] 17 | team_color_id = 3 18 | custom_color_id = 0 19 | car_id = 23 20 | decal_id = 0 21 | wheels_id = 1565 22 | boost_id = 35 23 | antenna_id = 0 24 | hat_id = 0 25 | paint_finish_id = 1681 26 | custom_finish_id = 1681 27 | engine_audio_id = 0 28 | trails_id = 3220 29 | goal_explosion_id = 3018 30 | 31 | [Bot Paint Blue] 32 | car_paint_id = 12 33 | decal_paint_id = 0 34 | wheels_paint_id = 7 35 | boost_paint_id = 7 36 | antenna_paint_id = 0 37 | hat_paint_id = 0 38 | trails_paint_id = 2 39 | goal_explosion_paint_id = 0 40 | 41 | [Bot Paint Orange] 42 | car_paint_id = 12 43 | decal_paint_id = 0 44 | wheels_paint_id = 14 45 | boost_paint_id = 14 46 | antenna_paint_id = 0 47 | hat_paint_id = 0 48 | trails_paint_id = 14 49 | goal_explosion_paint_id = 0 50 | -------------------------------------------------------------------------------- /src/main/resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RLBot/RLBotJavaExample/ffed34b232f5349fb81dd05c66fd63839ce8f5cd/src/main/resources/icon.png --------------------------------------------------------------------------------