diff --git a/CHANGELOG.md b/CHANGELOG.md index 85f7e72..b389534 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## 2025.1.2 + * Implement the Debug Run Configuration on Linux + ## 2025.1.1 * Fixes an issue with the Rimworld Run Configuration when using multiple parameters diff --git a/gradle.properties b/gradle.properties index dec539f..a53e823 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ DotnetPluginId=ReSharperPlugin.RimworldDev DotnetSolution=ReSharperPlugin.RimworldDev.sln RiderPluginId=com.jetbrains.rider.plugins.rimworlddev -PluginVersion=2025.1.1 +PluginVersion=2025.1.2 BuildConfiguration=Release diff --git a/src/rider/main/kotlin/run/RunConfiguration.kt b/src/rider/main/kotlin/run/RunConfiguration.kt index b60152e..33e5eac 100644 --- a/src/rider/main/kotlin/run/RunConfiguration.kt +++ b/src/rider/main/kotlin/run/RunConfiguration.kt @@ -9,20 +9,17 @@ import com.intellij.execution.process.* import com.intellij.execution.runners.ExecutionEnvironment import com.intellij.openapi.options.SettingsEditor import com.intellij.openapi.project.Project -import com.intellij.openapi.rd.util.startOnUiAsync -import com.intellij.openapi.rd.util.toPromise -import com.jetbrains.rd.platform.util.lifetime +import com.intellij.util.system.OS import com.jetbrains.rider.debugger.IRiderDebuggable import com.jetbrains.rider.plugins.unity.run.configurations.UnityAttachToPlayerFactory import com.jetbrains.rider.plugins.unity.run.configurations.UnityPlayerDebugConfigurationOptions import com.jetbrains.rider.run.configurations.AsyncRunConfiguration -import com.jetbrains.rider.run.getProcess -import kotlinx.coroutines.ExperimentalCoroutinesApi import org.jetbrains.concurrency.Promise import com.jetbrains.rider.plugins.unity.UnityBundle import com.jetbrains.rider.plugins.unity.run.configurations.unityExe.UnityExeConfiguration import com.jetbrains.rider.run.RiderRunBundle import icons.UnityIcons +import kotlin.io.path.Path internal class UnityPlayerDebugConfigurationTypeInternal : ConfigurationTypeBase( @@ -111,7 +108,7 @@ class RunConfiguration(project: Project, factory: ConfigurationFactory, name: St getScriptName(), getSaveFilePath(), getModListPath(), - getRimworldState(environment), + getRimworldState(environment, OS.CURRENT == OS.Linux), UnityDebugRemoteConfiguration(), environment, "CustomPlayer" @@ -122,11 +119,21 @@ class RunConfiguration(project: Project, factory: ConfigurationFactory, name: St return RimworldDev.Rider.run.SettingsEditor(project) } - private fun getRimworldState(environment: ExecutionEnvironment): CommandLineState { + private fun getRimworldState(environment: ExecutionEnvironment, debugInLinux: Boolean = false): CommandLineState { return object : CommandLineState(environment) { override fun startProcess(): ProcessHandler { - val commandLine = GeneralCommandLine(getScriptName()) - .withParameters(getCommandLineOptions().split(' ')) + var pathToRun = getScriptName() + var arguments = getCommandLineOptions() + + // If we're debugging in Rimworld, instead of /pwd/RimWorldLinux ...args we want to run /bin/sh /pwd/run.sh /pwd/RimWorldLinux ...args + if (debugInLinux) { + val bashScriptPath = "${Path(pathToRun).parent}/run.sh" + arguments = "$bashScriptPath $pathToRun $arguments" + pathToRun = "/bin/sh" + } + + val commandLine = GeneralCommandLine(pathToRun) + .withParameters(arguments.split(' ').filter { it.isNotEmpty() }) EnvironmentVariablesData.create(getEnvData(), true).configureCommandLine(commandLine, true) diff --git a/src/rider/main/kotlin/run/RunState.kt b/src/rider/main/kotlin/run/RunState.kt index 1615ff9..dcb1586 100644 --- a/src/rider/main/kotlin/run/RunState.kt +++ b/src/rider/main/kotlin/run/RunState.kt @@ -44,6 +44,20 @@ class RunState( "Doorstop/Mono.CompilerServices.SymbolWriter.dll", "Doorstop/pdb2mdb.exe", ), + OS.Linux to listOf( + "run.sh", + ".doorstop_version", + + "Doorstop/0Harmony.dll", + "Doorstop/dnlib.dll", + "Doorstop/Doorstop.dll", + "Doorstop/Doorstop.pdb", + "Doorstop/HotReload.dll", + "Doorstop/libdoorstop.so", + "Doorstop/Mono.Cecil.dll", + "Doorstop/Mono.CompilerServices.SymbolWriter.dll", + "Doorstop/pdb2mdb.exe", + ), OS.macOS to listOf( ".doorstop_version", ".doorstop_config.ini", @@ -69,7 +83,7 @@ class RunState( val rimworldResult = rimworldState.execute(executor, runner) workerProcessHandler.debuggerWorkerRealHandler.addProcessListener(createProcessListener(rimworldResult?.processHandler)) - + return result } @@ -79,7 +93,12 @@ class RunState( val processHandler = event.processHandler processHandler.removeProcessListener(this) - siblingProcessHandler?.getProcess()?.destroy() + if (OS.CURRENT == OS.Linux) { + siblingProcessHandler?.getProcess()?.destroyForcibly() + } else { + siblingProcessHandler?.getProcess()?.destroy() + } + QuickStartUtils.tearDown(saveFilePath) removeDoorstep() } diff --git a/src/rider/main/resources/UnityDoorstop/Linux/.doorstop_version b/src/rider/main/resources/UnityDoorstop/Linux/.doorstop_version new file mode 100644 index 0000000..0c89fc9 --- /dev/null +++ b/src/rider/main/resources/UnityDoorstop/Linux/.doorstop_version @@ -0,0 +1 @@ +4.0.0 \ No newline at end of file diff --git a/src/rider/main/resources/UnityDoorstop/Linux/Doorstop/0Harmony.dll b/src/rider/main/resources/UnityDoorstop/Linux/Doorstop/0Harmony.dll new file mode 100644 index 0000000..7aac19c Binary files /dev/null and b/src/rider/main/resources/UnityDoorstop/Linux/Doorstop/0Harmony.dll differ diff --git a/src/rider/main/resources/UnityDoorstop/Linux/Doorstop/Doorstop.dll b/src/rider/main/resources/UnityDoorstop/Linux/Doorstop/Doorstop.dll new file mode 100644 index 0000000..c0e066d Binary files /dev/null and b/src/rider/main/resources/UnityDoorstop/Linux/Doorstop/Doorstop.dll differ diff --git a/src/rider/main/resources/UnityDoorstop/Linux/Doorstop/Doorstop.pdb b/src/rider/main/resources/UnityDoorstop/Linux/Doorstop/Doorstop.pdb new file mode 100644 index 0000000..660a25e Binary files /dev/null and b/src/rider/main/resources/UnityDoorstop/Linux/Doorstop/Doorstop.pdb differ diff --git a/src/rider/main/resources/UnityDoorstop/Linux/Doorstop/HotReload.dll b/src/rider/main/resources/UnityDoorstop/Linux/Doorstop/HotReload.dll new file mode 100644 index 0000000..89dd8dc Binary files /dev/null and b/src/rider/main/resources/UnityDoorstop/Linux/Doorstop/HotReload.dll differ diff --git a/src/rider/main/resources/UnityDoorstop/Linux/Doorstop/Mono.Cecil.dll b/src/rider/main/resources/UnityDoorstop/Linux/Doorstop/Mono.Cecil.dll new file mode 100644 index 0000000..769f317 Binary files /dev/null and b/src/rider/main/resources/UnityDoorstop/Linux/Doorstop/Mono.Cecil.dll differ diff --git a/src/rider/main/resources/UnityDoorstop/Linux/Doorstop/Mono.CompilerServices.SymbolWriter.dll b/src/rider/main/resources/UnityDoorstop/Linux/Doorstop/Mono.CompilerServices.SymbolWriter.dll new file mode 100644 index 0000000..3e97558 Binary files /dev/null and b/src/rider/main/resources/UnityDoorstop/Linux/Doorstop/Mono.CompilerServices.SymbolWriter.dll differ diff --git a/src/rider/main/resources/UnityDoorstop/Linux/Doorstop/dnlib.dll b/src/rider/main/resources/UnityDoorstop/Linux/Doorstop/dnlib.dll new file mode 100644 index 0000000..76a7b32 Binary files /dev/null and b/src/rider/main/resources/UnityDoorstop/Linux/Doorstop/dnlib.dll differ diff --git a/src/rider/main/resources/UnityDoorstop/Linux/Doorstop/libdoorstop.so b/src/rider/main/resources/UnityDoorstop/Linux/Doorstop/libdoorstop.so new file mode 100644 index 0000000..d20bdfc Binary files /dev/null and b/src/rider/main/resources/UnityDoorstop/Linux/Doorstop/libdoorstop.so differ diff --git a/src/rider/main/resources/UnityDoorstop/Linux/Doorstop/pdb2mdb.exe b/src/rider/main/resources/UnityDoorstop/Linux/Doorstop/pdb2mdb.exe new file mode 100644 index 0000000..05c4bb7 Binary files /dev/null and b/src/rider/main/resources/UnityDoorstop/Linux/Doorstop/pdb2mdb.exe differ diff --git a/src/rider/main/resources/UnityDoorstop/Linux/run.sh b/src/rider/main/resources/UnityDoorstop/Linux/run.sh new file mode 100755 index 0000000..4071a0e --- /dev/null +++ b/src/rider/main/resources/UnityDoorstop/Linux/run.sh @@ -0,0 +1,280 @@ +#!/bin/sh +# Doorstop start script +# +# Run the script to start the game with Doorstop enabled +# +# There are two ways to use this script +# +# 1. Via CLI: Run ./run.sh [doorstop arguments] [game arguments] +# 2. Via config: edit the options below and run ./run.sh without any arguments + +# 0 is false, 1 is true + +# LINUX: name of Unity executable +# MACOS: name of the .app directory +executable_name="RimWorldLinux" + +# All of the below can be overriden with command line args + +# General Config Options + +# Enable Doorstop? +enabled="1" + +# Path to the assembly to load and execute +# NOTE: The entrypoint must be of format `static void Doorstop.Entrypoint.Start()` +target_assembly="Doorstop.dll" + +# Overrides the default boot.config file path +boot_config_override= + +# If enabled, DOORSTOP_DISABLE env var value is ignored +# USE THIS ONLY WHEN ASKED TO OR YOU KNOW WHAT THIS MEANS +ignore_disable_switch="0" + +# Mono Options + +# Overrides default Mono DLL search path +# Sometimes it is needed to instruct Mono to seek its assemblies from a different path +# (e.g. mscorlib is stripped in original game) +# This option causes Mono to seek mscorlib and core libraries from a different folder before Managed +# Original Managed folder is added as a secondary folder in the search path +# To specify multiple paths, separate them with colons (:) +dll_search_path_override="" + +# If 1, Mono debugger server will be enabled +debug_enable="1" + +# When debug_enabled is 1, specifies the address to use for the debugger server +debug_address="127.0.0.1:56000" + +# If 1 and debug_enabled is 1, Mono debugger server will suspend the game execution until a debugger is attached +debug_suspend="0" + +# CoreCLR options (IL2CPP) + +# Path to coreclr shared library WITHOUT THE EXTENSION that contains the CoreCLR runtime +coreclr_path="" + +# Path to the directory containing the managed core libraries for CoreCLR (mscorlib, System, etc.) +corlib_dir="" + +################################################################################ +# Everything past this point is the actual script + +# Special case: program is launched via Steam +# In that case rerun the script via their bootstrapper to ensure Steam overlay works +if [ "$2" = "SteamLaunch" ]; then + steam="$1 $2 $3 $4 $0 $5" + shift 5 + $steam "$@" + exit +fi + +# Handle first param being executable name +if [ -x "$1" ] ; then + executable_name="$1" + echo "Target executable: $1" + shift +fi + +if [ -z "${executable_name}" ] || [ ! -x "${executable_name}" ]; then + echo "Please set executable_name to a valid name in a text editor or as the first command line parameter" + exit 1 +fi + +# Use POSIX-compatible way to get the directory of the executable +a="/$0"; a=${a%/*}; a=${a#/}; a=${a:-.}; BASEDIR=$(cd "$a" || exit; pwd -P) + +arch="" +executable_path="" +lib_extension="" + +# Set executable path and the extension to use for the libdoorstop shared object +os_type="$(uname -s)" +case ${os_type} in + Linux*) + executable_path="${executable_name}" + # Handle relative paths + if ! echo "$executable_path" | grep "^/.*$"; then + executable_path="${BASEDIR}/${executable_path}" + fi + lib_extension="so" + ;; + Darwin*) + real_executable_name="${executable_name}" + + # Handle relative directories + if ! echo "$real_executable_name" | grep "^/.*$"; then + real_executable_name="${BASEDIR}/${real_executable_name}" + fi + + # If we're not even an actual executable, check .app Info for actual executable + if ! echo "$real_executable_name" | grep "^.*\.app/Contents/MacOS/.*"; then + # Add .app to the end if not given + if ! echo "$real_executable_name" | grep "^.*\.app$"; then + real_executable_name="${real_executable_name}.app" + fi + inner_executable_name=$(defaults read "${real_executable_name}/Contents/Info" CFBundleExecutable) + executable_path="${real_executable_name}/Contents/MacOS/${inner_executable_name}" + else + executable_path="${executable_name}" + fi + lib_extension="dylib" + ;; + *) + # alright whos running games on freebsd + echo "Unknown operating system ($(uname -s))" + echo "Make an issue at https://github.com/NeighTools/UnityDoorstop" + exit 1 + ;; +esac + +abs_path() { + echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")" +} + +_readlink() { + # relative links with readlink (without -f) do not preserve the path info + ab_path="$(abs_path "$1")" + link="$(readlink "${ab_path}")" + case $link in + /*);; + *) link="$(dirname "$ab_path")/$link";; + esac + echo "$link" +} + + +resolve_executable_path () { + e_path="$(abs_path "$1")" + + while [ -L "${e_path}" ]; do + e_path=$(_readlink "${e_path}"); + done + echo "${e_path}" +} + +# Get absolute path of executable and show to user +executable_path=$(resolve_executable_path "${executable_path}") +echo "${executable_path}" + +# Figure out the arch of the executable with file +file_out="$(LD_PRELOAD="" file -b "${executable_path}")" +case "${file_out}" in + *64-bit*) + arch="x64" + ;; + *32-bit*) + arch="x86" + ;; + *) + echo "The executable \"${executable_path}\" is not compiled for x86 or x64 (might be ARM?)" + echo "If you think this is a mistake (or would like to encourage support for other architectures)" + echo "Please make an issue at https://github.com/NeighTools/UnityDoorstop" + echo "Got: ${file_out}" + exit 1 + ;; +esac + +# Helper to convert common boolean strings into just 0 and 1 +doorstop_bool() { + case "$1" in + TRUE|true|t|T|1|Y|y|yes) + echo "1" + ;; + FALSE|false|f|F|0|N|n|no) + echo "0" + ;; + esac +} + +# Read from command line +while :; do + case "$1" in + --doorstop_enabled) # For backwards compatibility. Renamed to --doorstop-enabled + enabled="$(doorstop_bool "$2")" + shift + ;; + --doorstop_target_assembly) # For backwards compatibility. Renamed to --doorstop-target-assembly + target_assembly="$2" + shift + ;; + --doorstop-enabled) + enabled="$(doorstop_bool "$2")" + shift + ;; + --doorstop-target-assembly) + target_assembly="$2" + shift + ;; + --doorstop-boot-config-override) + boot_config_override="$2" + shift + ;; + --doorstop-mono-dll-search-path-override) + dll_search_path_override="$2" + shift + ;; + --doorstop-mono-debug-enabled) + debug_enable="$(doorstop_bool "$2")" + shift + ;; + --doorstop-mono-debug-suspend) + debug_suspend="$(doorstop_bool "$2")" + shift + ;; + --doorstop-mono-debug-address) + debug_address="$2" + shift + ;; + --doorstop-clr-runtime-coreclr-path) + coreclr_path="$2" + shift + ;; + --doorstop-clr-corlib-dir) + corlib_dir="$2" + shift + ;; + *) + if [ -z "$1" ]; then + break + fi + rest_args="$rest_args $1" + ;; + esac + shift +done + +# Move variables to environment +export DOORSTOP_ENABLED="$enabled" +export DOORSTOP_TARGET_ASSEMBLY="$target_assembly" +export DOORSTOP_BOOT_CONFIG_OVERRIDE="$boot_config_override" +export DOORSTOP_IGNORE_DISABLED_ENV="$ignore_disable_switch" +export DOORSTOP_MONO_DLL_SEARCH_PATH_OVERRIDE="$dll_search_path_override" +export DOORSTOP_MONO_DEBUG_ENABLED="$debug_enable" +export DOORSTOP_MONO_DEBUG_ADDRESS="$debug_address" +export DOORSTOP_MONO_DEBUG_SUSPEND="$debug_suspend" +export DOORSTOP_CLR_RUNTIME_CORECLR_PATH="$coreclr_path.$lib_extension" +export DOORSTOP_CLR_CORLIB_DIR="$corlib_dir" + +# Final setup +doorstop_directory="${BASEDIR}/Doorstop/" +doorstop_name="libdoorstop.${lib_extension}" + +export LD_LIBRARY_PATH="${doorstop_directory}:${corlib_dir}:${LD_LIBRARY_PATH}" +if [ -z "$LD_PRELOAD" ]; then + export LD_PRELOAD="${doorstop_name}" +else + export LD_PRELOAD="${doorstop_name}:${LD_PRELOAD}" +fi + +export DYLD_LIBRARY_PATH="${doorstop_directory}:${DYLD_LIBRARY_PATH}" +if [ -z "$DYLD_INSERT_LIBRARIES" ]; then + export DYLD_INSERT_LIBRARIES="${doorstop_name}" +else + export DYLD_INSERT_LIBRARIES="${doorstop_name}:${DYLD_INSERT_LIBRARIES}" +fi + +# shellcheck disable=SC2086 +exec "$executable_path" $rest_args