diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b92c51f3..3aa43ee0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,14 +18,12 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 - - name: Set up JDK 1.8 + - name: Set up JDK 17 uses: actions/setup-java@v3 with: distribution: temurin - java-version: 8 + java-version: 17 - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build - run: | - cd scripts - ./build.sh + run: ./gradlew binDir macOSApp diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 9510c47c..0d5c5c35 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -15,11 +15,11 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 - - name: Set up JDK 1.8 + - name: Set up JDK 17 uses: actions/setup-java@v3 with: distribution: temurin - java-version: 8 + java-version: 17 - name: Build RVTester (Java) run: make -C examples/java - name: Build RVTester (C++) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7ea8b6b9..4440977e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,21 +12,21 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 - - name: Set up JDK 1.8 + - name: Set up JDK 17 uses: actions/setup-java@v3 with: distribution: temurin - java-version: 8 + java-version: 17 - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build run: | - cd scripts - ./build.sh - cd .. + ./gradlew binDir macOSApp tar czf RoboViz.tar.gz bin/ + mv build/macos/RoboViz.app . + zip -r -9 RoboViz-macOS.zip RoboViz.app - name: Create Release id: create-release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token - run: gh release create ${{ github.ref }} --title ${{ github.ref }} ./RoboViz.tar.gz + run: gh release create ${{ github.ref }} --title ${{ github.ref_name }} ./RoboViz.tar.gz ./RoboViz-macOS.zip diff --git a/README.md b/README.md index 957a142f..a57ad3b9 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ RoboViz is a monitor and visualization tool for the [RoboCup 3D Soccer Simulation League](https://ssim.robocup.org/3d-simulation/). This is a fork of the original version by Justin Stoecker [hosted on SourceForge](https://sourceforge.net/projects/rcroboviz/). Compared to the original version, major improvements have been made as can be seen in detail in the [changelog](CHANGELOG.md). -Java 1.8 is required to build and run RoboViz. Pre-built binaries for Windows, Linux and Mac are available [here](https://github.com/magmaOffenburg/RoboViz/releases). You can also build it from source using [`scripts/build.sh`](scripts/build.sh) or [`scripts/build.bat`](scripts/build.bat). +Java 17 is required to build and run RoboViz. Pre-built binaries for Windows, Linux and Mac are available [here](https://github.com/magmaOffenburg/RoboViz/releases). You can also build it from source using [`scripts/build.sh`](scripts/build.sh) or [`scripts/build.bat`](scripts/build.bat). ![](images/video.gif) diff --git a/build.gradle b/build.gradle deleted file mode 100644 index d8aa7e47..00000000 --- a/build.gradle +++ /dev/null @@ -1,59 +0,0 @@ -plugins { - id 'java' - id 'application' - id 'com.github.johnrengelman.shadow' version '8.1.1' - id 'org.jetbrains.kotlin.jvm' version '1.8.21' -} - -group 'org.magmaoffenburg.roboviz' -version '1.8.5' -mainClassName = 'org.magmaoffenburg.roboviz.MainKt' - -ext { - jogl_version = '2.3.2' - log4j_version = '2.20.0' -} - -subprojects { - apply plugin: 'java' - - repositories { - maven { - url = 'https://repo.maven.apache.org/maven2' - } - } - - sourceCompatibility = '1.8' - targetCompatibility = '1.8' -} - -compileJava { - sourceCompatibility = '1.8' - targetCompatibility = '1.8' -} - -compileKotlin { - kotlinOptions.jvmTarget = '1.8' -} - -repositories { - mavenCentral() -} - -dependencies { - implementation 'org.jetbrains.kotlin:kotlin-stdlib' - implementation 'org.jetbrains.kotlin:kotlin-reflect' - implementation 'com.github.weisj:darklaf-core:3.0.2' - implementation "org.jogamp.jogl:jogl-all-main:$jogl_version" - implementation "org.jogamp.gluegen:gluegen-rt-main:$jogl_version" - implementation 'org.apache.commons:commons-compress:1.23.0' - implementation 'org.apache.logging.log4j:log4j-api-kotlin:1.2.0' - implementation "org.apache.logging.log4j:log4j-api:$log4j_version" - implementation "org.apache.logging.log4j:log4j-core:$log4j_version" - - implementation project(':jsgl') -} - -shadowJar { - archiveFileName = 'RoboViz.jar' -} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 00000000..bff17d45 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,124 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import org.apache.tools.ant.filters.ReplaceTokens + +plugins { + java + application + id("com.github.johnrengelman.shadow") version "8.1.1" + kotlin("jvm") version "1.9.10" +} + +group = "org.magmaoffenburg.roboviz" +version = "2.0.0-SNAPSHOT" +application { + mainClass.set("org.magmaoffenburg.roboviz.MainKt") + applicationDefaultJvmArgs = listOf("--add-exports=java.desktop/sun.awt=ALL-UNNAMED") +} + +val javaVersion by extra(17) +val joglVersion by extra("2.4.0") +val log4jVersion by extra("2.20.0") + +subprojects { + apply(plugin = "java") + + repositories { + mavenCentral() + maven(url = "https://www.jogamp.org/deployment/maven/") + } + + java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(javaVersion)) + } + } +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(javaVersion)) + } +} + +kotlin { + jvmToolchain(javaVersion) +} + +repositories { + mavenCentral() + maven(url = "https://www.jogamp.org/deployment/maven/") +} + +dependencies { + implementation("org.jetbrains.kotlin:kotlin-stdlib") + implementation("org.jetbrains.kotlin:kotlin-reflect") + implementation("com.github.weisj:darklaf-core:3.0.2") + implementation("org.jogamp.jogl:jogl-all-main:$joglVersion") + implementation("org.jogamp.gluegen:gluegen-rt-main:$joglVersion") + implementation("org.apache.commons:commons-compress:1.24.0") + implementation("org.apache.logging.log4j:log4j-api-kotlin:1.2.0") + implementation("org.apache.logging.log4j:log4j-api:$log4jVersion") + implementation("org.apache.logging.log4j:log4j-core:$log4jVersion") + + implementation(project(":jsgl")) +} + +tasks.jar { + manifest { + // We need to set Multi-Release to true so that log4j can determine the correct class names + attributes("Multi-Release" to "true") + } +} + +tasks.withType { + archiveFileName.set("${project.name}.jar") +} + +tasks.register("binDir") { + dependsOn(tasks.withType()) + + val binPath = layout.projectDirectory.dir("bin") + from( + layout.buildDirectory.file("libs/${project.name}.jar"), + layout.projectDirectory.file("config.txt"), + layout.projectDirectory.file("scripts/roboviz.sh"), + layout.projectDirectory.file("scripts/roboviz.bat"), + layout.projectDirectory.file("LICENSE.md"), + layout.projectDirectory.file("NOTICE.md"), + layout.projectDirectory.file("CHANGELOG.md") + ) + into(binPath) +} + +// Creates an application bundle for macOS +tasks.register("macOSApp") { + dependsOn(tasks.withType()) + + doLast { + copy { + from(layout.projectDirectory.file("macos/Info.plist")) + into(layout.buildDirectory.dir("macos/${project.name}.app/Contents")) + filter(ReplaceTokens::class, "tokens" to mapOf( + "CFBundleExecutable" to "launcher", + "CFBundleIconFile" to "icon", + "CFBundleIdentifier" to group.toString(), + "CFBundleName" to project.name, + "CFBundleShortVersionString" to version.toString(), + "CFBundleVersion" to version.toString(), + )) + } + copy { + from(layout.projectDirectory.file("scripts/roboviz.sh")) + into(layout.buildDirectory.dir("macos/${project.name}.app/Contents/MacOS")) + rename(".*", "launcher") + } + copy { + from(layout.projectDirectory.file("macos/icon.icns")) + into(layout.buildDirectory.dir("macos/${project.name}.app/Contents/Resources")) + } + copy { + from(layout.buildDirectory.file("libs/${project.name}.jar")) + into(layout.buildDirectory.dir("macos/${project.name}.app/Contents/MacOS")) + } + } +} diff --git a/jsgl/build.gradle b/jsgl/build.gradle deleted file mode 100644 index 6d16439a..00000000 --- a/jsgl/build.gradle +++ /dev/null @@ -1,8 +0,0 @@ -dependencies { - implementation "org.jogamp.gluegen:gluegen-rt-main:$jogl_version" - implementation "org.jogamp.jogl:jogl-all-main:$jogl_version" - implementation "org.apache.logging.log4j:log4j-api:$log4j_version" -} - -group = 'magmaOffenburg' -description = 'jsgl' diff --git a/jsgl/build.gradle.kts b/jsgl/build.gradle.kts new file mode 100644 index 00000000..33820e4a --- /dev/null +++ b/jsgl/build.gradle.kts @@ -0,0 +1,11 @@ +val joglVersion: String by rootProject.extra +val log4jVersion: String by rootProject.extra + +dependencies { + implementation("org.jogamp.gluegen:gluegen-rt-main:$joglVersion") + implementation("org.jogamp.jogl:jogl-all-main:$joglVersion") + implementation("org.apache.logging.log4j:log4j-api:$log4jVersion") +} + +group = "magmaOffenburg" +description = "jsgl" diff --git a/jsgl/src/main/java/jsgl/jogl/Shader.java b/jsgl/src/main/java/jsgl/jogl/Shader.java index af4c6927..624dd3d9 100644 --- a/jsgl/src/main/java/jsgl/jogl/Shader.java +++ b/jsgl/src/main/java/jsgl/jogl/Shader.java @@ -157,10 +157,10 @@ private static boolean checkCompileStatus(GL2 gl, int id, String file) @Override public void dispose(GL gl) { - if (gl instanceof GL2) - gl.getGL2().glDeleteShader(id); - else if (gl instanceof GL3) - gl.getGL3().glDeleteShader(id); + if (gl instanceof GL2 gl2) + gl2.glDeleteShader(id); + else if (gl instanceof GL3 gl3) + gl3.glDeleteShader(id); disposed = true; } diff --git a/jsgl/src/main/java/jsgl/jogl/util/TessCallback.java b/jsgl/src/main/java/jsgl/jogl/util/TessCallback.java index ba1f1b61..8e7bc58c 100644 --- a/jsgl/src/main/java/jsgl/jogl/util/TessCallback.java +++ b/jsgl/src/main/java/jsgl/jogl/util/TessCallback.java @@ -57,9 +57,7 @@ public void end() @Override public void vertex(Object vertexData) { - double[] pointer; - if (vertexData instanceof double[]) { - pointer = (double[]) vertexData; + if (vertexData instanceof double[] pointer) { if (pointer.length == 6) gl.glColor3dv(pointer, 3); gl.glVertex3dv(pointer, 0); diff --git a/macos/Info.plist b/macos/Info.plist new file mode 100644 index 00000000..781698ec --- /dev/null +++ b/macos/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + @CFBundleExecutable@ + CFBundleIconFile + @CFBundleIconFile@ + CFBundleIdentifier + @CFBundleIdentifier@ + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + @CFBundleName@ + CFBundlePackageType + APPL + CFBundleShortVersionString + @CFBundleShortVersionString@ + CFBundleVersion + @CFBundleVersion@ + LSApplicationCategoryType + public.app-category.developer-tools + NSHighResolutionCapable + + NSHumanReadableCopyright + Copyright 2011-2023 The RoboViz authors + NSPrincipalClass + NSApplication + + diff --git a/macos/icon.icns b/macos/icon.icns new file mode 100644 index 00000000..b40e1f35 Binary files /dev/null and b/macos/icon.icns differ diff --git a/scripts/build.bat b/scripts/build.bat index db1a9d1b..e498a7e9 100644 --- a/scripts/build.bat +++ b/scripts/build.bat @@ -1,20 +1,6 @@ cd .. -set BIN=bin\ - -IF EXIST %BIN% GOTO COMPILE -mkdir %BIN% - -:COMPILE -call gradlew.bat clean shadowJar - -copy build\libs\RoboViz.jar %BIN%\ -copy config.txt %BIN%\ -copy scripts\roboviz.sh %BIN%\ -copy scripts\roboviz.bat %BIN%\ -copy LICENSE.md %BIN%\ -copy NOTICE.md %BIN%\ -copy CHANGELOG.md %BIN%\ +call gradlew.bat clean binDir gradlew.bat clean pause diff --git a/scripts/build.sh b/scripts/build.sh index d7f9f765..10e90e31 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -1,20 +1,6 @@ -#!/bin/bash +#!/bin/sh -BIN=../bin +../gradlew -p .. clean binDir -# create a bin folder in the RoboViz root directory -mkdir -p $BIN - -../gradlew -p .. clean shadowJar - -# copy over resources and libraries to bin folder -cp ../build/libs/RoboViz.jar $BIN/ -cp ../config.txt $BIN/ -cp ../scripts/roboviz.sh $BIN/ -cp ../scripts/roboviz.bat $BIN/ -cp ../LICENSE.md $BIN/ -cp ../NOTICE.md $BIN/ -cp ../CHANGELOG.md $BIN/ - -# clean up the gradle build directorys +# clean up the gradle build directories ../gradlew -p .. clean diff --git a/scripts/roboviz.bat b/scripts/roboviz.bat index 00052bf4..c5ff6e42 100644 --- a/scripts/roboviz.bat +++ b/scripts/roboviz.bat @@ -2,6 +2,6 @@ set prev=%cd% cd /D "%~dp0" -java -jar RoboViz.jar %* +java --add-exports=java.desktop/sun.awt=ALL-UNNAMED -jar RoboViz.jar %* cd %prev% diff --git a/scripts/roboviz.sh b/scripts/roboviz.sh index 368f2e5a..e7a09873 100755 --- a/scripts/roboviz.sh +++ b/scripts/roboviz.sh @@ -1,28 +1,15 @@ -#!/bin/bash +#!/bin/sh -args="" -while [ $# -gt 0 ] -do - if [[ $1 == --logFile=* ]]; - then - logFileName=${1#*=} - DIR_LOGFILE="$( eval cd "$( dirname "$logFileName" )" && pwd )" - LOGFILE=$DIR_LOGFILE/$(basename $logFileName) - args="$args --logFile=$LOGFILE" - else - args="$args $1" - fi - shift 1 -done +DIR="$( cd "$( dirname "$0" )" && pwd )" -set -- $args - -DIR="$( cd "$( dirname "$0" )" && pwd )" -cd $DIR - -if [[ `uname -s` == "Darwin" ]]; +VM_ARGS="-Xmx512m --add-exports=java.desktop/sun.awt=ALL-UNNAMED" +if [ `uname -s` = "Darwin" ]; then - java -Xmx512m -Xdock:name="RoboViz" -jar RoboViz.jar "$@" -else - java -Xmx512m -jar RoboViz.jar "$@" + VM_ARGS="$VM_ARGS -Xdock:name=RoboViz" + if [ -f "$DIR/../Resources/icon.icns" ]; + then + VM_ARGS="$VM_ARGS -Xdock:icon=$DIR/../Resources/icon.icns" + fi fi + +java $VM_ARGS -jar "$DIR/RoboViz.jar" "$@" diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index e7d1fbf0..00000000 --- a/settings.gradle +++ /dev/null @@ -1,2 +0,0 @@ -rootProject.name = 'RoboViz' -include(':jsgl') diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 00000000..9404f11d --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,2 @@ +rootProject.name = "RoboViz" +include(":jsgl") diff --git a/src/main/java/rv/comm/rcssserver/GameState.java b/src/main/java/rv/comm/rcssserver/GameState.java index 1555a6e7..db8ad449 100644 --- a/src/main/java/rv/comm/rcssserver/GameState.java +++ b/src/main/java/rv/comm/rcssserver/GameState.java @@ -92,16 +92,8 @@ public static class Foul public long receivedTime; } - public static class HistoryItem + public record HistoryItem(float time, String playMode) { - public final float time; - public final String playMode; - - public HistoryItem(float time, String playMode) - { - this.time = time; - this.playMode = playMode; - } } // Measurements and Rules diff --git a/src/main/java/rv/comm/rcssserver/LogAnalyzerThread.java b/src/main/java/rv/comm/rcssserver/LogAnalyzerThread.java index 5c45348d..97805bdd 100644 --- a/src/main/java/rv/comm/rcssserver/LogAnalyzerThread.java +++ b/src/main/java/rv/comm/rcssserver/LogAnalyzerThread.java @@ -10,18 +10,8 @@ public class LogAnalyzerThread extends Thread { - public static class Goal + public record Goal(int frame, int viewFrame, int scoringTeam) { - public final int frame; - public final int viewFrame; - public final int scoringTeam; - - public Goal(int frame, int viewFrame, int scoringTeam) - { - this.frame = frame; - this.viewFrame = viewFrame; - this.scoringTeam = scoringTeam; - } } public interface ResultCallback diff --git a/src/main/java/rv/comm/rcssserver/LogPlayer.java b/src/main/java/rv/comm/rcssserver/LogPlayer.java index fa9a1567..947bf021 100644 --- a/src/main/java/rv/comm/rcssserver/LogPlayer.java +++ b/src/main/java/rv/comm/rcssserver/LogPlayer.java @@ -215,8 +215,8 @@ public void stepBackwardGoal() int relativeFrame = getDesiredFrame() - getGoalStepThresholdFrames(); int closestFrame = -1; for (Goal goal : goals) { - if (goal.viewFrame < relativeFrame && goal.viewFrame > closestFrame) { - closestFrame = goal.viewFrame; + if (goal.viewFrame() < relativeFrame && goal.viewFrame() > closestFrame) { + closestFrame = goal.viewFrame(); } } if (closestFrame != -1) { @@ -229,8 +229,8 @@ public void stepForwardGoal() int relativeFrame = getDesiredFrame() + getGoalStepThresholdFrames(); int closestFrame = Integer.MAX_VALUE; for (Goal goal : goals) { - if (goal.viewFrame > relativeFrame && goal.viewFrame < closestFrame) { - closestFrame = goal.viewFrame; + if (goal.viewFrame() > relativeFrame && goal.viewFrame() < closestFrame) { + closestFrame = goal.viewFrame(); } } if (closestFrame != Integer.MAX_VALUE) { @@ -257,7 +257,7 @@ public boolean hasPreviousGoal() return false; for (Goal goal : goals) { - if (getDesiredFrame() - getGoalStepThresholdFrames() > goal.viewFrame) + if (getDesiredFrame() - getGoalStepThresholdFrames() > goal.viewFrame()) return true; } return false; @@ -269,7 +269,7 @@ public boolean hasNextGoal() return false; for (Goal goal : goals) { - if (getDesiredFrame() + getGoalStepThresholdFrames() < goal.viewFrame) + if (getDesiredFrame() + getGoalStepThresholdFrames() < goal.viewFrame()) return true; } return false; @@ -297,7 +297,7 @@ private Integer getPreviousGoalNumber() { Integer previousGoalNumber = null; for (int i = 0; i < goals.size(); i++) { - if (getDesiredFrame() - getGoalStepThresholdFrames() > goals.get(i).viewFrame) { + if (getDesiredFrame() - getGoalStepThresholdFrames() > goals.get(i).viewFrame()) { previousGoalNumber = i + 1; } } @@ -308,7 +308,7 @@ private Integer getNextGoalNumber() { Integer nextGoalNumber = null; for (int i = 0; i < goals.size(); i++) { - if (getDesiredFrame() + getGoalStepThresholdFrames() < goals.get(i).viewFrame) { + if (getDesiredFrame() + getGoalStepThresholdFrames() < goals.get(i).viewFrame()) { nextGoalNumber = i + 1; break; } @@ -435,7 +435,7 @@ public void goalFound(Goal goal) return; } goals.add(goal); - analyzedFrames = goal.frame; + analyzedFrames = goal.frame(); stateChanged(); } diff --git a/src/main/java/rv/comm/rcssserver/SExp.java b/src/main/java/rv/comm/rcssserver/SExp.java index d7fce05d..b3e9b24f 100644 --- a/src/main/java/rv/comm/rcssserver/SExp.java +++ b/src/main/java/rv/comm/rcssserver/SExp.java @@ -81,8 +81,7 @@ public void printMultiLine() private void printExpression(int depth, StringBuilder sb) { // indentation - for (int i = 0; i < depth; i++) - sb.append("--"); + sb.append("--".repeat(Math.max(0, depth))); // print atoms sb.append("["); diff --git a/src/main/java/rv/comm/rcssserver/scenegraph/SceneGraph.java b/src/main/java/rv/comm/rcssserver/scenegraph/SceneGraph.java index b3f72e72..5c792ba3 100644 --- a/src/main/java/rv/comm/rcssserver/scenegraph/SceneGraph.java +++ b/src/main/java/rv/comm/rcssserver/scenegraph/SceneGraph.java @@ -83,8 +83,7 @@ public StaticMeshNode findStaticMeshNode(String name) public StaticMeshNode findStaticMeshNode(Node node, String[] materials) { // check if current node is the node we're looking for - if (node instanceof StaticMeshNode) { - StaticMeshNode smn = (StaticMeshNode) node; + if (node instanceof StaticMeshNode smn) { String[] nodeMats = smn.getMaterials(); // make sure each material in the list is in the node's materials diff --git a/src/main/java/rv/content/ContentManager.java b/src/main/java/rv/content/ContentManager.java index 80a594c1..021770b9 100644 --- a/src/main/java/rv/content/ContentManager.java +++ b/src/main/java/rv/content/ContentManager.java @@ -241,8 +241,7 @@ public Mesh loadMesh(String name) // this is necessary for the shader to blend meshes that have textures // for some parts and materials for others for (MeshPart p : mesh.getParts()) { - if (p.getMaterial() instanceof ObjMaterial) { - ObjMaterial mat = (ObjMaterial) p.getMaterial(); + if (p.getMaterial() instanceof ObjMaterial mat) { if (mat.getTexture() == null) mat.setTexture(whiteTexture, false); } @@ -277,8 +276,7 @@ public void newSceneGraph(SceneGraph sg) private void checkForMeshes(Node node) { - if (node instanceof StaticMeshNode) { - StaticMeshNode meshNode = (StaticMeshNode) node; + if (node instanceof StaticMeshNode meshNode) { getModel(meshNode.getName()); } diff --git a/src/main/java/rv/content/Model.java b/src/main/java/rv/content/Model.java index 4f250f77..dac74626 100644 --- a/src/main/java/rv/content/Model.java +++ b/src/main/java/rv/content/Model.java @@ -92,8 +92,7 @@ public void readMeshData(ContentManager cm) // this is necessary for the shader to blend meshes that have // textures for some parts and only color materials for others for (MeshPart p : mesh.getParts()) { - if (p.getMaterial() instanceof ObjMaterial) { - ObjMaterial mat = (ObjMaterial) p.getMaterial(); + if (p.getMaterial() instanceof ObjMaterial mat) { if (mat.getTexture() == null && mat.getTextureSource() == null) { mat.setTexture(cm.getWhiteTexture(), false); } diff --git a/src/main/java/rv/ui/CameraSetting.java b/src/main/java/rv/ui/CameraSetting.java index 145911cd..7c0c461d 100644 --- a/src/main/java/rv/ui/CameraSetting.java +++ b/src/main/java/rv/ui/CameraSetting.java @@ -19,24 +19,6 @@ import jsgl.math.vector.Vec2f; import jsgl.math.vector.Vec3f; -public class CameraSetting +public record CameraSetting(Vec3f position, Vec2f rotation) { - private final Vec3f position; - private final Vec2f rotation; - - public Vec3f getPosition() - { - return position; - } - - public Vec2f getRotation() - { - return rotation; - } - - public CameraSetting(Vec3f position, Vec2f rotation) - { - this.position = position; - this.rotation = rotation; - } } diff --git a/src/main/java/rv/ui/screens/GameStateOverlay.java b/src/main/java/rv/ui/screens/GameStateOverlay.java index b5165a6f..7469657b 100644 --- a/src/main/java/rv/ui/screens/GameStateOverlay.java +++ b/src/main/java/rv/ui/screens/GameStateOverlay.java @@ -139,13 +139,13 @@ void render(GL2 gl, GameState gs, int screenW, int screenH) } } else if (gs.getPlayModeHistory().size() > 1 && gs.getPlayMode().equals(GameState.PLAY_ON)) { GameState.HistoryItem item = gs.getPlayModeHistory().get(gs.getPlayModeHistory().size() - 2); - if (item.playMode.equals(GameState.PASS_LEFT) || item.playMode.equals(GameState.PASS_RIGHT)) { - float timeOfLastPassEnd = gs.getPlayModeHistory().get(gs.getPlayModeHistory().size() - 1).time; + if (item.playMode().equals(GameState.PASS_LEFT) || item.playMode().equals(GameState.PASS_RIGHT)) { + float timeOfLastPassEnd = gs.getPlayModeHistory().get(gs.getPlayModeHistory().size() - 1).time(); float timePassed = gs.getTime() - timeOfLastPassEnd; if (timePassed >= 0) { final float COOLDOWN = 10; passModeWaitToScoreTime = COOLDOWN - timePassed; - if (item.playMode.equals(GameState.PASS_LEFT)) { + if (item.playMode().equals(GameState.PASS_LEFT)) { leftTeamPassModeWaitToScore = true; } } diff --git a/src/main/java/rv/ui/screens/LiveGameScreen.java b/src/main/java/rv/ui/screens/LiveGameScreen.java index bba53bf5..a0d10dfd 100644 --- a/src/main/java/rv/ui/screens/LiveGameScreen.java +++ b/src/main/java/rv/ui/screens/LiveGameScreen.java @@ -126,8 +126,7 @@ private void moveSelection(Vec3f pos) if (selected instanceof Ball) { serverPos.z = Renderer.world.getGameState().getBallRadius(); Renderer.netManager.getServer().moveBall(serverPos); - } else if (selected instanceof Agent) { - Agent a = (Agent) selected; + } else if (selected instanceof Agent a) { boolean leftTeam = a.getTeam().getID() == Team.LEFT; Renderer.netManager.getServer().moveAgent(serverPos, leftTeam, a.getID()); } diff --git a/src/main/java/rv/ui/screens/LogfileModeScreen.java b/src/main/java/rv/ui/screens/LogfileModeScreen.java index 5296939b..946650ba 100644 --- a/src/main/java/rv/ui/screens/LogfileModeScreen.java +++ b/src/main/java/rv/ui/screens/LogfileModeScreen.java @@ -106,8 +106,8 @@ public void gsPlayStateChanged(GameState gs) int frame = player.getFrame(); if (player.getAnalyzedFrames() > frame) { for (Goal goal : player.getGoals()) { - if (goal.frame == frame) { - addTeamScoredOverlay(gs, goal.scoringTeam); + if (goal.frame() == frame) { + addTeamScoredOverlay(gs, goal.scoringTeam()); } } } else { diff --git a/src/main/java/rv/ui/screens/ViewerScreenBase.java b/src/main/java/rv/ui/screens/ViewerScreenBase.java index c1217f2e..ea45a0c2 100644 --- a/src/main/java/rv/ui/screens/ViewerScreenBase.java +++ b/src/main/java/rv/ui/screens/ViewerScreenBase.java @@ -270,8 +270,8 @@ private Agent getTrackedPlayer() result = rightTeam.get(0); ISelectable selection = Renderer.world.getSelectedObject(); - if (selection instanceof Agent) - result = (Agent) selection; + if (selection instanceof Agent agent) + result = agent; // players usually beam right after they connect, avoid camera jumps if (result != null && result.getAge() < 50) { diff --git a/src/main/java/rv/ui/view/SimsparkController.java b/src/main/java/rv/ui/view/SimsparkController.java index 7c9a1d19..7cc32187 100644 --- a/src/main/java/rv/ui/view/SimsparkController.java +++ b/src/main/java/rv/ui/view/SimsparkController.java @@ -118,8 +118,8 @@ private void setCamera(int i) return; FPCamera camera = CameraController.fpCamera; - camera.setPosition(cameras[i].getPosition().clone()); - camera.setRotation(cameras[i].getRotation().clone()); + camera.setPosition(cameras[i].position().clone()); + camera.setRotation(cameras[i].rotation().clone()); } /** Initialize saved camera positions */ diff --git a/src/main/java/rv/world/WorldModel.java b/src/main/java/rv/world/WorldModel.java index 5684a293..4acea36e 100644 --- a/src/main/java/rv/world/WorldModel.java +++ b/src/main/java/rv/world/WorldModel.java @@ -143,8 +143,7 @@ public synchronized void setSceneGraph(SceneGraph sceneGraph) sgi.sceneGraphChanged(sceneGraph); } - if (selectedObject instanceof Agent) { - Agent agent = (Agent) selectedObject; + if (selectedObject instanceof Agent agent) { Agent newSelection; if (agent.getTeam().getID() == Team.LEFT) { newSelection = getLeftTeam().getAgentByID(agent.getID()); diff --git a/src/main/kotlin/org/magmaoffenburg/roboviz/Main.kt b/src/main/kotlin/org/magmaoffenburg/roboviz/Main.kt index 7d0c64cc..d30ac328 100644 --- a/src/main/kotlin/org/magmaoffenburg/roboviz/Main.kt +++ b/src/main/kotlin/org/magmaoffenburg/roboviz/Main.kt @@ -13,7 +13,7 @@ import java.awt.EventQueue class Main { companion object { const val name = "RoboViz" - const val version = "1.8.5" + const val version = "2.0.0-SNAPSHOT" var mode = Mode.LIVE lateinit var config: Config // TODO maybe Config should be a Object diff --git a/src/main/kotlin/org/magmaoffenburg/roboviz/configuration/Config.kt b/src/main/kotlin/org/magmaoffenburg/roboviz/configuration/Config.kt index 23104e53..9317103b 100644 --- a/src/main/kotlin/org/magmaoffenburg/roboviz/configuration/Config.kt +++ b/src/main/kotlin/org/magmaoffenburg/roboviz/configuration/Config.kt @@ -19,13 +19,15 @@ class Config(args: Array) { private val globalPath = System.getProperty("user.home") + "/.roboviz/config.txt" private val parser = ConfigParser() - private val filePath = if (File(globalPath).exists()) globalPath else localPath + val filePath = if (File(globalPath).exists() || !File(localPath).exists()) globalPath else localPath private val configChangeListeners = arrayListOf() init { parser.parseArgs(args) - parser.parseFile(filePath) + if (filePathExists()) { + parser.parseFile(filePath) + } try { read() @@ -34,24 +36,26 @@ class Config(args: Array) { } } + fun filePathExists() = File(filePath).exists() + object General { var recordLogs = false var logfileDirectory = "" var logReplayFile = "" - var lookAndFeel = "javax.swing.plaf.nimbus.NimbusLookAndFeel" + var lookAndFeel = "system" var drawingFilter = ".*" } object Graphics { - var useBloom = false - var usePhong = false - var useShadows = false - var useSoftShadows = false + var useBloom = true + var usePhong = true + var useShadows = true + var useSoftShadows = true var shadowResolution = 1024 var useStereo = false - var useVsync = true - var useFsaa = false - var fsaaSamples = 4 + var useVsync = false + var useFsaa = true + var fsaaSamples = 8 var targetFPS = 60 var firstPersonFOV = 120 var thirdPersonFOV = 80 @@ -85,9 +89,9 @@ class Config(args: Array) { object OverlayVisibility { var serverSpeed = true var foulOverlay = true - var fieldOverlay = false - var numberOfPlayers = false - var playerIDs = false + var fieldOverlay = true + var numberOfPlayers = true + var playerIDs = true } object TeamColors { @@ -198,9 +202,10 @@ class Config(args: Array) { } /** - * set parser list to object variable values + * set parser list to object variable values and write config to disk + * @param initConfig whether to initialize the config file if it does not exist yet */ - fun write() { + fun write(initConfig: Boolean = false) { // general parser.setValue("Record Logfiles", General.recordLogs.toString()) parser.setValue("Logfile Directory", General.logfileDirectory) @@ -251,12 +256,13 @@ class Config(args: Array) { "Team Color", TeamColors.byTeamNames.map { Pair(it.key, "0x${Integer.toHexString(it.value.rgb and 0xFFFFFF)}") }) - try { - parser.writeFile(filePath) - } catch (e: ArrayIndexOutOfBoundsException) { - logger.error("Error while trying to save the config.", e) + if (initConfig || filePathExists()) { + try { + parser.writeFile(filePath) + } catch (e: ArrayIndexOutOfBoundsException) { + logger.error("Error while trying to save the config.", e) + } } - } fun configChanged() { diff --git a/src/main/kotlin/org/magmaoffenburg/roboviz/configuration/ConfigParser.kt b/src/main/kotlin/org/magmaoffenburg/roboviz/configuration/ConfigParser.kt index cea1b0e3..d0334a93 100644 --- a/src/main/kotlin/org/magmaoffenburg/roboviz/configuration/ConfigParser.kt +++ b/src/main/kotlin/org/magmaoffenburg/roboviz/configuration/ConfigParser.kt @@ -2,6 +2,7 @@ package org.magmaoffenburg.roboviz.configuration import org.apache.logging.log4j.kotlin.logger import java.io.File +import java.io.FileNotFoundException class ConfigParser { private val logger = logger() @@ -66,7 +67,12 @@ class ConfigParser { val configFile = File(path) // read raw file - val rawFileList = configFile.readLines().toMutableList() + val rawFileList = try { + configFile.readLines().toMutableList() + } catch (e: FileNotFoundException) { + // File doesn't exist yet + mutableListOf() + } val matchKey = { line: String, key: String -> line.substringBefore(":").trim() == key } @@ -85,7 +91,14 @@ class ConfigParser { val firstIndex = rawFileList.indexOfFirst { matchKey(it, pairList.key) } rawFileList.removeIf { matchKey(it, pairList.key) } pairList.value.forEachIndexed { i, pair -> - rawFileList.add(firstIndex + i, "${pairList.key.padEnd(20)} : ${pair.first}:${pair.second}") + val updatedLine = "${pairList.key.padEnd(20)} : ${pair.first}:${pair.second}" + if (firstIndex == -1) { + // Key not yet present in config file + // Append new entries at the bottom of the file + rawFileList.add(updatedLine) + } else { + rawFileList.add(firstIndex + i, updatedLine) + } } } @@ -95,6 +108,7 @@ class ConfigParser { sb.append(line) sb.append("\n") } + configFile.absoluteFile.parentFile.mkdir() configFile.writeText(sb.toString()) } diff --git a/src/main/kotlin/org/magmaoffenburg/roboviz/gui/windows/config/ConfigWindow.kt b/src/main/kotlin/org/magmaoffenburg/roboviz/gui/windows/config/ConfigWindow.kt index 8846cead..cef32580 100644 --- a/src/main/kotlin/org/magmaoffenburg/roboviz/gui/windows/config/ConfigWindow.kt +++ b/src/main/kotlin/org/magmaoffenburg/roboviz/gui/windows/config/ConfigWindow.kt @@ -1,14 +1,17 @@ package org.magmaoffenburg.roboviz.gui.windows.config import org.magmaoffenburg.roboviz.Main +import org.magmaoffenburg.roboviz.util.ConfirmResult import org.magmaoffenburg.roboviz.util.SwingUtils import java.awt.BorderLayout import java.awt.Dimension import java.awt.GridLayout import java.awt.Point +import java.io.File import javax.imageio.ImageIO import javax.swing.JButton import javax.swing.JFrame +import javax.swing.JOptionPane import javax.swing.JPanel import javax.swing.JTabbedPane @@ -42,7 +45,20 @@ object ConfigWindow : JFrame() { } val saveButton = JButton("Save and Close") saveButton.addActionListener { - Main.config.write() // save to the config file + var initConfig = false + if (!Main.config.filePathExists()) { + val result = JOptionPane.showConfirmDialog( + ConfigWindow, + "A configuration file does not exist yet. Do you wish to create one at ${File(Main.config.filePath).absolutePath}?" + ).let { ConfirmResult.fromInt(it) } + initConfig = when (result) { + ConfirmResult.YES -> true + ConfirmResult.NO -> false + ConfirmResult.CANCEL -> return@addActionListener + } + } + + Main.config.write(initConfig) // save to the config file Main.config.configChanged() this.isVisible = false diff --git a/src/main/kotlin/org/magmaoffenburg/roboviz/util/DataTypes.kt b/src/main/kotlin/org/magmaoffenburg/roboviz/util/DataTypes.kt index 6723a155..2eca6533 100644 --- a/src/main/kotlin/org/magmaoffenburg/roboviz/util/DataTypes.kt +++ b/src/main/kotlin/org/magmaoffenburg/roboviz/util/DataTypes.kt @@ -1,5 +1,22 @@ package org.magmaoffenburg.roboviz.util + +import java.lang.IllegalStateException + enum class Mode { LOG, LIVE } + +enum class ConfirmResult { + YES, NO, CANCEL; + + companion object { + fun fromInt(result: Int) = + when (result) { + 0 -> YES + 1 -> NO + 2 -> CANCEL + else -> throw IllegalStateException() + } + } +}