Repository: JetBrains/UnrealLink Branch: main Commit: b562e254780f Files: 490 Total size: 2.2 MB Directory structure: gitextract_ql_wvwqe/ ├── .editorconfig ├── .gitattributes ├── .github/ │ └── workflows/ │ └── build.yml ├── .gitignore ├── .run/ │ ├── Debug backend.run.xml │ └── UnrealLink [runIde].run.xml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── License.txt ├── README.md ├── SETUP.md ├── SdkBasedPluginEmbedding.Root ├── SubplatformsCollection.Root ├── UnrealLink.sln ├── build.gradle.kts ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── protocol/ │ ├── build.gradle.kts │ └── src/ │ └── main/ │ └── kotlin/ │ └── model/ │ ├── editorPlugin/ │ │ ├── LiveCodingModel.kt │ │ └── RdEditorModel.kt │ ├── lib/ │ │ └── ue4/ │ │ └── UE4Library.kt │ └── rider/ │ └── RdRiderModel.kt ├── settings.gradle.kts ├── setup_unreal_plugin.cmd ├── src/ │ ├── SubplatformsCollection.Root │ ├── cpp/ │ │ ├── BuildScript/ │ │ │ └── GenerateRiderLinkZip.cs │ │ ├── RiderLink/ │ │ │ ├── RiderLink.uplugin.template │ │ │ └── Source/ │ │ │ ├── RD/ │ │ │ │ ├── RD.Build.cs │ │ │ │ ├── RD.cpp │ │ │ │ ├── RD.h │ │ │ │ ├── src/ │ │ │ │ │ ├── rd_core_cpp/ │ │ │ │ │ │ ├── rd_core_export.h │ │ │ │ │ │ └── src/ │ │ │ │ │ │ └── main/ │ │ │ │ │ │ ├── lifetime/ │ │ │ │ │ │ │ ├── Lifetime.cpp │ │ │ │ │ │ │ ├── Lifetime.h │ │ │ │ │ │ │ ├── LifetimeDefinition.cpp │ │ │ │ │ │ │ ├── LifetimeDefinition.h │ │ │ │ │ │ │ ├── LifetimeImpl.cpp │ │ │ │ │ │ │ ├── LifetimeImpl.h │ │ │ │ │ │ │ ├── SequentialLifetimes.cpp │ │ │ │ │ │ │ └── SequentialLifetimes.h │ │ │ │ │ │ ├── reactive/ │ │ │ │ │ │ │ ├── Property.h │ │ │ │ │ │ │ ├── ViewableList.h │ │ │ │ │ │ │ ├── ViewableMap.h │ │ │ │ │ │ │ ├── ViewableSet.h │ │ │ │ │ │ │ └── base/ │ │ │ │ │ │ │ ├── IProperty.h │ │ │ │ │ │ │ ├── IPropertyBase.h │ │ │ │ │ │ │ ├── IViewableList.h │ │ │ │ │ │ │ ├── IViewableMap.h │ │ │ │ │ │ │ ├── IViewableSet.h │ │ │ │ │ │ │ ├── SignalCookie.cpp │ │ │ │ │ │ │ ├── SignalCookie.h │ │ │ │ │ │ │ ├── SignalX.h │ │ │ │ │ │ │ ├── interfaces.h │ │ │ │ │ │ │ └── viewable_collections.h │ │ │ │ │ │ ├── std/ │ │ │ │ │ │ │ ├── allocator.h │ │ │ │ │ │ │ ├── hash.h │ │ │ │ │ │ │ ├── list.h │ │ │ │ │ │ │ ├── to_string.h │ │ │ │ │ │ │ ├── unordered_map.h │ │ │ │ │ │ │ └── unordered_set.h │ │ │ │ │ │ ├── types/ │ │ │ │ │ │ │ ├── DateTime.cpp │ │ │ │ │ │ │ ├── DateTime.h │ │ │ │ │ │ │ ├── Void.h │ │ │ │ │ │ │ └── wrapper.h │ │ │ │ │ │ └── util/ │ │ │ │ │ │ ├── core_traits.h │ │ │ │ │ │ ├── core_util.h │ │ │ │ │ │ ├── enum.h │ │ │ │ │ │ ├── erase_if.h │ │ │ │ │ │ ├── gen_util.h │ │ │ │ │ │ ├── overloaded.h │ │ │ │ │ │ └── shared_function.h │ │ │ │ │ ├── rd_framework_cpp/ │ │ │ │ │ │ ├── rd_framework_export.h │ │ │ │ │ │ └── src/ │ │ │ │ │ │ └── main/ │ │ │ │ │ │ ├── base/ │ │ │ │ │ │ │ ├── IProtocol.cpp │ │ │ │ │ │ │ ├── IProtocol.h │ │ │ │ │ │ │ ├── IRdBindable.h │ │ │ │ │ │ │ ├── IRdDynamic.h │ │ │ │ │ │ │ ├── IRdReactive.h │ │ │ │ │ │ │ ├── IRdWireable.cpp │ │ │ │ │ │ │ ├── IRdWireable.h │ │ │ │ │ │ │ ├── ISerializersOwner.cpp │ │ │ │ │ │ │ ├── ISerializersOwner.h │ │ │ │ │ │ │ ├── IUnknownInstance.cpp │ │ │ │ │ │ │ ├── IUnknownInstance.h │ │ │ │ │ │ │ ├── IWire.h │ │ │ │ │ │ │ ├── RdBindableBase.cpp │ │ │ │ │ │ │ ├── RdBindableBase.h │ │ │ │ │ │ │ ├── RdPropertyBase.h │ │ │ │ │ │ │ ├── RdReactiveBase.cpp │ │ │ │ │ │ │ ├── RdReactiveBase.h │ │ │ │ │ │ │ ├── WireBase.cpp │ │ │ │ │ │ │ └── WireBase.h │ │ │ │ │ │ ├── ext/ │ │ │ │ │ │ │ ├── ExtWire.cpp │ │ │ │ │ │ │ ├── ExtWire.h │ │ │ │ │ │ │ ├── RdExtBase.cpp │ │ │ │ │ │ │ └── RdExtBase.h │ │ │ │ │ │ ├── impl/ │ │ │ │ │ │ │ ├── RName.cpp │ │ │ │ │ │ │ ├── RName.h │ │ │ │ │ │ │ ├── RdList.h │ │ │ │ │ │ │ ├── RdMap.h │ │ │ │ │ │ │ ├── RdProperty.h │ │ │ │ │ │ │ ├── RdSet.h │ │ │ │ │ │ │ └── RdSignal.h │ │ │ │ │ │ ├── intern/ │ │ │ │ │ │ │ ├── InternRoot.cpp │ │ │ │ │ │ │ ├── InternRoot.h │ │ │ │ │ │ │ ├── InternScheduler.cpp │ │ │ │ │ │ │ └── InternScheduler.h │ │ │ │ │ │ ├── protocol/ │ │ │ │ │ │ │ ├── Buffer.cpp │ │ │ │ │ │ │ ├── Buffer.h │ │ │ │ │ │ │ ├── Identities.cpp │ │ │ │ │ │ │ ├── Identities.h │ │ │ │ │ │ │ ├── MessageBroker.cpp │ │ │ │ │ │ │ ├── MessageBroker.h │ │ │ │ │ │ │ ├── Protocol.cpp │ │ │ │ │ │ │ ├── Protocol.h │ │ │ │ │ │ │ ├── RdId.cpp │ │ │ │ │ │ │ └── RdId.h │ │ │ │ │ │ ├── scheduler/ │ │ │ │ │ │ │ ├── SimpleScheduler.cpp │ │ │ │ │ │ │ ├── SimpleScheduler.h │ │ │ │ │ │ │ ├── SingleThreadScheduler.cpp │ │ │ │ │ │ │ ├── SingleThreadScheduler.h │ │ │ │ │ │ │ ├── SynchronousScheduler.cpp │ │ │ │ │ │ │ ├── SynchronousScheduler.h │ │ │ │ │ │ │ └── base/ │ │ │ │ │ │ │ ├── IScheduler.cpp │ │ │ │ │ │ │ ├── IScheduler.h │ │ │ │ │ │ │ ├── SingleThreadSchedulerBase.cpp │ │ │ │ │ │ │ └── SingleThreadSchedulerBase.h │ │ │ │ │ │ ├── serialization/ │ │ │ │ │ │ │ ├── AbstractPolymorphic.h │ │ │ │ │ │ │ ├── ArraySerializer.h │ │ │ │ │ │ │ ├── DefaultAbstractDeclaration.cpp │ │ │ │ │ │ │ ├── DefaultAbstractDeclaration.h │ │ │ │ │ │ │ ├── ISerializable.cpp │ │ │ │ │ │ │ ├── ISerializable.h │ │ │ │ │ │ │ ├── InternedAnySerializer.h │ │ │ │ │ │ │ ├── InternedSerializer.h │ │ │ │ │ │ │ ├── NullableSerializer.h │ │ │ │ │ │ │ ├── Polymorphic.cpp │ │ │ │ │ │ │ ├── Polymorphic.h │ │ │ │ │ │ │ ├── RdAny.cpp │ │ │ │ │ │ │ ├── RdAny.h │ │ │ │ │ │ │ ├── SerializationCtx.cpp │ │ │ │ │ │ │ ├── SerializationCtx.h │ │ │ │ │ │ │ ├── Serializers.cpp │ │ │ │ │ │ │ └── Serializers.h │ │ │ │ │ │ ├── task/ │ │ │ │ │ │ │ ├── RdCall.h │ │ │ │ │ │ │ ├── RdEndpoint.h │ │ │ │ │ │ │ ├── RdSymmetricCall.h │ │ │ │ │ │ │ ├── RdTask.h │ │ │ │ │ │ │ ├── RdTaskImpl.h │ │ │ │ │ │ │ ├── RdTaskResult.h │ │ │ │ │ │ │ ├── WiredRdTask.h │ │ │ │ │ │ │ └── WiredRdTaskImpl.h │ │ │ │ │ │ ├── util/ │ │ │ │ │ │ │ ├── framework_traits.h │ │ │ │ │ │ │ ├── guards.h │ │ │ │ │ │ │ ├── hashing.cpp │ │ │ │ │ │ │ ├── hashing.h │ │ │ │ │ │ │ ├── thread_util.cpp │ │ │ │ │ │ │ └── thread_util.h │ │ │ │ │ │ └── wire/ │ │ │ │ │ │ ├── ByteBufferAsyncProcessor.cpp │ │ │ │ │ │ ├── ByteBufferAsyncProcessor.h │ │ │ │ │ │ ├── PkgInputStream.cpp │ │ │ │ │ │ ├── PkgInputStream.h │ │ │ │ │ │ ├── PumpScheduler.cpp │ │ │ │ │ │ ├── PumpScheduler.h │ │ │ │ │ │ ├── SocketWire.cpp │ │ │ │ │ │ ├── SocketWire.h │ │ │ │ │ │ ├── WireUtil.cpp │ │ │ │ │ │ └── WireUtil.h │ │ │ │ │ └── rd_gen_cpp/ │ │ │ │ │ └── src/ │ │ │ │ │ ├── RdTextBuffer.cpp │ │ │ │ │ └── RdTextBuffer.h │ │ │ │ └── thirdparty/ │ │ │ │ ├── CTPL/ │ │ │ │ │ ├── LICENSE │ │ │ │ │ ├── README.md │ │ │ │ │ └── include/ │ │ │ │ │ └── ctpl_stl.h │ │ │ │ ├── clsocket/ │ │ │ │ │ ├── README │ │ │ │ │ └── src/ │ │ │ │ │ ├── ActiveSocket.cpp │ │ │ │ │ ├── ActiveSocket.h │ │ │ │ │ ├── Host.h │ │ │ │ │ ├── PassiveSocket.cpp │ │ │ │ │ ├── PassiveSocket.h │ │ │ │ │ ├── SimpleSocket.cpp │ │ │ │ │ ├── SimpleSocket.h │ │ │ │ │ └── StatTimer.h │ │ │ │ ├── countdownlatch/ │ │ │ │ │ ├── LICENSE │ │ │ │ │ ├── README.md │ │ │ │ │ ├── countdownlatch.cpp │ │ │ │ │ └── countdownlatch.hpp │ │ │ │ ├── optional/ │ │ │ │ │ ├── COPYING │ │ │ │ │ ├── README.md │ │ │ │ │ └── tl/ │ │ │ │ │ └── optional.hpp │ │ │ │ ├── ordered-map/ │ │ │ │ │ ├── LICENSE │ │ │ │ │ ├── README.md │ │ │ │ │ └── include/ │ │ │ │ │ └── tsl/ │ │ │ │ │ ├── ordered_hash.h │ │ │ │ │ ├── ordered_map.h │ │ │ │ │ └── ordered_set.h │ │ │ │ ├── spdlog/ │ │ │ │ │ ├── LICENSE │ │ │ │ │ ├── README.md │ │ │ │ │ ├── cmake/ │ │ │ │ │ │ ├── ide.cmake │ │ │ │ │ │ ├── spdlog.pc.in │ │ │ │ │ │ ├── spdlogCPack.cmake │ │ │ │ │ │ ├── spdlogConfig.cmake.in │ │ │ │ │ │ ├── utils.cmake │ │ │ │ │ │ └── version.rc.in │ │ │ │ │ ├── include/ │ │ │ │ │ │ └── spdlog/ │ │ │ │ │ │ ├── async.h │ │ │ │ │ │ ├── async_logger-inl.h │ │ │ │ │ │ ├── async_logger.h │ │ │ │ │ │ ├── cfg/ │ │ │ │ │ │ │ ├── argv.h │ │ │ │ │ │ │ ├── env.h │ │ │ │ │ │ │ ├── helpers-inl.h │ │ │ │ │ │ │ ├── helpers.h │ │ │ │ │ │ │ └── log_levels.h │ │ │ │ │ │ ├── common-inl.h │ │ │ │ │ │ ├── common.h │ │ │ │ │ │ ├── details/ │ │ │ │ │ │ │ ├── backtracer-inl.h │ │ │ │ │ │ │ ├── backtracer.h │ │ │ │ │ │ │ ├── circular_q.h │ │ │ │ │ │ │ ├── console_globals.h │ │ │ │ │ │ │ ├── file_helper-inl.h │ │ │ │ │ │ │ ├── file_helper.h │ │ │ │ │ │ │ ├── fmt_helper.h │ │ │ │ │ │ │ ├── log_msg-inl.h │ │ │ │ │ │ │ ├── log_msg.h │ │ │ │ │ │ │ ├── log_msg_buffer-inl.h │ │ │ │ │ │ │ ├── log_msg_buffer.h │ │ │ │ │ │ │ ├── mpmc_blocking_q.h │ │ │ │ │ │ │ ├── null_mutex.h │ │ │ │ │ │ │ ├── os-inl.h │ │ │ │ │ │ │ ├── os.h │ │ │ │ │ │ │ ├── periodic_worker-inl.h │ │ │ │ │ │ │ ├── periodic_worker.h │ │ │ │ │ │ │ ├── registry-inl.h │ │ │ │ │ │ │ ├── registry.h │ │ │ │ │ │ │ ├── synchronous_factory.h │ │ │ │ │ │ │ ├── tcp_client-windows.h │ │ │ │ │ │ │ ├── tcp_client.h │ │ │ │ │ │ │ ├── thread_pool-inl.h │ │ │ │ │ │ │ ├── thread_pool.h │ │ │ │ │ │ │ ├── udp_client-windows.h │ │ │ │ │ │ │ ├── udp_client.h │ │ │ │ │ │ │ └── windows_include.h │ │ │ │ │ │ ├── fmt/ │ │ │ │ │ │ │ ├── bin_to_hex.h │ │ │ │ │ │ │ ├── bundled/ │ │ │ │ │ │ │ │ ├── LICENSE.rst │ │ │ │ │ │ │ │ ├── args.h │ │ │ │ │ │ │ │ ├── chrono.h │ │ │ │ │ │ │ │ ├── color.h │ │ │ │ │ │ │ │ ├── compile.h │ │ │ │ │ │ │ │ ├── core.h │ │ │ │ │ │ │ │ ├── fmt.license.rst │ │ │ │ │ │ │ │ ├── format-inl.h │ │ │ │ │ │ │ │ ├── format.h │ │ │ │ │ │ │ │ ├── locale.h │ │ │ │ │ │ │ │ ├── os.h │ │ │ │ │ │ │ │ ├── ostream.h │ │ │ │ │ │ │ │ ├── posix.h │ │ │ │ │ │ │ │ ├── printf.h │ │ │ │ │ │ │ │ ├── ranges.h │ │ │ │ │ │ │ │ └── xchar.h │ │ │ │ │ │ │ ├── chrono.h │ │ │ │ │ │ │ ├── compile.h │ │ │ │ │ │ │ ├── fmt.h │ │ │ │ │ │ │ ├── ostr.h │ │ │ │ │ │ │ ├── ranges.h │ │ │ │ │ │ │ └── xchar.h │ │ │ │ │ │ ├── formatter.h │ │ │ │ │ │ ├── fwd.h │ │ │ │ │ │ ├── logger-inl.h │ │ │ │ │ │ ├── logger.h │ │ │ │ │ │ ├── pattern_formatter-inl.h │ │ │ │ │ │ ├── pattern_formatter.h │ │ │ │ │ │ ├── sinks/ │ │ │ │ │ │ │ ├── android_sink.h │ │ │ │ │ │ │ ├── ansicolor_sink-inl.h │ │ │ │ │ │ │ ├── ansicolor_sink.h │ │ │ │ │ │ │ ├── base_sink-inl.h │ │ │ │ │ │ │ ├── base_sink.h │ │ │ │ │ │ │ ├── basic_file_sink-inl.h │ │ │ │ │ │ │ ├── basic_file_sink.h │ │ │ │ │ │ │ ├── daily_file_sink.h │ │ │ │ │ │ │ ├── dist_sink.h │ │ │ │ │ │ │ ├── dup_filter_sink.h │ │ │ │ │ │ │ ├── hourly_file_sink.h │ │ │ │ │ │ │ ├── mongo_sink.h │ │ │ │ │ │ │ ├── msvc_sink.h │ │ │ │ │ │ │ ├── null_sink.h │ │ │ │ │ │ │ ├── ostream_sink.h │ │ │ │ │ │ │ ├── qt_sinks.h │ │ │ │ │ │ │ ├── ringbuffer_sink.h │ │ │ │ │ │ │ ├── rotating_file_sink-inl.h │ │ │ │ │ │ │ ├── rotating_file_sink.h │ │ │ │ │ │ │ ├── sink-inl.h │ │ │ │ │ │ │ ├── sink.h │ │ │ │ │ │ │ ├── stdout_color_sinks-inl.h │ │ │ │ │ │ │ ├── stdout_color_sinks.h │ │ │ │ │ │ │ ├── stdout_sinks-inl.h │ │ │ │ │ │ │ ├── stdout_sinks.h │ │ │ │ │ │ │ ├── syslog_sink.h │ │ │ │ │ │ │ ├── systemd_sink.h │ │ │ │ │ │ │ ├── tcp_sink.h │ │ │ │ │ │ │ ├── udp_sink.h │ │ │ │ │ │ │ ├── win_eventlog_sink.h │ │ │ │ │ │ │ ├── wincolor_sink-inl.h │ │ │ │ │ │ │ └── wincolor_sink.h │ │ │ │ │ │ ├── spdlog-inl.h │ │ │ │ │ │ ├── spdlog.h │ │ │ │ │ │ ├── stopwatch.h │ │ │ │ │ │ ├── tweakme.h │ │ │ │ │ │ └── version.h │ │ │ │ │ ├── scripts/ │ │ │ │ │ │ ├── extract_version.py │ │ │ │ │ │ └── format.sh │ │ │ │ │ └── src/ │ │ │ │ │ ├── async.cpp │ │ │ │ │ ├── cfg.cpp │ │ │ │ │ ├── color_sinks.cpp │ │ │ │ │ ├── file_sinks.cpp │ │ │ │ │ ├── fmt.cpp │ │ │ │ │ ├── spdlog.cpp │ │ │ │ │ └── stdout_sinks.cpp │ │ │ │ ├── string-view-lite/ │ │ │ │ │ ├── LICENSE.txt │ │ │ │ │ ├── README.md │ │ │ │ │ ├── cmake/ │ │ │ │ │ │ └── string_view-lite-config.cmake.in │ │ │ │ │ └── include/ │ │ │ │ │ └── nonstd/ │ │ │ │ │ └── string_view.hpp │ │ │ │ ├── thirdparty.cpp │ │ │ │ ├── thirdparty.hpp │ │ │ │ ├── utf-cpp/ │ │ │ │ │ ├── LICENSE.md │ │ │ │ │ ├── README.md │ │ │ │ │ └── include/ │ │ │ │ │ └── ww898/ │ │ │ │ │ ├── cp_utf16.hpp │ │ │ │ │ ├── cp_utf32.hpp │ │ │ │ │ ├── cp_utf8.hpp │ │ │ │ │ ├── cp_utfw.hpp │ │ │ │ │ ├── utf_config.hpp │ │ │ │ │ ├── utf_converters.hpp │ │ │ │ │ ├── utf_selector.hpp │ │ │ │ │ └── utf_sizes.hpp │ │ │ │ └── variant/ │ │ │ │ ├── LICENSE.md │ │ │ │ ├── README.md │ │ │ │ ├── cmake/ │ │ │ │ │ └── mpark_variant-config.cmake.in │ │ │ │ └── include/ │ │ │ │ └── mpark/ │ │ │ │ ├── config.hpp │ │ │ │ ├── in_place.hpp │ │ │ │ ├── lib.hpp │ │ │ │ └── variant.hpp │ │ │ ├── RiderBlueprint/ │ │ │ │ ├── Private/ │ │ │ │ │ ├── BlueprintProvider.cpp │ │ │ │ │ └── RiderBlueprint.cpp │ │ │ │ ├── Public/ │ │ │ │ │ ├── BlueprintProvider.hpp │ │ │ │ │ └── RiderBlueprint.hpp │ │ │ │ └── RiderBlueprint.Build.cs │ │ │ ├── RiderDebuggerSupport/ │ │ │ │ ├── Private/ │ │ │ │ │ ├── BlueprintStackGetter.cpp │ │ │ │ │ ├── DebugLogger.cpp │ │ │ │ │ ├── DebugLogger.h │ │ │ │ │ ├── RiderDebuggerSupport.cpp │ │ │ │ │ ├── RiderDebuggerSupport.h │ │ │ │ │ ├── UnrealFunctions.cpp │ │ │ │ │ ├── UnrealFunctions.h │ │ │ │ │ ├── WideStringWrapper.cpp │ │ │ │ │ └── WideStringWrapper.h │ │ │ │ └── RiderDebuggerSupport.Build.cs │ │ │ ├── RiderGameControl/ │ │ │ │ ├── Private/ │ │ │ │ │ └── RiderGameControl.cpp │ │ │ │ ├── Public/ │ │ │ │ │ └── RiderGameControl.hpp │ │ │ │ └── RiderGameControl.Build.cs │ │ │ ├── RiderLC/ │ │ │ │ ├── Private/ │ │ │ │ │ ├── RiderLC.cpp │ │ │ │ │ └── RiderLC.hpp │ │ │ │ └── RiderLC.Build.cs │ │ │ ├── RiderLink/ │ │ │ │ ├── Private/ │ │ │ │ │ ├── ProtocolFactory.cpp │ │ │ │ │ ├── ProtocolFactory.h │ │ │ │ │ ├── RiderLink.cpp │ │ │ │ │ ├── RiderLink.hpp │ │ │ │ │ └── UE4TypesMarshallers.cpp │ │ │ │ ├── Public/ │ │ │ │ │ ├── IRiderLink.hpp │ │ │ │ │ └── UE4TypesMarshallers.h │ │ │ │ └── RiderLink.Build.cs │ │ │ ├── RiderLogging/ │ │ │ │ ├── Private/ │ │ │ │ │ ├── RiderLogging.cpp │ │ │ │ │ ├── RiderLogging.hpp │ │ │ │ │ ├── RiderOutputDevice.cpp │ │ │ │ │ └── RiderOutputDevice.hpp │ │ │ │ └── RiderLogging.Build.cs │ │ │ └── RiderShaderInfo/ │ │ │ ├── RiderShaderInfo.Build.cs │ │ │ ├── RiderShaderInfo.cpp │ │ │ └── RiderShaderInfo.h │ │ ├── Subplatform.Root │ │ └── Subplatform.Snk │ ├── dotnet/ │ │ ├── BuildScript/ │ │ │ └── DefineUnrealLinkConstants.cs │ │ ├── Directory.Build.props │ │ ├── PackagesLock.targets │ │ ├── Plugin.props │ │ ├── RiderPlugin.UnrealLink/ │ │ │ ├── BuildScript/ │ │ │ │ └── UnrealLinkInRiderProduct.cs │ │ │ ├── EditorNavigator.cs │ │ │ ├── Install/ │ │ │ │ └── AdvertiseRiderBundledPlugin.cs │ │ │ ├── LinkResolver.cs │ │ │ ├── PluginInstaller/ │ │ │ │ ├── PluginPathsProvider.cs │ │ │ │ ├── UnrealPluginDetector.cs │ │ │ │ ├── UnrealPluginInstallInfo.cs │ │ │ │ ├── UnrealPluginInstaller.cs │ │ │ │ └── UnrealProjectsRefresher.cs │ │ │ ├── Resources/ │ │ │ │ ├── Strings.Designer.cs │ │ │ │ └── Strings.resx │ │ │ ├── RiderBackendToUnrealEditor.cs │ │ │ ├── RiderPlugin.UnrealLink.csproj │ │ │ ├── Settings/ │ │ │ │ └── UnrealLinkOptionsPage.cs │ │ │ ├── UnrealEngineAssetsNavigationProvider.cs │ │ │ ├── UnrealHost.cs │ │ │ ├── UnrealLiveCodingBuildRunner.cs │ │ │ ├── Utils/ │ │ │ │ ├── CmdUtils.cs │ │ │ │ ├── FsUtils.cs │ │ │ │ └── ModelUtils.cs │ │ │ └── ZoneMarker.cs │ │ ├── Subplatform.Root │ │ ├── Subplatform.Snk │ │ └── Versions.props │ └── rider/ │ ├── main/ │ │ ├── kotlin/ │ │ │ ├── com/ │ │ │ │ └── jetbrains/ │ │ │ │ └── rider/ │ │ │ │ ├── UnrealLinkBundle.kt │ │ │ │ ├── plugins/ │ │ │ │ │ └── unreal/ │ │ │ │ │ ├── RiderLinkInstallService.kt │ │ │ │ │ ├── UE4LibraryStartUpSerializerRegistrar.kt │ │ │ │ │ ├── UnrealHost.kt │ │ │ │ │ ├── UnrealLinkSettings.kt │ │ │ │ │ ├── UnrealPluginUsagesCollector.kt │ │ │ │ │ ├── actions/ │ │ │ │ │ │ ├── ActionsUtils.kt │ │ │ │ │ │ ├── CancelRiderLinkInstallAction.kt │ │ │ │ │ │ ├── EnableAutoUpdatePlugin.kt │ │ │ │ │ │ ├── FilterCheckboxAction.kt │ │ │ │ │ │ ├── HotReloadBuildAction.kt │ │ │ │ │ │ ├── HotReloadBuildModeProvider.kt │ │ │ │ │ │ ├── InstallActions.kt │ │ │ │ │ │ ├── PlayActions.kt │ │ │ │ │ │ ├── PlaySettingsActions.kt │ │ │ │ │ │ └── UnrealHostOperations.kt │ │ │ │ │ ├── debugger/ │ │ │ │ │ │ ├── BlueprintCallstackFrameCompatibilityMatcher.kt │ │ │ │ │ │ ├── BlueprintCallstackFrameMatchResult.kt │ │ │ │ │ │ ├── BlueprintExecutionStack.kt │ │ │ │ │ │ ├── BlueprintStackFrameContainer.kt │ │ │ │ │ │ ├── BlueprintStackTransformer.kt │ │ │ │ │ │ ├── UnrealDebugProcessExtension.kt │ │ │ │ │ │ ├── UnrealDebuggerLogger.kt │ │ │ │ │ │ ├── actions/ │ │ │ │ │ │ │ ├── FramesPopupGroup.kt │ │ │ │ │ │ │ ├── ShowBlueprintFunctionsAction.kt │ │ │ │ │ │ │ ├── ShowLibraryFramesAction.kt │ │ │ │ │ │ │ ├── ShowUnrealFramesAction.kt │ │ │ │ │ │ │ └── UnrealToggleBaseAction.kt │ │ │ │ │ │ └── frames/ │ │ │ │ │ │ ├── BlueprintFrame.kt │ │ │ │ │ │ ├── BlueprintFrameMessages.kt │ │ │ │ │ │ ├── StubBlueprintFrame.kt │ │ │ │ │ │ ├── UnrealExternalCodeFrame.kt │ │ │ │ │ │ └── UnrealFrameBase.kt │ │ │ │ │ ├── filters/ │ │ │ │ │ │ └── linkInfo/ │ │ │ │ │ │ ├── BlueprintClassHyperLinkInfo.kt │ │ │ │ │ │ ├── BlueprintFunctionHyperLinkInfo.kt │ │ │ │ │ │ ├── MethodReferenceHyperLinkInfo.kt │ │ │ │ │ │ └── UnrealClassHyperLinkInfo.kt │ │ │ │ │ ├── notifications/ │ │ │ │ │ │ └── OutOfSyncPluginProtocolListener.kt │ │ │ │ │ ├── spellchecker/ │ │ │ │ │ │ └── UnrealBundledDictionaryProvider.kt │ │ │ │ │ └── toolWindow/ │ │ │ │ │ ├── UnrealToolWindowFactory.kt │ │ │ │ │ └── log/ │ │ │ │ │ ├── UnrealLogCategoryFilterComboBox.kt │ │ │ │ │ ├── UnrealLogConsoleActionsPostProcessor.kt │ │ │ │ │ ├── UnrealLogConsoleViewContentType.kt │ │ │ │ │ ├── UnrealLogFilter.kt │ │ │ │ │ ├── UnrealLogPanel.kt │ │ │ │ │ ├── UnrealLogPanelSettings.kt │ │ │ │ │ ├── UnrealLogSettings.kt │ │ │ │ │ └── UnrealLogVerbosityFilterComboBox.kt │ │ │ │ └── settings/ │ │ │ │ ├── UnrealLinkSettingsConfigurable.kt │ │ │ │ ├── UnrealLogColorSettingsPage.kt │ │ │ │ └── UnrealLogSettingsConfigurable.kt │ │ │ └── icons/ │ │ │ └── UnrealIcons.kt │ │ └── resources/ │ │ ├── META-INF/ │ │ │ └── plugin.xml │ │ ├── UnrealIconMappings.json │ │ ├── com/ │ │ │ └── jetbrains/ │ │ │ └── rider/ │ │ │ └── plugins/ │ │ │ └── unreal/ │ │ │ └── spellchecker/ │ │ │ └── unreal.dic │ │ ├── icon-robots.txt │ │ ├── icons/ │ │ │ └── expui/ │ │ │ └── icon-robots.txt │ │ └── messages/ │ │ └── UnrealLinkBundle.properties │ └── test/ │ ├── kotlin/ │ │ └── com/ │ │ └── jetbrains/ │ │ └── rider/ │ │ └── plugins/ │ │ └── unreal/ │ │ └── test/ │ │ └── cases/ │ │ ├── RiderTestDataMarker.kt │ │ └── integrationTests/ │ │ ├── Notification.kt │ │ ├── PlayButtons.kt │ │ ├── UnrealLinkBase.kt │ │ ├── UnrealLinkInstallation.kt │ │ └── projectModel/ │ │ └── RefreshSolution.kt │ ├── resources/ │ │ ├── episodes/ │ │ │ └── Plugins/ │ │ │ ├── UnrealIntegration.xml │ │ │ └── UnrealLinkSmoke.xml │ │ └── intellij.rider.plugins.unreal.link.test.cases.xml │ └── testData/ │ ├── integrationTests/ │ │ └── projectModel/ │ │ └── RefreshSolution/ │ │ └── refreshSolution/ │ │ ├── refreshSolution.gold │ │ ├── refreshSolution_4_27.gold │ │ ├── refreshSolution_5_5.gold │ │ ├── refreshSolution_text.gold │ │ ├── refreshSolution_text_4_27.gold │ │ ├── refreshSolution_text_5_5.gold │ │ └── refreshSolution_text_5_7.gold │ ├── solutions/ │ │ └── TestSolutions/ │ │ └── Unreal/ │ │ └── EmptyUProject/ │ │ ├── EmptyUProject.uproject │ │ └── Source/ │ │ ├── EmptyUProject/ │ │ │ ├── EmptyUProject.Build.cs │ │ │ ├── EmptyUProject.cpp │ │ │ └── EmptyUProject.h │ │ ├── EmptyUProject.Target.cs │ │ └── EmptyUProjectEditor.Target.cs │ └── unreal/ │ └── additionalSource/ │ └── plugins/ │ └── EmptyTestPlugin/ │ ├── EmptyTestPlugin.uplugin │ └── Source/ │ └── EmptyTestPlugin/ │ ├── EmptyTestPlugin.Build.cs │ ├── Private/ │ │ └── EmptyTestPlugin.cpp │ └── Public/ │ └── EmptyTestPlugin.h └── tools/ └── dotnet.cmd ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ root = true [*.{csproj,props,targets}] indent_style = space indent_size = 2 ================================================ FILE: .gitattributes ================================================ # Set the default behavior, in case people don't have core.autocrlf set. * text=auto # Keep LF in shell scripts *.sh text eol=lf # Preserve line endings in gradle scripts gradlew* -text diff ================================================ FILE: .github/workflows/build.yml ================================================ name: Build UnrealLink on: push: branches: [ 'net212**' ] paths-ignore: - src/rider/test - testData - README.md - CHANGELOG.md pull_request: branches: [ 'net212**' ] paths-ignore: - src/rider/test - testData - README.md - CHANGELOG.md workflow_dispatch: inputs: arguments: description: 'Additional attributes to gradle buildPlugin' required: false default: '' jobs: build: name: ${{ matrix.config.name }} runs-on: ${{ matrix.config.os }} strategy: fail-fast: false matrix: config: - { name: "Windows Latest", artifact-prefix: "UnrealLink-", os: windows-latest, SEP: \ } # Steps represent a sequence of tasks that will be executed as part of the job steps: - uses: actions/checkout@v2 # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/cache@v2 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} restore-keys: ${{ runner.os }}-gradle- - name: Build Plugin run: gradlew.bat buildPlugin -PbuildCounter=${{ github.run_number }} -PbuildConfiguration=Release ${{ github.event.inputs.arguments }} shell: cmd - name: Upload artifacts if: success() uses: actions/upload-artifact@v2 with: name: ${{ matrix.config.artifact-prefix }}artifacts path: output/** ================================================ FILE: .gitignore ================================================ # Build artifacts [Bb]in/ [Oo]bj/ build output .gradle .tmp out/* *.generated.* NuGet.Config # User-specific files *.suo *.user *.sln.docstates *.sln.binlog *.cache # IDEs .idea .vs _ReSharper* _dotTrace* # Unreal Plugin generated files src/cpp/RiderLink/RiderLink.uplugin src/cpp/RiderLink/checksum src/cpp/RiderLink/Binaries src/cpp/RiderLink/Intermediate src/cpp/RiderLink/Source/RiderLink/Public/Model # rd-cpp extra files CMakeLists.txt src/cpp/RiderLink/Source/RD/**/.* src/cpp/RiderLink/Source/RD/**/*.yml src/cpp/RiderLink/Source/RD/**/test src/cpp/RiderLink/Source/RD/**/tests src/cpp/RiderLink/Source/RD/**/bench src/cpp/RiderLink/Source/RD/**/example src/cpp/RiderLink/Source/RD/**/pch.* src/cpp/RiderLink/Source/RD/**/INSTALL src/cpp/RiderLink/Source/RD/**/doxygen.conf # Local setup specific files UnrealEngineProjectPath.txt dependencies/* /.intellijPlatform/ # Tmp release files release_notes.md # Zipper tools/Zipper/* !tools/Zipper/Program.cs !tools/Zipper/Zipper.csproj !tools/Zipper/Zipper.sln /src/cpp/RiderLink/Resources/checksum ================================================ FILE: .run/Debug backend.run.xml ================================================ ================================================ FILE: .run/UnrealLink [runIde].run.xml ================================================ true true false false ================================================ FILE: CHANGELOG.md ================================================ # Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0). Note that this project does not follow semantic versioning but uses version numbers based on JetBrains [Rider](https://www.jetbrains.com/rider/) releases. This plugin contains a plugin for the Unreal Editor (RiderLink) that is used to communicate with Rider. Changes marked with a "Rider:" prefix are specific to Rider, while changes for the Unreal Editor plugin are marked with a "Unreal Editor:" prefix. No prefix means that the change is common to both Rider and ReSharper. The plugin is always bundled with Rider. ## [Unreleased] ## [2023.1.0.282] ### Fixed - [RIDER-93789](https://youtrack.jetbrains.com/issue/RIDER-93789) ## [2023.1.0.188] ### Fixed - Navigating from Rider to Unreal Editor for DataAssets ## [2022.3.0.200] ### Changed - Add option to specify intermediate folder for building RiderLink plugin (File | Settings | Languages & Frameworks | Unreal Engine). - If you have non-ASCII symbols in the path, your RiderLink build might fail. - Add option to specify behavior of the auto-update - Select between automatically building and installing plugin, or simply extracting sources of RiderLink and building them yourselves - Extracting plugin might be a better option for RiderLink installed in the Game projects, or Engine project built from the sources ### Fixed - Support installing RiderLink into the pre-built version of the UE5 (eg installed through Epic Games Launcher) - From now on, RiderLink will be installed in the `Engine/Plugins/Marketplace/Developer/RiderLink` folder, if you chose to install it in the Engine - Initially, RiderLink will be installed into `Plugins/Developer/RiderLink` for Game project and `Engine/Plugins/Marketplace/Developer/RiderLink` for Engine project, but if you wish to move it to another folder, Rider will take into the account the new location and will be updating the plugin in place. ## [2022.3.0.178] ### Changed - Disable installation to Engine for UE 5 - [RIDER-71374](https://youtrack.jetbrains.com/issue/RIDER-71374) - Unreal log panel was made more consistent with output log window in Unreal Editor - Log categories are now sorted in popup ([RIDER-61132](https://youtrack.jetbrains.com/issue/RIDER-61132)) - Log verbosity and category popups do not hide on item toggling - Full log messages are colorized, not only category name ### Fixed - Fix [RIDER-69165](https://youtrack.jetbrains.com/issue/RIDER-69165) - Fix [DEXP-642989](https://youtrack.jetbrains.com/issue/DEXP-642989) ## [2021.2.1.4] ### Fixed - Fix [RIDER-66784](https://youtrack.jetbrains.com/issue/RIDER-66784) - Possibly fix [RIDER-67192](https://youtrack.jetbrains.com/issue/RIDER-67192) ## [2021.2.1.3] ### Fixed - [GITHUB-135](https://github.com/JetBrains/UnrealLink/issues/135) ## [2021.2.1.2] ### Fixed - [GITHUB-135](https://github.com/JetBrains/UnrealLink/issues/135) ## [2021.2.1.1] ### Changed - Sending logs from Unreal Editor to Rider moved to separate thread, less load on UE UI thread ### Fixed - [RIDER-68865](https://youtrack.jetbrains.com/issue/RIDER-68865) - Unreal log messages are not limited anymore with 4KB size ## [2021.2.0.659] ### Fixed - Finally fix for [GITHUB-99](https://github.com/JetBrains/UnrealLink/issues/99) - as well as [GITHUB-97](https://github.com/JetBrains/UnrealLink/issues/97) - Number of fixes for clashing "Project Model Generation" with "Building RiderLink" actions blocking UnrealBuildTool. They don't have dedicated issues in YT or GH, but has producing annoying results like: - "can't generate project model, because UBT is running" message; - Building RiderLink and failing would leave a hanging UBT process. ### Known Issues ## [2021.1.12] ### Added ### Changed ### Deprecated ### Removed ### Fixed - HOTFIX building RideLink on MacOS ### Known Issues ## [2021.1.11] ### Added ### Changed ### Deprecated ### Removed ### Fixed - [RIDER-63716](https://youtrack.jetbrains.com/issue/RIDER-63716) - [RIDER-63584](https://youtrack.jetbrains.com/issue/RIDER-63584) - [RIDER-64422](https://youtrack.jetbrains.com/issue/RIDER-64422) - [GITHUB-109](https://github.com/JetBrains/UnrealLink/issues/109) - [GITHUB-112](https://github.com/JetBrains/UnrealLink/issues/112) - Possible fix for [GITHUB-99](https://github.com/JetBrains/UnrealLink/issues/99) - Fix build RiderLink for UE5 main branch ### Known Issues ## [2021.1.10] ### Added ### Changed ### Deprecated ### Removed ### Fixed - [RIDER-63716](https://youtrack.jetbrains.com/issue/RIDER-63716) - [RIDER-63584](https://youtrack.jetbrains.com/issue/RIDER-63584) - [GITHUB-109](https://github.com/JetBrains/UnrealLink/issues/109) - Possible fix for [GITHUB-99](https://github.com/JetBrains/UnrealLink/issues/99) - Fix build RiderLink for UE5 main branch ### Known Issues ## [2021.1.9] ### Added ### Changed ### Deprecated ### Removed ### Fixed - [RIDER-63716](https://youtrack.jetbrains.com/issue/RIDER-63716) - [RIDER-63584](https://youtrack.jetbrains.com/issue/RIDER-63584) - [GITHUB-109](https://github.com/JetBrains/UnrealLink/issues/109) - Possible fix for [GITHUB-99](https://github.com/JetBrains/UnrealLink/issues/99) - Fix build RiderLink for UE5 main branch ### Known Issues ## [2021.1.8] ### Added ### Changed ### Deprecated ### Removed ### Fixed - Possible fix for https://youtrack.jetbrains.com/issue/RIDER-62012 - UE crashing on exit in RiderLink - Fix building RiderLink on UE5 - Fix https://github.com/JetBrains/UnrealLink/issues/93 ### Known Issues ## [2021.1.7] ### Added - Option to cancell installation of RiderLink ### Changed ### Deprecated - Dropped support for versions of UE 4.22 and older ### Removed ### Fixed - Fixed [Rider-51111](https://youtrack.jetbrains.com/issue/RIDER-51111) - Fixed installation on macOS - Failing to install RiderLink due to other UBT processes running ### Known Issues ## [2021.1.6] ### Added ### Changed ### Deprecated ### Removed ### Fixed - Fixed [Rider-51111](https://youtrack.jetbrains.com/issue/RIDER-51111) - Fixed installation on macOS ### Known Issues ## [2021.1.5] ### Added ### Changed ### Deprecated ### Removed ### Fixed - Fixed [Rider-51111](https://youtrack.jetbrains.com/issue/RIDER-51111) - Fixed installation on macOS ### Known Issues ## [2021.1.4] ### Added ### Changed ### Deprecated ### Removed ### Fixed - Fixed potential issues with inconsistent protocol state on disconnects - Reinitialize protocol each time - Fixed bunch of issues with game control actions - Use PIE actions logic from UE4 instead of reimplementing it - Actions are no more stuck in disabled state after engine unexpected shutdowns ### Known Issues - "Compile before play" option is not properly supported - Actions visibility and availability states are not synchronized with editor ## [2021.1.3] ### Added - Disabling Play and Play settings button after press - Rider should reflect state of Unreal Editor. Play buttons should change only when Unreal Editor notifies that they have changed ### Changed ### Deprecated ### Removed ### Fixed - Fix: sync state of play buttons and play settings buttns between Rider and Unreal Editor - Fix: don't install RiderLink into `UnrealLaunchDaemon` project ### Known Issues ## [2021.1.2] ### Added ### Changed ### Deprecated ### Removed ### Fixed - Exception on generitng project files during installation of RiderLink plugin - Rider not being able to connect to Unreal Editor ### Known Issues ## [2021.1.1] ### Added - Support MacOS and Linux - Installing RiderLink plugin logs show in Build panel ### Changed ### Deprecated ### Removed ### Fixed ### Known Issues ## [2020.3.116] ### Added ### Changed ### Deprecated ### Removed ### Fixed - Fix https://github.com/JetBrains/UnrealLink/issues/62 ### Known Issues ## [2020.3.113] ### Added ### Changed - Build RiderLink plugin from source on user PC instead of bundling dll files. - Reason: frequent changes of toolchains and breaking of ABI compatibility in MSVC toolchain render this practice impossible. ### Deprecated - Drop support for UE 4.21 and older - Reason: RD framework doesn't use PCH files and doesn't comply with UE rules for include files. It's impossible to state that module doesn't use PCH files in UE 4.21 and older. ### Removed ### Fixed ### Known Issues ## [2020.3.104] ### Added - Proper README for the https://github.com/JetBrains/UnrealLink ### Changed ### Deprecated ### Removed ### Fixed - Fix memory leak in log panel on exit of Rider - Fix deprecated code ### Known Issues ## [2020.3.85] ### Added - Supported Rider 2020.3 ### Changed ### Deprecated ### Removed ### Fixed ### Known Issues ## [2020.2.83] ### Added ### Changed ### Deprecated ### Removed ### Fixed - More fixes to https://youtrack.jetbrains.com/issue/RIDER-50354 ### Known Issues ## [2020.2.79] ### Added - More info and notifications when RiderLink can't be built - Unable auto-update plugin from JetBrains Marketplace ### Changed ### Deprecated ### Removed - Removed verbose log messages from RD framework in stdout ### Fixed - Fix https://youtrack.jetbrains.com/issue/RIDER-50354 - Fix corrupted hyperlinks after toggling timestamps ### Known Issues ## [2020.2.69] ### Added ### Changed ### Deprecated ### Removed ### Fixed - Fix https://github.com/JetBrains/UnrealLink/issues/46 ### Known Issues ## [2020.2.67] ### Added ### Changed ### Deprecated ### Removed ### Fixed - Fix showing popup "update project file" after installing RiderLink - After pressing "update project file", it'd update {GameProject}.uproject file, adding reference to RiderLink. This is not necessary for RiderLink to work properly, but it'll break project for people that will sink to {GameProject}.uproject, but who doesn't have RiderLink installed ### Known Issues ## [2020.2.66] ### Added ### Changed ### Deprecated ### Removed ### Fixed - Fix https://github.com/JetBrains/UnrealLink/issues/40 - Fix https://youtrack.jetbrains.com/issue/RIDER-47839 ### Known Issues ## [2020.2.62] ### Added ### Changed ### Deprecated ### Removed ### Fixed - Fixed: https://github.com/JetBrains/UnrealLink/issues/19 ### Known Issues ## [2020.2.61] ### Added - Unreal log filters - Verbosity - Category - Show/hide timestamps ### Changed - Generated files for RD protocol will have ".Generated.h" suffix ### Deprecated ### Removed - Packing plugin for ReSharper - Not supported yet ### Fixed - Fixed: building RiderLink with Unity build disabled - Fixed: Wording in "Install RiderLink" popup - Fixed: Wording in UnrealEngine settings - No more auto-install plugin, only auto-update plugin ### Known Issues ## [2020.2.36] ### Added ### Changed - rd-cpp updated to 201.1.85 ### Deprecated ### Removed ### Fixed - Fixed stackoverflow crash on exit of Unreal Editor ### Known Issues ## [2020.2.34] ### Added ### Changed - move plugin installation in backend thread - no more freeze of UI when installing RiderLink ### Deprecated ### Removed ### Fixed - fix loading RiderLink plugin on cooking - fix crash on closing UnrealEditor in RiderLoggingExtension - fixed number of problems discovering Unreal Editor installation, should fix a lot of false failing installations of RiderLink - fix wording in RiderLink settings panel ( auto update and install -> auto update) - fix UnrealBuildTool removing EnableByDefault: true from RiderLink.uplugin - No more modification of {GameName}.uproject file to enable RiderLink ### Known Issues ## [2020.1.10] ### Added - Add option to install plugin to either Engine or Game - Installing to Engine from EGS requires building plugin - Old versions of plugins will be backed up - New version will be copied to %USER% folder and built - Result binaries copyied to Engine or Game - Sln will be refreshed - In case of errors, all the results will be reverted ### Changed - Moved thirdparty dependencies of RD to submodules, fixed build.kts for it ### Deprecated ### Removed ### Fixed - [Extremely long log entry causes RiderLink to lose connection to Unreal Engine](https://github.com/JetBrains/UnrealLink/issues/17) - Fix looking for Unreal Engine root when Game is placed in the same folder as Engine - Fix RiderLink for non-Unity builds ### Known Issues ## [2020.1.8] ### Added - Add progress bar when installing "RiderLink" plugin - Hide notification suggesting installing RiderLink after selecting "Install plugin" - [Add option to Compile Game Project before starting PIE](https://github.com/JetBrains/UnrealLink/issues/9) ### Changed - Installation to Engine instead of game Project - [Add option to install RiderLink to Engine instead of Game Project](https://github.com/JetBrains/UnrealLink/issues/7) - During installation process, RiderLink will be built and put into correct folder - No more gimmicks with "build plugin in Game project, then copy-paste it in Engine project" - Refactored "RiderLink" - Separated "RiderLink" into separate module with different loading steps - e.g. For "RiderLink', start as soon as possible, for "RiderLinkGameControlExtension" start after Unreal Editor is fully loaded, etc - Fixed all warnings (TEXT macro redefined, obsolete API, etc) ### Deprecated ### Removed ### Fixed - Lower verbosity of RD protocol logger - Less noise in debug console - [Build on non-unity projects](https://github.com/JetBrains/UnrealLink/issues/10) - [Exception during installation of RiderLink](https://github.com/JetBrains/UnrealLink/issues/5) - [Multiple popups suggesting installing RiderLink](https://youtrack.jetbrains.com/issue/RIDER-43928) - Bunch of issues: - https://youtrack.jetbrains.com/issue/RIDER-44056 - https://youtrack.jetbrains.com/issue/RIDER-44027 - https://youtrack.jetbrains.com/issue/RIDER-43929 ### Known Issues ## [2020.1.7] ### Added ### Changed ### Deprecated ### Removed ### Fixed - [Incorrect time in log](https://github.com/JetBrains/UnrealLink/issues/8) - [Infinite process: RiderLink Discovering Asset Data](https://youtrack.jetbrains.com/issue/RIDER-43353) - Spawn of multiple tabs when sending huge log message from Unreal Editor to Rider ### Known Issues ## [2020.1.6] ### Added - FileWatcher initialization moved to separate thread, speed up startup time for plugin on RIder side - New icons for Play actions and connection status ### Changed - Cleaned up severity of internal logs - Were polluting package logs ### Deprecated ### Removed ### Fixed - Connecting to Unreal Editor when plugin is installed in Engine ### Known Issues ## [2020.1.5] ### Added ### Changed ### Deprecated ### Removed ### Fixed - [Cant close Unreal log window](https://github.com/JetBrains/UnrealLink/issues/2) - Disabled spawning multiple tabs for logs. Only one log tab for Unreal Editor log will be available at all times - [Rider installs plugins to Programs](https://github.com/JetBrains/UnrealLink/issues/6) - [Exception: The process cannot access the file because it is being used by another process](https://github.com/JetBrains/UnrealLink/issues/5) - [Crash in RiderLink when packaging project](https://github.com/JetBrains/UnrealLink/issues/4) ### Known Issues ## [2020.1.4] ### Added - Option to enable auto-update Unreal Editor (RiderLink) plugin - Notification after plugin installation ### Changed ### Deprecated ### Removed ### Fixed - Not finding uproject files on loading solution - [UnrealLink plugin generates wrong sln for Rocket builds of UE](https://github.com/JetBrains/UnrealLink/issues/1) ### Known Issues ## [2020.1.2] ### Added ### Changed ### Deprecated ### Removed ### Fixed - Fixed: when adding RiderLink plugin to project, Rider would regenerate sln and It would add Programs folder to solution tree for Rocket builds (UE from EGS) - Fixed: building plugin on v4.20+ - Fixed: Use user temp folder instead of the project folder for intermediate file operations to avoid asking for elevated options ### Known Issues ## [2020.1.1] ### Added - Open Blueprints from Rider - Show logs of Unreal Editor inside Rider with hyperlinks to Blueprints inside Rider - Toolbar buttons to control playback of the game inside Unreal Editor - Play, Pause, Stop, Skip frame; - Setup multiplayer: number of players, dedicated server, etc. - Notification if connection to Unreal Editor is established or not (bottom right corner of status bar); - Auto-install plugins in project; - Setting to enable auto-install (`true` by default). - If auto-install is disabled - notification will show up with quick-fix action. ### Changed ### Deprecated ### Removed ### Fixed ### Known Issues - Service for opening assets in Unreal Editor has changed starting with UE 4.24 - opening same BP multiple times will open multiple tabs instead of re-using already opened one; - Icon for connection status looks weird, waiting for new icons from designers - Connecting to multiple instances of Unreal Editor is not working correctly - Toolbar controls will be affecting only the last connected editor ================================================ FILE: CODE_OF_CONDUCT.md ================================================ ## Code of Conduct This project and the corresponding community is governed by the [JetBrains Open Source and Community Code of Conduct](https://confluence.jetbrains.com/display/ALL/JetBrains+Open+Source+and+Community+Code+of+Conduct). Please make sure you read it. ================================================ FILE: License.txt ================================================ Apache License Version 2.0, January 2004 https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0.txt Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================ [![official JetBrains project](https://jb.gg/badges/official-flat-square.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) # UnrealLink for Unreal Engine

The UnrealLink plugin enables advanced integration between JetBrains Rider and Epic Games’ Unreal Editor.

Rider is a fast and powerful IDE for Unreal Engine and C++ development. It provides rich code navigation, inspections, refactorings, understands Blueprints and the Unreal Engine reflection mechanism, and supports HLSL.

The plugin brings Blueprints information to the editor, adds settings to manage game launch, and provides a more convenient version of the Unreal Editor log.

Plugin structure

There are two plugins under the hood, the UnrealLink plugin for Rider and the RiderLink plugin for Unreal Editor, packed together.

Installation

UnrealLink is bundled with Rider and also distributed via the JetBrains plugin marketplace.

RiderLink is installed by Rider itself, there is no need to install it manually. The first time you open an Unreal Engine project in Rider, you'll see a notification that the RiderLink plugin is missing and an invitation to install it. If you skip this popup message, you can install the plugin later by going to the Rider settings on the Languages and Frameworks | Unreal Engine page.

Both the popup message and the settings page offer two installation options:

Contribute to the plugin

We love contributions! If you want to contribute to the UnrealLink plugin, fork this repository and follow the instructions on how to set up a development environment.

Features

Interact with blueprints

Blueprint files are written in binary form and are usually edited visually. However, they contain a whole lot of useful information for the developers of the C++ part of the game.

Rider reads Blueprints and allows you to see the bigger picture behind your code:

UnrealLink extends this functionality and introduces the ability to navigate to the Blueprint inside the Unreal Editor from your C++ code.

Interact with blueprints

Manage the game

The plugin allows you to manage your game right inside the IDE: select the running mode, run a server for your multiplayer game, specify the number of players, and more.

Manage the game

Browse the Unreal Editor log

UnrealLink offers you an enhanced version of the Unreal Editor log output panel with colored text for easy reading, as well as verbosity and event category filters. You can also click on any highlighted link to navigate to the related source code line.

Browse the Unreal Editor log Learn more about Rider for Unreal Engine >>

What could possibly go wrong?

The plugin and Rider for Unreal Engine itself are in active development now, so there could be some issues. Please share your feedback and report any bugs you encounter:

A few typical issues, and what to do in such cases:

Failed to build RiderLink plugin
``` Failed to build RiderLink plugin Check build logs for more info Help > Diagnostic Tools > Show Log in Explorer And contact dev team for help at GitHub Issues page ```

There are several reasons you might get this message:

Failed to backup old plugin
``` Failed to backup old plugin Close all running instances of Unreal Editor and try again Path to old plugin: ```

You tried to install a new version of RiderLink while you have a running instance of Unreal Editor with the plugin installed. Please close Unreal Editor and try again to install the plugin.

Failed to refresh project files

This warning message means that installation was successful, but updating the project files in Rider failed. Everything should work fine, except the plugin will not appear in the /Plugins/Developer folder in the Explorer view.

If you have any issues with the plugin that you can’t resolve, please contact the developers via GitHub Issues.

================================================ FILE: SETUP.md ================================================

Setting up development environment

After cloning the repository, you need to perform some actions to set up the environment.

[TBD] Setting up UnrealLink frontend

Setting up UnrealLink backend

Here you go! Now you can open UnrealLink.sln and work on backend code.

Setting up RiderLink, Unreal Editor plugin

Code for the Unreal Editor part is stored inside UnrealLink repository, but in order to work with cpp code, it should be a part of Unreal Engine project. As a workaround, we provide an option to create a junction from RiderLink source folder to Unreal Engine game project. That way, when you make changes to RiderLink plugin inside Unreal Engine project, they will be picked up automatically in git.

RiderLink plugin will be available under {GameProjectRoot}/Plugins/Development/RiderLink

================================================ FILE: SdkBasedPluginEmbedding.Root ================================================ ================================================ FILE: SubplatformsCollection.Root ================================================ ================================================ FILE: UnrealLink.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.29123.88 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RiderPlugin.UnrealLink", "src\dotnet\RiderPlugin.UnrealLink\RiderPlugin.UnrealLink.csproj", "{084172D1-A9C6-46D0-96AD-05C5B09A5E5D}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "misc", "misc", "{4A9ABB95-3762-448B-B5BF-099E46DB22DE}" ProjectSection(SolutionItems) = preProject CHANGELOG.md = CHANGELOG.md src\dotnet\Plugin.props = src\dotnet\Plugin.props src\rider\main\resources\META-INF\plugin.xml = src\rider\main\resources\META-INF\plugin.xml README.md = README.md EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {084172D1-A9C6-46D0-96AD-05C5B09A5E5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {084172D1-A9C6-46D0-96AD-05C5B09A5E5D}.Debug|Any CPU.Build.0 = Debug|Any CPU {084172D1-A9C6-46D0-96AD-05C5B09A5E5D}.Release|Any CPU.ActiveCfg = Release|Any CPU {084172D1-A9C6-46D0-96AD-05C5B09A5E5D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {AE34A340-CA1E-400F-9367-864EEF8033C7} EndGlobalSection EndGlobal ================================================ FILE: build.gradle.kts ================================================ import com.jetbrains.plugin.structure.base.utils.isFile import org.apache.tools.ant.taskdefs.condition.Os import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.jetbrains.changelog.Changelog import org.jetbrains.intellij.platform.gradle.tasks.PrepareSandboxTask import org.jetbrains.intellij.platform.gradle.tasks.RunIdeTask import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import java.io.ByteArrayOutputStream import java.security.MessageDigest import javax.inject.Inject import org.gradle.process.ExecOperations import kotlin.io.path.absolute import kotlin.io.path.isDirectory import org.jetbrains.intellij.platform.gradle.Constants import org.jetbrains.intellij.platform.gradle.TestFrameworkType import kotlin.io.path.pathString plugins { id("me.filippov.gradle.jvm.wrapper") version "0.16.0" id("org.jetbrains.changelog") version "2.0.0" id("org.jetbrains.intellij.platform") kotlin("jvm") } interface Injected { @get:Inject val exec: ExecOperations } val injected = objects.newInstance() java { sourceCompatibility = JavaVersion.VERSION_25 targetCompatibility = JavaVersion.VERSION_25 } repositories { maven("https://cache-redirector.jetbrains.com/intellij-dependencies") maven("https://cache-redirector.jetbrains.com/intellij-repository/releases") maven("https://cache-redirector.jetbrains.com/intellij-repository/snapshots") maven("https://cache-redirector.jetbrains.com/maven-central") intellijPlatform { defaultRepositories() jetbrainsRuntime() } } dependencies { testImplementation("com.fasterxml.jackson.core:jackson-databind:2.14.0") } apply { plugin("kotlin") } kotlin { sourceSets { main { kotlin.srcDir("src/rider/main/kotlin") } test { kotlin.srcDir("src/rider/test/kotlin") } } } sourceSets { main { kotlin.srcDir("src/rider/generated/kotlin") resources.srcDir("src/rider/main/resources") } } project.version = "${property("majorVersion")}." + "${property("minorVersion")}." + "${property("buildCounter")}" if (System.getenv("TEAMCITY_VERSION") != null) { logger.lifecycle("##teamcity[buildNumber '${project.version}']") } else { logger.lifecycle("Plugin version: ${project.version}") } val buildConfigurationProp = project.property("buildConfiguration").toString() val repoRoot by extra { project.rootDir } val isWindows by extra { Os.isFamily(Os.FAMILY_WINDOWS) } val idePluginId by extra { "RiderPlugin" } val dotNetSolutionId by extra { "UnrealLink" } val dotNetDir by extra { File(repoRoot, "src/dotnet") } val dotNetBinDir by extra { dotNetDir.resolve("$idePluginId.$dotNetSolutionId").resolve("bin") } val dotNetPluginId by extra { "$idePluginId.${project.name}" } val dotNetSolution by extra { File(repoRoot, "$dotNetSolutionId.sln") } val modelDir = File(repoRoot, "protocol/src/main/kotlin/model") val hashBaseDir = File(repoRoot, "build/rdgen") val ktOutputRelativePath = "src/rider/main/kotlin/com/jetbrains/rider/model" val cppOutputRoot = File(repoRoot, "src/cpp/RiderLink/Source/RiderLink/Public/Model") val csOutputRoot = File(repoRoot, "src/dotnet/RiderPlugin.UnrealLink/obj/model") val ktOutputRoot = File(repoRoot, ktOutputRelativePath) val riderLinkDir = File("$rootDir/src/cpp/RiderLink") val currentBranchName = getBranchName() fun TaskContainerScope.setupCleanup(task: Task) { withType { delete(task.outputs.files) } } fun getBranchName(): String { val execResult = providers.exec { executable = "git" args("rev-parse", "--abbrev-ref", "HEAD") workingDir = projectDir isIgnoreExitValue = true } if (execResult.result.get().exitValue == 0) { val output = execResult.standardOutput.asText.get().trim() if (output.isNotEmpty()) return output } return "net222" } fun getProductMonorepoRoot(): File? { var currentDir = repoRoot while (currentDir.parent != null) { if (currentDir.resolve(".ultimate.root.marker").exists()) { return currentDir } currentDir = currentDir.parentFile } return null } changelog { version.set(project.version.toString()) // https://github.com/JetBrains/gradle-changelog-plugin/blob/main/src/main/kotlin/org/jetbrains/changelog/Changelog.kt#L23 // This is just common semVerRegex with the addition of a forth optional group (number) ( x.x.x[.x][-alpha43] ) headerParserRegex.set( """^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)\.?(0|[1-9]\d*)?(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*) (?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?${'$'}""" .trimMargin().toRegex()) groups.set(listOf("Added", "Changed", "Deprecated", "Removed", "Fixed", "Known Issues")) keepUnreleasedSection.set(true) itemPrefix.set("-") } dependencies { intellijPlatform { val dependencyPath = File(projectDir, "dependencies") if (dependencyPath.exists()) { val localPath = dependencyPath.canonicalPath local(localPath) logger.lifecycle("Will use ${File(localPath, "build.txt").readText()} from $localPath as RiderSDK") } else { val version = "${project.property("majorVersion")}-SNAPSHOT" logger.lifecycle("*** Using Rider SDK $version from intellij-snapshots repository") rider(version, useInstaller = false) } jetbrainsRuntime() // Workaround for https://youtrack.jetbrains.com/issue/IDEA-179607 bundledPlugin("rider.intellij.plugin.appender") bundledPlugin("com.intellij.cidr.debugger") bundledPlugin("com.jetbrains.rider-cpp") // TODO: Temporary I hope hope hope bundledLibrary(provider { project.intellijPlatform.platformPath.resolve("lib/testFramework.jar").pathString }) } } intellijPlatform { tasks { val currentReleaseNotesAsHtml = """

New in "${project.version}"

${changelog.renderItem(changelog.getLatest(), Changelog.OutputType.HTML)}

See the CHANGELOG for more details and history.

""".trimIndent() val currentReleaseNotesAsMarkdown = """ ## New in ${project.version} ${changelog.renderItem(changelog.getLatest())} See the [CHANGELOG](https://github.com/JetBrains/UnrealLink/blob/$currentBranchName/CHANGELOG.md) for more details and history. """.trimIndent() val dumpCurrentChangelog by registering { val outputFile = layout.buildDirectory.file("release_notes.md") outputs.file(outputFile) doLast { file(outputFile).writeText(currentReleaseNotesAsMarkdown) } } // PatchPluginXml gets the latest (always Unreleased) section from the current changelog and writes it into plugin.xml // dumpCurrentChangelog dumps the same section to file (for Marketplace changelog) // After, patchChangelog rename [Unreleased] to [202x.x.x.x] and create new empty Unreleased. // So order is important! patchPluginXml { changeNotes.set( provider { currentReleaseNotesAsHtml }) } patchChangelog { mustRunAfter(patchPluginXml, dumpCurrentChangelog) } publishPlugin { dependsOn(patchPluginXml, dumpCurrentChangelog, patchChangelog) token.set(System.getenv("UNREALLINK_intellijPublishToken")) val pubChannels = project.findProperty("publishChannels") if ( pubChannels != null) { val chan = pubChannels.toString().split(',') println("Channels for publish $chan") channels.set(chan) } else { channels.set(listOf("alpha")) } } } } val riderModel: Configuration by configurations.creating { isCanBeConsumed = true isCanBeResolved = false } artifacts { add(riderModel.name, provider { intellijPlatform.platformPath.resolve("lib/rd/rider-model.jar").also { check(it.isFile) { "rider-model.jar is not found at $riderModel" } } }) { builtBy(Constants.Tasks.INITIALIZE_INTELLIJ_PLATFORM_PLUGIN) } } tasks { val dotNetSdkPath by lazy { val sdkPath = intellijPlatform.platformPath.resolve("lib/DotNetSdkForRdPlugins").absolute() assert(sdkPath.isDirectory()) println(".NET SDK path: $sdkPath") return@lazy sdkPath.toRealPath() } instrumentCode { enabled = false } withType().configureEach { maxHeapSize = "4096m" } withType().configureEach { maxHeapSize = "4096m" if (project.hasProperty("ignoreFailures")) { ignoreFailures = true } useTestNG { } testLogging { showStandardStreams = true showExceptions = true exceptionFormat = TestExceptionFormat.FULL } } withType().configureEach { dependsOn("generateModels") compilerOptions { jvmTarget.set(JvmTarget.JVM_25) } } val prepareRiderBuildProps by registering { group = "RiderBackend" val generatedFile = layout.buildDirectory.file("DotNetSdkPath.generated.props") inputs.property("dotNetSdkFile", { dotNetSdkPath.toString() }) outputs.file(generatedFile) doLast { project.file(generatedFile).writeText( """ | | $dotNetSdkPath | |""".trimMargin() ) } } val prepareNuGetConfig by registering { group = "RiderBackend" dependsOn(prepareRiderBuildProps) val generatedFile = project.projectDir.resolve("NuGet.Config") inputs.property("dotNetSdkFile", { dotNetSdkPath.toString() }) outputs.file(generatedFile) doLast { val dotNetSdkFile = dotNetSdkPath logger.info("dotNetSdk location: '$dotNetSdkFile'") val nugetConfigText = """ | | | | | | | """.trimMargin() generatedFile.writeText(nugetConfigText) logger.info("Generated content:\n$nugetConfigText") } } val buildResharperHost by registering { group = "RiderBackend" description = "Build backend for Rider" dependsOn(":generateModels", prepareNuGetConfig) inputs.file(file(dotNetSolution)) inputs.dir(file("$repoRoot/src/dotnet")) outputs.dir(file("$repoRoot/src/dotnet/RiderPlugin.UnrealLink/bin/RiderPlugin.UnrealLink/$buildConfigurationProp")) doLast { val warningsAsErrors: String by project.extra val buildArguments = listOf( "build", dotNetSolution.canonicalPath, "-consoleLoggerParameters:ErrorsOnly", "/p:Configuration=$buildConfigurationProp", "/p:Version=${project.version}", "/p:TreatWarningsAsErrors=$warningsAsErrors", "/bl:${dotNetSolution.name}.binlog", "/nologo" ) logger.info("call dotnet.cmd with '{}'", buildArguments) injected.exec.exec { executable = "$rootDir/tools/dotnet.cmd" args = buildArguments workingDir = dotNetSolution.parentFile } } } val patchUpluginVersion by register("patchUpluginVersion") { val pathToUpluginTemplate = File("${project.rootDir}/src/cpp/RiderLink/RiderLink.uplugin.template") val filePathToUplugin = File("${project.rootDir}/src/cpp/RiderLink/RiderLink.uplugin") inputs.file(pathToUpluginTemplate) inputs.property("version", project.version) outputs.file(filePathToUplugin) doLast { if(filePathToUplugin.exists()) filePathToUplugin.delete() pathToUpluginTemplate.copyTo(filePathToUplugin) val text = filePathToUplugin.readLines().map { it.replace("%PLUGIN_VERSION%", "${project.version}") } filePathToUplugin.writeText(text.joinToString(System.lineSeparator())) } } withType { delete(patchUpluginVersion.outputs.files) } val generateChecksum by register("generateChecksum") { dependsOn(":generateModels") val upluginFile = riderLinkDir.resolve("RiderLink.uplugin.template") val resourcesDir = riderLinkDir.resolve("Resources") val sourceDir = riderLinkDir.resolve("Source") val checksumFile = riderLinkDir.resolve("Resources/checksum") inputs.file(upluginFile) inputs.dir(resourcesDir) inputs.dir(sourceDir) outputs.file(checksumFile) doLast { checksumFile.delete() val inputFiles = sequence{ yield(upluginFile) resourcesDir.walkTopDown().forEach { if(it.isFile && (it.nameWithoutExtension != "checksum")) yield(it) } sourceDir.walkTopDown().forEach { if(it.isFile) yield(it) } } val instance = MessageDigest.getInstance("MD5") inputFiles.forEach { instance.update(it.readBytes()) } checksumFile.writeBytes(instance.digest()) } } withType { delete(generateChecksum.outputs.files) } val packCppSide by registering(Zip::class) { dependsOn(patchUpluginVersion) dependsOn(":generateModels") dependsOn(generateChecksum) archiveFileName.set("RiderLink.zip") excludes.addAll(arrayOf("RiderLink.uplugin.template", "Intermediate", "Binaries")) destinationDirectory.set(File("$rootDir/build/distributions")) from("$rootDir/src/cpp/RiderLink") } withType { dependsOn(buildResharperHost, packCppSide) outputs.upToDateWhen { false } //need to dotnet artifacts be included when only dotnet sources were changed val outputFolder = dotNetBinDir .resolve(dotNetPluginId) .resolve(buildConfigurationProp) val dllFiles = listOf( File(outputFolder, "$dotNetPluginId.dll"), File(outputFolder, "$dotNetPluginId.pdb") ) dllFiles.forEach { from(it) { into("${intellijPlatform.projectName.get()}/dotnet") } } from(packCppSide.get().archiveFile) { into("${intellijPlatform.projectName.get()}/EditorPlugin") } doLast { dllFiles.forEach { file -> if (!file.exists()) throw RuntimeException("File $file does not exist") } } } val generateModels by registering { group = "protocol" description = "Generates protocol models." dependsOn(":protocol:rdgen") } withType { delete(csOutputRoot, cppOutputRoot, ktOutputRoot) } buildSearchableOptions { } val getUnrealEngineProject by register("getUnrealEngineProject") { doLast { val ueProjectPathTxt = rootDir.resolve("UnrealEngineProjectPath.txt") if (ueProjectPathTxt.exists()) { val ueProjectPath = ueProjectPathTxt.readText() val ueProjectPathDir = File(ueProjectPath) if (!ueProjectPathDir.exists()) throw AssertionError("$ueProjectPathDir doesn't exist") if (!ueProjectPathDir.isDirectory) throw AssertionError("$ueProjectPathDir is not directory") val isUEProject = ueProjectPathDir.listFiles()?.any { it.extension == "uproject" } if (isUEProject == true) { extra["UnrealProjectPath"] = ueProjectPathDir } else { throw AssertionError("Add path to a valid UnrealEngine project folder to: $ueProjectPathTxt") } } else { ueProjectPathTxt.createNewFile() throw AssertionError("Add path to a valid UnrealEngine project folder to: $ueProjectPathTxt") } } } val symlinkPluginToUnrealProject by registering { dependsOn(getUnrealEngineProject) dependsOn(patchUpluginVersion) doLast { val unrealProjectPath = getUnrealEngineProject.extra["UnrealProjectPath"] as File val targetDir = File("$unrealProjectPath/Plugins/Developer/RiderLink") if(targetDir.exists()) { val stdOut = ByteArrayOutputStream() // Check if it's Junction val result = injected.exec.exec { commandLine = if(isWindows) listOf("cmd.exe", "/c", "fsutil", "reparsepoint", "query", targetDir.absolutePath, "|", "find", "Print Name:") else listOf("find", targetDir.absolutePath, "-maxdepth", "1", "-type", "l", "-ls") isIgnoreExitValue = true standardOutput = stdOut } // Check if it's Junction to local RiderLink if(result.exitValue == 0) { val output = stdOut.toString().trim() if (output.isNotEmpty()) { val pathToJunction = if (isWindows) output.substringAfter("Print Name:").trim() else output.substringAfter("->").trim() if (File(pathToJunction) == riderLinkDir) { println("Junction is already correct") throw StopExecutionException() } } } // If it's not Junction or if it's a Junction but doesn't point to local RiderLink - delete it targetDir.delete() } targetDir.parentFile.mkdirs() val stdOut = ByteArrayOutputStream() val result = injected.exec.exec { commandLine = if(isWindows) listOf("cmd.exe", "/c", "mklink", "/J", targetDir.absolutePath, riderLinkDir.absolutePath) else listOf("ln", "-s", riderLinkDir.absolutePath, targetDir.absolutePath) errorOutput = stdOut isIgnoreExitValue = true } if (result.exitValue != 0) { println(stdOut.toString().trim()) } } } wrapper { gradleVersion = "9.4.1" distributionUrl = "https://cache-redirector.jetbrains.com/services.gradle.org/distributions/gradle-${gradleVersion}-bin.zip" } } ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://cache-redirector.jetbrains.com/services.gradle.org/distributions/gradle-9.4.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists ================================================ FILE: gradle.properties ================================================ # Properties for the build. Override on the command line with -Pprop=value # E.g. -PBuildConfiguration=Release # Note that these values are strings! # Local builds and installs will have this ridiculours number 20xx.x.x.9999 # Teamcity overrides buildCounter to %build.counter% (Teamcity counter) # majorVersion is used as SDK version when no SDK in UnrealLink/dependencies # Downloads "majorVersion-SNAPSHOT" from https://cache-redirector.jetbrains.com/intellij-repository/snapshots majorVersion=2026.3 minorVersion=0 buildCounter=9999 # Overrides on Teamcity to Release buildConfiguration=Debug warningsAsErrors=false # List of channels to publish. Defined as comma-separated string (publishChannels="alpha,beta,eap") # Alpha channel uses by default =) #publishChannels=alpha # We need it to avoid bundle Kotlin jars into plugin kotlin.stdlib.default.dependency=false # Updated automatically on idea sync rdVersion=2026.1.3 rdKotlinVersion=2.3.0 intellijPlatformGradlePluginVersion=2.11.0 gradleJvmWrapperVersion=0.15.0 org.gradle.jvmargs=-Xmx4g ================================================ FILE: gradlew ================================================ #!/bin/sh # # Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # SPDX-License-Identifier: Apache-2.0 # ############################################################################## # # Gradle start up script for POSIX generated by Gradle. # # Important for running: # # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is # noncompliant, but you have some other compliant shell such as ksh or # bash, then to run this script, type that shell name before the whole # command line, like: # # ksh Gradle # # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: # * functions; # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», # «${var#prefix}», «${var%suffix}», and «$( cmd )»; # * compound commands having a testable exit status, especially «case»; # * various built-in commands including «command», «set», and «ulimit». # # Important for patching: # # (2) This script targets any POSIX shell, so it avoids extensions provided # by Bash, Ksh, etc; in particular arrays are avoided. # # The "traditional" practice of packing multiple parameters into a # space-separated string is a well documented source of bugs and security # problems, so this is (mostly) avoided, by progressively accumulating # options in "$@", and eventually passing that to Java. # # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; # see the in-line comments for details. # # There are tweaks for specific operating systems such as AIX, CygWin, # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template # https://github.com/gradle/gradle/blob/2d6327017519d23b96af35865dc997fcb544fb40/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. # ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link app_path=$0 # Need this for daisy-chained symlinks. while APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path [ -h "$app_path" ] do ls=$( ls -ld "$app_path" ) link=${ls#*' -> '} case $link in #( /*) app_path=$link ;; #( *) app_path=$APP_HOME$link ;; esac done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum warn () { echo "$*" } >&2 die () { echo echo "$*" echo exit 1 } >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false case "$( uname )" in #( CYGWIN* ) cygwin=true ;; #( Darwin* ) darwin=true ;; #( MSYS* | MINGW* ) msys=true ;; #( NONSTOP* ) nonstop=true ;; esac # GRADLE JVM WRAPPER START MARKER BUILD_DIR="${HOME}/.local/share/gradle-jvm" JVM_ARCH=$(uname -m) JVM_TEMP_FILE=$BUILD_DIR/gradle-jvm-temp.tar.gz if [ "$darwin" = "true" ]; then case $JVM_ARCH in x86_64) JVM_URL=https://download.oracle.com/java/25/archive/jdk-25.0.2_macos-x64_bin.tar.gz JVM_TARGET_DIR=$BUILD_DIR/jdk-25.0.2_macos-x64_bin-99fbca ;; arm64) JVM_URL=https://download.oracle.com/java/25/archive/jdk-25.0.2_macos-aarch64_bin.tar.gz JVM_TARGET_DIR=$BUILD_DIR/jdk-25.0.2_macos-aarch64_bin-d7817e ;; *) die "Unknown architecture $JVM_ARCH" ;; esac elif [ "$cygwin" = "true" ] || [ "$msys" = "true" ]; then JVM_URL=https://download.oracle.com/java/25/archive/jdk-25.0.2_windows-x64_bin.zip JVM_TARGET_DIR=$BUILD_DIR/jdk-25.0.2_windows-x64_bin-96701c else JVM_ARCH=$(linux$(getconf LONG_BIT) uname -m) case $JVM_ARCH in x86_64) JVM_URL=https://download.oracle.com/java/25/archive/jdk-25.0.2_linux-x64_bin.tar.gz JVM_TARGET_DIR=$BUILD_DIR/jdk-25.0.2_linux-x64_bin-3c4431 ;; aarch64) JVM_URL=https://download.oracle.com/java/25/archive/jdk-25.0.2_linux-aarch64_bin.tar.gz JVM_TARGET_DIR=$BUILD_DIR/jdk-25.0.2_linux-aarch64_bin-a88282 ;; *) die "Unknown architecture $JVM_ARCH" ;; esac fi set -e if [ -e "$JVM_TARGET_DIR/.flag" ] && [ -n "$(ls "$JVM_TARGET_DIR")" ] && [ "x$(cat "$JVM_TARGET_DIR/.flag")" = "x${JVM_URL}" ]; then # Everything is up-to-date in $JVM_TARGET_DIR, do nothing true else echo "Downloading $JVM_URL to $JVM_TEMP_FILE" rm -f "$JVM_TEMP_FILE" mkdir -p "$BUILD_DIR" if command -v curl >/dev/null 2>&1; then if [ -t 1 ]; then CURL_PROGRESS="--progress-bar"; else CURL_PROGRESS="--silent --show-error"; fi # shellcheck disable=SC2086 curl $CURL_PROGRESS -L --output "${JVM_TEMP_FILE}" "$JVM_URL" 2>&1 elif command -v wget >/dev/null 2>&1; then if [ -t 1 ]; then WGET_PROGRESS=""; else WGET_PROGRESS="-nv"; fi wget $WGET_PROGRESS -O "${JVM_TEMP_FILE}" "$JVM_URL" 2>&1 else die "ERROR: Please install wget or curl" fi echo "Extracting $JVM_TEMP_FILE to $JVM_TARGET_DIR" rm -rf "$JVM_TARGET_DIR" mkdir -p "$JVM_TARGET_DIR" case "$JVM_URL" in *".zip") unzip "$JVM_TEMP_FILE" -d "$JVM_TARGET_DIR" ;; *) tar -x -f "$JVM_TEMP_FILE" -C "$JVM_TARGET_DIR" ;; esac rm -f "$JVM_TEMP_FILE" echo "$JVM_URL" >"$JVM_TARGET_DIR/.flag" fi JAVA_HOME= for d in "$JVM_TARGET_DIR" "$JVM_TARGET_DIR"/* "$JVM_TARGET_DIR"/Contents/Home "$JVM_TARGET_DIR"/*/Contents/Home; do if [ -e "$d/bin/java" ]; then JAVA_HOME="$d" fi done if [ '!' -e "$JAVA_HOME/bin/java" ]; then die "Unable to find bin/java under $JVM_TARGET_DIR" fi # Make it available for child processes export JAVA_HOME set +e # GRADLE JVM WRAPPER END MARKER # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD=$JAVA_HOME/jre/sh/java else JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD=java if ! command -v java >/dev/null 2>&1 then die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi # Collect all arguments for the java command, stacking in reverse order: # * args from the command line # * the main class name # * -classpath # * -D...appname settings # * --module-path (only if needed) # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) # Now convert the arguments - kludge to limit ourselves to /bin/sh for arg do if case $arg in #( -*) false ;; # don't mess with options #( /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath [ -e "$t" ] ;; #( *) false ;; esac then arg=$( cygpath --path --ignore --mixed "$arg" ) fi # Roll the args list around exactly as many times as the number of # args, so each arg winds up back in the position where it started, but # possibly modified. # # NB: a `for` loop captures its iteration list before it begins, so # changing the positional parameters here affects neither the number of # iterations, nor the values presented in `arg`. shift # remove old arg set -- "$@" "$arg" # push replacement arg done fi # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. if ! command -v xargs >/dev/null 2>&1 then die "xargs is not available" fi # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. # # In Bash we could simply go: # # readarray ARGS < <( xargs -n1 <<<"$var" ) && # set -- "${ARGS[@]}" "$@" # # but POSIX shell has neither arrays nor command substitution, so instead we # post-process each arg (as a line of input to sed) to backslash-escape any # character that might be a shell metacharacter, then use eval to reverse # that process (while maintaining the separation between arguments), and wrap # the whole thing up as a single "set" statement. # # This will of course break if any of these variables contains a newline or # an unmatched quote. # eval "set -- $( printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | xargs -n1 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | tr '\n' ' ' )" '"$@"' exec "$JAVACMD" "$@" ================================================ FILE: gradlew.bat ================================================ @rem @rem Copyright 2015 the original author or authors. @rem @rem Licensed under the Apache License, Version 2.0 (the "License"); @rem you may not use this file except in compliance with the License. @rem You may obtain a copy of the License at @rem @rem https://www.apache.org/licenses/LICENSE-2.0 @rem @rem Unless required by applicable law or agreed to in writing, software @rem distributed under the License is distributed on an "AS IS" BASIS, @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem @rem SPDX-License-Identifier: Apache-2.0 @rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. @rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Resolve any "." and ".." in APP_HOME to make it shorter. for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem GRADLE JVM WRAPPER START MARKER setlocal set BUILD_DIR=%LOCALAPPDATA%\gradle-jvm for /f "tokens=3 delims= " %%A in ('reg query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v "PROCESSOR_ARCHITECTURE"') do set WIN_ARCH=%%A if "%WIN_ARCH%" equ "AMD64" ( set JVM_TARGET_DIR=%BUILD_DIR%\jdk-25.0.2_windows-x64_bin-96701c\ set JVM_URL=https://download.oracle.com/java/25/archive/jdk-25.0.2_windows-x64_bin.zip ) else if "%WIN_ARCH%" equ "ARM64" ( set JVM_TARGET_DIR=%BUILD_DIR%\microsoft-jdk-25.0.2-windows-aarch64-7d2a81\ set JVM_URL=https://aka.ms/download-jdk/microsoft-jdk-25.0.2-windows-aarch64.zip ) else ( echo Unknown architecture %WIN_ARCH% goto fail ) set IS_TAR_GZ=0 set JVM_TEMP_FILE=gradle-jvm.zip if /I "%JVM_URL:~-7%"==".tar.gz" ( set IS_TAR_GZ=1 set JVM_TEMP_FILE=gradle-jvm.tar.gz ) set POWERSHELL=%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe if not exist "%JVM_TARGET_DIR%" MD "%JVM_TARGET_DIR%" if not exist "%JVM_TARGET_DIR%.flag" goto downloadAndExtractJvm set /p CURRENT_FLAG=<"%JVM_TARGET_DIR%.flag" if "%CURRENT_FLAG%" == "%JVM_URL%" goto continueWithJvm :downloadAndExtractJvm PUSHD "%BUILD_DIR%" if errorlevel 1 goto fail echo Downloading %JVM_URL% to %BUILD_DIR%\%JVM_TEMP_FILE% if exist "%JVM_TEMP_FILE%" DEL /F "%JVM_TEMP_FILE%" "%POWERSHELL%" -nologo -noprofile -Command "Set-StrictMode -Version 3.0; $ErrorActionPreference = \"Stop\"; (New-Object Net.WebClient).DownloadFile('%JVM_URL%', '%JVM_TEMP_FILE%')" if errorlevel 1 goto fail POPD RMDIR /S /Q "%JVM_TARGET_DIR%" if errorlevel 1 goto fail MKDIR "%JVM_TARGET_DIR%" if errorlevel 1 goto fail PUSHD "%JVM_TARGET_DIR%" if errorlevel 1 goto fail echo Extracting %BUILD_DIR%\%JVM_TEMP_FILE% to %JVM_TARGET_DIR% if "%IS_TAR_GZ%"=="1" ( tar xf "..\\%JVM_TEMP_FILE%" ) else ( "%POWERSHELL%" -nologo -noprofile -command "Set-StrictMode -Version 3.0; $ErrorActionPreference = \"Stop\"; Add-Type -A 'System.IO.Compression.FileSystem'; [IO.Compression.ZipFile]::ExtractToDirectory('..\\%JVM_TEMP_FILE%', '.');" ) if errorlevel 1 goto fail DEL /F "..\%JVM_TEMP_FILE%" if errorlevel 1 goto fail POPD echo %JVM_URL%>"%JVM_TARGET_DIR%.flag" if errorlevel 1 goto fail :continueWithJvm set JAVA_HOME= for /d %%d in ("%JVM_TARGET_DIR%"*) do if exist "%%d\bin\java.exe" set JAVA_HOME=%%d if not exist "%JAVA_HOME%\bin\java.exe" ( echo Unable to find java.exe under %JVM_TARGET_DIR% goto fail ) endlocal & set JAVA_HOME=%JAVA_HOME% @rem GRADLE JVM WRAPPER END MARKER @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute echo. 1>&2 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute echo. 1>&2 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 goto fail :execute @rem Setup the command line @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! set EXIT_CODE=%ERRORLEVEL% if %EXIT_CODE% equ 0 set EXIT_CODE=1 if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: protocol/build.gradle.kts ================================================ import com.jetbrains.rd.generator.gradle.RdGenTask plugins { // Version is configured in gradle.properties id("com.jetbrains.rdgen") id("org.jetbrains.kotlin.jvm") } repositories { maven("https://cache-redirector.jetbrains.com/intellij-dependencies") maven("https://cache-redirector.jetbrains.com/maven-central") val rd_version: String? by project if (rd_version == "SNAPSHOT") { mavenLocal() } } val isMonorepo = rootProject.projectDir != projectDir.parentFile val unrealLinkRepoRoot: File = projectDir.parentFile sourceSets { main { kotlin { srcDir(unrealLinkRepoRoot.resolve("protocol/src/main/kotlin/model")) } } } data class UnrealLinkGeneratorSettings( val ue4LibCsLibraryOutput: File, val ue4LibCppLibraryOutput: File, val ue4LibKtLibraryOutput: File, val riderModelCsOutput: File, val riderModelKtOutput: File, val csEditorOutput: File, val cppEditorOutput: File, val fileSuffix: String ) val ktOutputRelativePath = "src/rider/generated/kotlin/com/jetbrains/rider/model" val unrealLinkGeneratorSettings = if (isMonorepo) { val monorepoRoot = buildscript.sourceFile?.parentFile?.parentFile?.parentFile?.parentFile?.parentFile ?: error("Cannot find products home") check(monorepoRoot.resolve(".ultimate.root.marker").isFile) { error("Incorrect location in monorepo: monorepoRoot='$monorepoRoot'") } val monorepoPreGeneratedRootDir = monorepoRoot.resolve("dotnet/Plugins/_UnrealLink.Pregenerated") val monorepoPreGeneratedFrontendDir = monorepoPreGeneratedRootDir.resolve("Frontend") val monorepoPreGeneratedBackendDir = monorepoPreGeneratedRootDir.resolve("BackendModel") val monorepoPreGeneratedCppDir = monorepoPreGeneratedRootDir.resolve("CppModel") val ktOutputMonorepoRoot = monorepoPreGeneratedFrontendDir.resolve(ktOutputRelativePath) UnrealLinkGeneratorSettings( monorepoPreGeneratedBackendDir.resolve("Library"), monorepoPreGeneratedCppDir.resolve( "Library"), ktOutputMonorepoRoot.resolve("Library"), monorepoPreGeneratedBackendDir.resolve("RdRiderProtocol"), ktOutputMonorepoRoot.resolve("RdRiderProtocol"), monorepoPreGeneratedBackendDir.resolve("RdEditorProtocol"), monorepoPreGeneratedCppDir.resolve("RdEditorProtocol"), ".Pregenerated" ) } else { val csOutputRoot = File(unrealLinkRepoRoot, "src/dotnet/RiderPlugin.UnrealLink/obj/model") val cppOutputRoot = File(unrealLinkRepoRoot, "src/cpp/RiderLink/Source/RiderLink/Public/Model") val ktOutputRoot = File(unrealLinkRepoRoot, ktOutputRelativePath) UnrealLinkGeneratorSettings( csOutputRoot.resolve("Library"), cppOutputRoot.resolve( "Library"), ktOutputRoot.resolve("Library"), csOutputRoot.resolve("RdRiderProtocol"), ktOutputRoot.resolve("RdRiderProtocol"), csOutputRoot.resolve("RdEditorProtocol"), cppOutputRoot.resolve("RdEditorProtocol"), ".Pregenerated" ) } rdgen { verbose = true packages = "model.editorPlugin,model.lib.ue4,model.rider" generator { language = "csharp" transform = "symmetric" root = "model.lib.ue4.UE4Library" directory = unrealLinkGeneratorSettings.ue4LibCsLibraryOutput.absolutePath generatedFileSuffix = unrealLinkGeneratorSettings.fileSuffix } generator { language = "cpp" transform = "reversed" root = "model.lib.ue4.UE4Library" directory = unrealLinkGeneratorSettings.ue4LibCppLibraryOutput.absolutePath generatedFileSuffix = unrealLinkGeneratorSettings.fileSuffix } generator { language = "kotlin" transform = "asis" root = "model.lib.ue4.UE4Library" directory = unrealLinkGeneratorSettings.ue4LibKtLibraryOutput.absolutePath generatedFileSuffix = unrealLinkGeneratorSettings.fileSuffix } generator { language = "csharp" transform = "reversed" root = "com.jetbrains.rider.model.nova.ide.IdeRoot" directory = unrealLinkGeneratorSettings.riderModelCsOutput.absolutePath generatedFileSuffix = unrealLinkGeneratorSettings.fileSuffix } generator { language = "kotlin" transform = "asis" root = "com.jetbrains.rider.model.nova.ide.IdeRoot" directory = unrealLinkGeneratorSettings.riderModelKtOutput.absolutePath generatedFileSuffix = unrealLinkGeneratorSettings.fileSuffix } generator { language = "csharp" transform = "asis" root = "model.editorPlugin.RdEditorRoot" directory = unrealLinkGeneratorSettings.csEditorOutput.absolutePath generatedFileSuffix = unrealLinkGeneratorSettings.fileSuffix } generator { language = "cpp" transform = "reversed" root = "model.editorPlugin.RdEditorRoot" directory = unrealLinkGeneratorSettings.cppEditorOutput.absolutePath generatedFileSuffix = unrealLinkGeneratorSettings.fileSuffix } } tasks.withType { dependsOn(sourceSets["main"].runtimeClasspath) classpath(sourceSets["main"].runtimeClasspath) } dependencies { if (isMonorepo) { implementation(project(":rider-model")) } else { val rdVersion: String by project val rdKotlinVersion: String by project implementation("com.jetbrains.rd:rd-gen:$rdVersion") implementation("org.jetbrains.kotlin:kotlin-stdlib:$rdKotlinVersion") implementation( project( mapOf( "path" to ":", "configuration" to "riderModel" ) ) ) } } ================================================ FILE: protocol/src/main/kotlin/model/editorPlugin/LiveCodingModel.kt ================================================ package model.editorPlugin import com.jetbrains.rd.generator.nova.* import com.jetbrains.rd.generator.nova.PredefinedType.* @Suppress("unused") object LiveCodingModel : Ext(RdEditorModel) { init { call("LC_IsEnabledByDefault", void, bool) source("LC_EnableByDefault", bool) call("LC_IsEnabledForSession", void, bool) call("LC_CanEnableForSession", void, bool) source("LC_EnableForSession", bool) call("LC_IsCompiling", void, bool) call("LC_HasStarted", void, bool) source("LC_Compile", void) sink("LC_OnPatchComplete", void) } } ================================================ FILE: protocol/src/main/kotlin/model/editorPlugin/RdEditorModel.kt ================================================ package model.editorPlugin import com.jetbrains.rd.generator.nova.* import com.jetbrains.rd.generator.nova.PredefinedType.* import com.jetbrains.rd.generator.nova.cpp.Cpp17Generator import com.jetbrains.rd.generator.nova.csharp.CSharp50Generator import model.lib.ue4.UE4Library @Suppress("unused") object RdEditorRoot : Root() { init { setting(CSharp50Generator.Namespace, "RiderPlugin.UnrealLink.Model.BackendUnreal") setting(Cpp17Generator.Namespace, "JetBrains::EditorPlugin") setting(Cpp17Generator.AdditionalHeaders, listOf("UE4TypesMarshallers.h")) setting(Cpp17Generator.ExportMacroName, "RIDERLINK_API") setting(Cpp17Generator.GeneratePrecompiledHeaders, false) setting(Cpp17Generator.UsePrecompiledHeaders, false) } } @Suppress("unused") object RdEditorModel : Ext(RdEditorRoot) { init { property("connectionInfo", UE4Library.ConnectionInfo).readonly signal("unrealLog", UE4Library.UnrealLogEvent).async signal("openBlueprint", UE4Library.BlueprintReference) signal("onBlueprintAdded", UE4Library.UClass).async call("isBlueprintPathName", UE4Library.FString, bool) call("getPathNameByPath", UE4Library.FString, UE4Library.FString.nullable) callback("AllowSetForegroundWindow", int, bool) property("isGameControlModuleInitialized", false).readonly sink("PlayStateFromEditor", UE4Library.PlayState) source("RequestPlayFromRider", int) source("RequestPauseFromRider", int) source("RequestResumeFromRider", int) source("RequestStopFromRider", int) source("RequestFrameSkipFromRider", int) sink("NotificationReplyFromEditor", UE4Library.RequestResultBase) sink("PlayModeFromEditor", int) source("PlayModeFromRider", int) // Hot Reload here is not Unreal's HotReload but generic Hot Reload mechanism which can be either Unreal's HotReload or Unreal's LiveCoding property("IsHotReloadAvailable", false).readonly property("IsHotReloadCompiling", false).readonly source("TriggerHotReload", void) } } ================================================ FILE: protocol/src/main/kotlin/model/lib/ue4/UE4Library.kt ================================================ package model.lib.ue4 import com.jetbrains.rd.generator.nova.* import com.jetbrains.rd.generator.nova.PredefinedType.* import com.jetbrains.rd.generator.nova.cpp.Cpp17Generator import com.jetbrains.rd.generator.nova.cpp.CppIntrinsicType import com.jetbrains.rd.generator.nova.csharp.CSharp50Generator import com.jetbrains.rd.generator.nova.kotlin.Kotlin11Generator object UE4Library : Root() { init { setting(Kotlin11Generator.Namespace, "com.jetbrains.rider.plugins.unreal.model") setting(CSharp50Generator.Namespace, "RiderPlugin.UnrealLink.Model") setting(Cpp17Generator.Namespace, "JetBrains::EditorPlugin") setting(Cpp17Generator.AdditionalHeaders, listOf( "UE4TypesMarshallers.h", "Runtime/Core/Public/Containers/Array.h", "Runtime/Core/Public/Containers/ContainerAllocationPolicies.h" )) setting(Cpp17Generator.ListType, CppIntrinsicType(null, "TArray", "Containers/Array.h")) setting(Cpp17Generator.AllocatorType) { "FDefaultAllocator" } setting(Cpp17Generator.ExportMacroName, "RIDERLINK_API") setting(Cpp17Generator.GeneratePrecompiledHeaders, false) setting(Cpp17Generator.UsePrecompiledHeaders, false) } private fun declare(intrinsic: CppIntrinsicType, factory: Toplevel.() -> T): T { return this.factory().apply { intrinsic.namespace?.let { ns -> setting(Cpp17Generator.Namespace, ns) } setting(Cpp17Generator.Intrinsic, intrinsic) } } val StringRange = structdef("StringRange") { field("first", int) field("last", int) } val FString = declare(CppIntrinsicType(null, "FString", "Containers/UnrealString.h")) { structdef("FString") { field("data", string) } } val PlayState = enum("PlayState") { +"Idle" +"Play" +"Pause" } val RequestResultBase = basestruct("RequestResultBase") { field("requestID", int) } @Suppress("unused") val RequestSucceed = structdef("RequestSucceed") extends RequestResultBase { } @Suppress("unused") val RequestFailed = structdef("RequestFailed") extends RequestResultBase { field("type", enum("NotificationType") { +"Message" +"Error" }) field("message", FString) } val VerbosityType = declare(CppIntrinsicType("ELogVerbosity", "Type", "Logging/LogVerbosity.h")) { setting(Cpp17Generator.IsNonScoped, "uint8") enum("VerbosityType") { (+"NoLogging").doc("= 0") (+"Fatal").doc( "Always prints a fatal error to console (and log file) and crashes (even if logging is disabled)" ) (+"Error").doc( "Prints an error to console (and log file)." + "Command lets and the editor collect and report errors. Error messages result in command let failure." ) (+"Warning").doc( "Prints a warning to console (and log file)." + "Command lets and the editor collect and report warnings. Warnings can be treated as an error.") (+"Display").doc( "Prints a message to console (and log file)" ) (+"Log").doc("Prints a message to a log file (does not print to console)") (+"Verbose").doc( "Prints a verbose message to a log file (if Verbose logging is enabled for the given category, usually used for detailed logging)" ) (+"VeryVerbose").doc( "Prints a verbose message to a log file (if VeryVerbose logging is enabled, usually used for detailed logging that would otherwise spam output)" ) // Log masks and special Enum values (+"All")/*.setting(Cpp17Generator.EnumConstantValue, 7)*/.doc("=VeryVerbose") (+"NumVerbosity") (+"VerbosityMask").setting(Cpp17Generator.EnumConstantValue, 0xf) (+"SetColor").setting(Cpp17Generator.EnumConstantValue, 0x40).doc("not actually a verbosity, used to set the color of an output device") (+"BreakOnLog").setting(Cpp17Generator.EnumConstantValue, 0x80) } } private val LogMessageInfo = structdef("LogMessageInfo") { field("type", VerbosityType) field("category", FString) field("time", dateTime.nullable) } val UnrealLogEvent = structdef("UnrealLogEvent") { field("info", LogMessageInfo) field("text", FString) field("bpPathRanges", immutableList(StringRange)) field("methodRanges", immutableList(StringRange)) } /*@Suppress("unused") private val LogMessageEvent = structdef("LogMessageEvent") extends LogEvent { field("message", FString) }*/ val UClass = structdef("UClass") { field("name", FString) } @Suppress("unused") val BlueprintFunction = structdef("BlueprintFunction") { field("class", UClass) field("name", FString) const("separator", string, ":") } //region Script Call Stack private val ScriptCallStackFrame = structdef("ScriptCallStackFrame") { // field("header", FString) // field("blueprintFunction", BlueprintFunction) field("entry", FString) } private val IScriptCallStack = basestruct("IScriptCallStack") { const("header", string, "Script call stack:") } @Suppress("unused") private val EmptyScriptCallStack = structdef("EmptyScriptCallStack") extends IScriptCallStack { const("message", string, "Script call stack: [Empty] (FFrame::GetStackTrace() called from native code)") } @Suppress("unused") private val ScriptCallStack = structdef("ScriptCallStack") extends IScriptCallStack { field("frames", immutableList(ScriptCallStackFrame)) } @Suppress("unused") private val UnableToDisplayScriptCallStack = structdef("UnableToDisplayScriptCallStack") extends IScriptCallStack { const("message", string, "Unable to display Script Callstack. Compile with DO_BLUEPRINT_GUARD=1") } //endregion //region ScriptMsg private val IScriptMsg = basestruct("IScriptMsg") { const("header", string, "Script msg:") } @Suppress("unused") private val ScriptMsgException = structdef("ScriptMsgException") extends IScriptMsg { field("message", FString) } @Suppress("unused") private val ScriptMsgCallStack = structdef("ScriptMsgCallStack") extends IScriptMsg { field("message", FString) field("scriptCallStack", IScriptCallStack) } /* @Suppress("unused") private val ScriptMsgEvent = structdef("ScriptMsgEvent") extends LogPart { field("scriptMsg", IScriptMsg) } */ //endregion @Suppress("unused") val BlueprintHighlighter = structdef("BlueprintHighlighter") { field("begin", int) field("end", int) } val BlueprintReference = structdef("BlueprintReference") { field("pathName", FString) field("guid", FString) } val ConnectionInfo = structdef("ConnectionInfo") { field("projectName", string) field("executableName", string) field("processId", int) } } ================================================ FILE: protocol/src/main/kotlin/model/rider/RdRiderModel.kt ================================================ package model.rider import com.jetbrains.rd.generator.nova.* import com.jetbrains.rd.generator.nova.PredefinedType.* import com.jetbrains.rd.generator.nova.csharp.CSharp50Generator import com.jetbrains.rd.generator.nova.kotlin.Kotlin11Generator import com.jetbrains.rider.model.nova.ide.SolutionModel import model.lib.ue4.UE4Library @Suppress("unused") object RdRiderModel : Ext(SolutionModel.Solution) { init { setting(Kotlin11Generator.Namespace, "com.jetbrains.rider.plugins.unreal.model.frontendBackend") setting(CSharp50Generator.Namespace, "RiderPlugin.UnrealLink.Model.FrontendBackend") } private val LinkRequest = structdef("LinkRequest") { field("data", UE4Library.FString) } val ILinkResponse = basestruct("ILinkResponse") {} val LinkResponseBlueprint = structdef("LinkResponseBlueprint") extends ILinkResponse { field("fullPath", UE4Library.FString) field("range", UE4Library.StringRange) } val LinkResponseFilePath = structdef("LinkResponseFilePath") extends ILinkResponse { field("fullPath", UE4Library.FString) field("range", UE4Library.StringRange) } val LinkResponseUnresolved = structdef("LinkResponseUnresolved") extends ILinkResponse {} private val MethodReference = structdef("MethodReference") { field("class", UE4Library.UClass) field("method", UE4Library.FString) const("separator", string, "::") } private val PluginInstallStatus = enum("PluginInstallStatus") { +"NoPlugin" +"UpToDate" +"InEngine" +"InGame" } private val EditorPluginOutOfSync = structdef("EditorPluginOutOfSync") { field("status", PluginInstallStatus) field("IsGameAvailable", bool) } private val InstallMessage = structdef("InstallMessage") { field("text", string) field("type", enum("ContentType") { +"Normal" +"Error" }) } private val InstallPluginDescription = structdef("InstallPluginDescription") { field("location", enum("PluginInstallLocation") { +"Engine" +"Game" +"NotInstalled" }) field("forceInstall", enum("ForceInstall") { +"Yes" +"No" }) field("buildRequired", bool).default(true) field("selectedUprojectPaths", immutableList(string)) field("unselectedUprojectPaths", immutableList(string)) } private val GamePluginInstallInfo = structdef("GamePluginInstallInfo") { field("projectName", string) field("uprojectPath", string) field("isPluginAvailable", bool) field("isPluginSynced", bool) } init { property("editorId", 0).readonly.async signal("unrealLog", UE4Library.UnrealLogEvent) call("filterLinkCandidates", immutableList(LinkRequest), array(ILinkResponse)).async call("isMethodReference", MethodReference, bool).async signal("navigateToMethod", MethodReference) signal("navigateToClass", UE4Library.UClass) signal("openBlueprint", UE4Library.BlueprintReference) callback("AllowSetForegroundWindow", int, bool) property("isConnectedToUnrealEditor", false).readonly.async property("isUnrealEngineSolution", false) property("isPreBuiltEngine", false) property("connectionInfo", UE4Library.ConnectionInfo).readonly sink("onEditorPluginOutOfSync", EditorPluginOutOfSync) source("installEditorPlugin", InstallPluginDescription) source("refreshProjects", void) source("enableAutoupdatePlugin", void) property("isGameControlModuleInitialized", false).readonly sink("PlayStateFromEditor", UE4Library.PlayState) source("RequestPlayFromRider", int) source("RequestPauseFromRider", int) source("RequestResumeFromRider", int) source("RequestStopFromRider", int) source("RequestFrameSkipFromRider", int) sink("NotificationReplyFromEditor", UE4Library.RequestResultBase) sink("PlayModeFromEditor", int) source("PlayModeFromRider", int) sink("RiderLinkInstallPanelInit", void) sink("RiderLinkInstallMessage", InstallMessage).async sink("InstallPluginFinished", bool).async property("RiderLinkInstallationInProgress", false) property("RefreshInProgress", false) property("IsUproject", false) property("isInstallInfoAvailable", false) sink("gamePluginInstallInfos", immutableList(GamePluginInstallInfo)) source("CancelRiderLinkInstall", void) // Hot Reload here is not Unreal's HotReload but generic Hot Reload mechanism which can be either Unreal's HotReload or Unreal's LiveCoding property("IsHotReloadAvailable", false).readonly property("IsHotReloadCompiling", false).readonly signal("TriggerHotReload", void) signal("DeletePlugin", void) } } ================================================ FILE: settings.gradle.kts ================================================ rootProject.name = "UnrealLink" pluginManagement { val rdVersion: String by settings val rdKotlinVersion: String by settings val intellijPlatformGradlePluginVersion: String by settings val gradleJvmWrapperVersion: String by settings repositories { maven("https://cache-redirector.jetbrains.com/intellij-dependencies") maven("https://cache-redirector.jetbrains.com/plugins.gradle.org") maven("https://cache-redirector.jetbrains.com/maven-central") if (rdVersion == "SNAPSHOT") { mavenLocal() } } plugins { id("com.jetbrains.rdgen") version rdVersion id("org.jetbrains.kotlin.jvm") version rdKotlinVersion id("org.jetbrains.intellij.platform") version intellijPlatformGradlePluginVersion id("me.filippov.gradle.jvm.wrapper") version gradleJvmWrapperVersion } resolutionStrategy { eachPlugin { when (requested.id.name) { // This required to correctly rd-gen plugin resolution. // Maybe we should switch our naming to match Gradle plugin naming convention. "rdgen" -> { useModule("com.jetbrains.rd:rd-gen:${rdVersion}") } } } } } include(":protocol") ================================================ FILE: setup_unreal_plugin.cmd ================================================ .\gradlew -q symlinkPluginToUnrealProject ================================================ FILE: src/SubplatformsCollection.Root ================================================ ================================================ FILE: src/cpp/BuildScript/GenerateRiderLinkZip.cs ================================================ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Security.Cryptography; using JetBrains.Application.BuildScript; using JetBrains.Application.BuildScript.Compile; using JetBrains.Application.BuildScript.Solution; using JetBrains.Build; using JetBrains.Extension; using JetBrains.Util; using JetBrains.Util.dataStructures; using JetBrains.Util.Special; using JetBrains.Util.Storage; namespace Plugins.UnrealLink.src.cpp.BuildScript { public class GenerateRiderLinkZip { [BuildStep] public static SubplatformFileForPackaging[] Run(AllAssembliesOnEverything allass, ProductHomeDirArtifact homedir, ILogger logger) { if (allass.FindSubplatformByClass() is SubplatformOnSources subplatform) { FileSystemPath dirRiderLink = homedir.ProductHomeDir / subplatform.Name.RelativePath / "RiderLink"; FileSystemPath dirModel = dirRiderLink.Parent.Parent.Parent.Parent / "_UnrealLink.Pregenerated" / "CppModel"; IList files = dirRiderLink.GetChildFiles(flags: PathSearchFlags.RecurseIntoSubdirectories) .OrderBy().Select(file => ImmutableFileItem.CreateFromDisk(file).WithRelativePath(file.MakeRelativeTo(dirRiderLink))).ToList(); IList models = dirModel.GetChildFiles(flags: PathSearchFlags.RecurseIntoSubdirectories) .OrderBy().Select(file => ImmutableFileItem.CreateFromDisk(file).WithRelativePath((RelativePath)"Source" / "RiderLink" / "Public" / "Model" / file.MakeRelativeTo(dirModel))).ToList(); files.AddRange(models); ImmutableFileItem fiUpluginTemplate = files.Single(fi => fi.RelativePath == "RiderLink.uplugin.template"); StreamEx.TextAndEncoding taeUpluginTemplate = fiUpluginTemplate.FileContent.ReadTextFromFile(); string sUpluginContent = taeUpluginTemplate.Text.Replace("%PLUGIN_VERSION%", GlobalDefines.FullMarketingVersionString); ImmutableFileItem fiUplugin = new ImmutableFileItem(fiUpluginTemplate.RelativePath.NameWithoutExtension, ImmutableByteStream.FromByteArray(taeUpluginTemplate.Encoding.GetBytes(sUpluginContent).ToImmutableArray())); byte[] bytesChecksumContent = MD5.Create().WithDispose(hasher => { foreach (var fi in files) hasher.TransformBlock(fi.FileContent); hasher.TransformFinalBlock(Array.Empty(), 0, 0); return hasher.Hash; }); ImmutableFileItem fiChecksum = new ImmutableFileItem("Resources/checksum", ImmutableByteStream.FromByteArray(bytesChecksumContent.ToImmutableArray())); ImmutableFileItem fiZip = Compression.ZipCompress(dirRiderLink.Name + ExtensionConstants.Zip, files.Where(fi => fi != fiUpluginTemplate).Concat(fiUplugin, fiChecksum), logger, whencompress: Compression.WhenToCompressEntry.Always); return new[] { new SubplatformFileForPackaging(subplatform.Name, fiZip) }; } return Array.Empty(); } } } ================================================ FILE: src/cpp/RiderLink/RiderLink.uplugin.template ================================================ { "FileVersion": 3, "Version": 1, "VersionName": "%PLUGIN_VERSION%", "FriendlyName": "RiderLink", "Description": "Plugin for establishing IPC connection with JetBrains Rider IDE", "Category": "Programming", "CreatedBy": "JetBrains", "CreatedByURL": "https://www.jetbrains.com/", "DocsURL": "", "MarketplaceURL": "", "SupportURL": "", "EnabledByDefault": true, "CanContainContent": false, "IsBetaVersion": false, "Installed": false, "Modules": [ { "Name": "RD", "Type": "EditorNoCommandlet", "LoadingPhase": "PreDefault" }, { "Name": "RiderLink", "Type": "EditorNoCommandlet", "LoadingPhase": "PreDefault" }, { "Name": "RiderLogging", "Type": "EditorNoCommandlet", "LoadingPhase": "PreDefault" }, { "Name": "RiderBlueprint", "Type": "EditorNoCommandlet", "LoadingPhase": "Default" }, { "Name": "RiderGameControl", "Type": "EditorNoCommandlet", "LoadingPhase": "Default" }, { "Name": "RiderShaderInfo", "Type": "EditorNoCommandlet", "LoadingPhase": "PostEngineInit" }, { "Name": "RiderLC", "Type": "EditorNoCommandlet", "LoadingPhase": "PostEngineInit" }, { "Name": "RiderDebuggerSupport", "Type": "EditorNoCommandlet", "LoadingPhase": "PreDefault", "PlatformAllowList": [ "Win64" ], "WhitelistPlatforms": [ "Win64" ] } ] } ================================================ FILE: src/cpp/RiderLink/Source/RD/RD.Build.cs ================================================ using System.IO; using UnrealBuildTool; public class RD : ModuleRules { public RD(ReadOnlyTargetRules Target) : base(Target) { PublicDependencyModuleNames.Add("Core"); bUseRTTI = true; #if UE_5_2_OR_LATER bDisableStaticAnalysis = true; #endif #if UE_5_2_OR_LATER IWYUSupport = IWYUSupport.KeepAsIs; #else bEnforceIWYU = false; #endif #if UE_5_6_OR_LATER CppStandard = CppStandardVersion.Cpp20; #elif UE_4_22_OR_LATER CppStandard = CppStandardVersion.Cpp17; #endif #if UE_4_22_OR_LATER PCHUsage = PCHUsageMode.NoPCHs; #else PCHUsage = PCHUsageMode.NoSharedPCHs; #endif #if UE_5_6_OR_LATER CppCompileWarningSettings.ShadowVariableWarningLevel = WarningLevel.Off; #elif UE_4_24_OR_LATER ShadowVariableWarningLevel = WarningLevel.Off; #else bEnableShadowVariableWarnings = false; #endif #if UE_4_24_OR_LATER bUseUnity = false; #else bFasterWithoutUnity = true; #endif PublicDefinitions.Add("_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS"); if (Target.Platform == UnrealTargetPlatform.Win64) { PublicDefinitions.Add("_WINSOCK_DEPRECATED_NO_WARNINGS"); PublicDefinitions.Add("_CRT_SECURE_NO_WARNINGS"); PublicDefinitions.Add("_CRT_NONSTDC_NO_DEPRECATE"); PublicDefinitions.Add("SPDLOG_WCHAR_FILENAMES"); PublicDefinitions.Add("SPDLOG_WCHAR_TO_UTF8_SUPPORT"); PrivateDefinitions.Add("WIN32_LEAN_AND_MEAN"); } if (Target.Platform == UnrealTargetPlatform.Mac) { PublicDefinitions.Add("_DARWIN"); } // Common dependencies PrivateDefinitions.Add("rd_framework_cpp_EXPORTS"); PrivateDefinitions.Add("rd_core_cpp_EXPORTS"); PrivateDefinitions.Add("spdlog_EXPORTS"); PrivateDefinitions.Add("FMT_EXPORT"); PrivateDefinitions.Add("_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR"); PublicDefinitions.Add("SPDLOG_NO_EXCEPTIONS"); PublicDefinitions.Add("SPDLOG_COMPILED_LIB"); PublicDefinitions.Add("SPDLOG_SHARED_LIB"); PublicDefinitions.Add( "nssv_CONFIG_SELECT_STRING_VIEW=nssv_STRING_VIEW_NONSTD"); PublicDefinitions.Add("FMT_SHARED"); string[] Paths = { "src", "src/rd_core_cpp", "src/rd_core_cpp/src/main" , "src/rd_framework_cpp", "src/rd_framework_cpp/src/main" , "src/rd_framework_cpp/src/main/util", "src/rd_gen_cpp/src" , "thirdparty", "thirdparty/ordered-map/include" , "thirdparty/optional/tl", "thirdparty/variant/include" , "thirdparty/string-view-lite/include", "thirdparty/spdlog/include" , "thirdparty/clsocket/src", "thirdparty/CTPL/include", "thirdparty/utf-cpp/include" }; foreach (var Item in Paths) { PublicIncludePaths.Add(Path.Combine(ModuleDirectory, Item)); } } } ================================================ FILE: src/cpp/RiderLink/Source/RD/RD.cpp ================================================ #include "RD.h" #include #define LOCTEXT_NAMESPACE "RD" DEFINE_LOG_CATEGORY(FLogRDModule); IMPLEMENT_MODULE(FRDModule, RD); #undef LOCTEXT_NAMESPACE ================================================ FILE: src/cpp/RiderLink/Source/RD/RD.h ================================================ // Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. #pragma once #include "Logging/LogMacros.h" #include "Logging/LogVerbosity.h" #include "Modules/ModuleInterface.h" DECLARE_LOG_CATEGORY_EXTERN(FLogRDModule, Log, All); class FRDModule : public IModuleInterface { public: FRDModule() = default; ~FRDModule() = default; }; ================================================ FILE: src/cpp/RiderLink/Source/RD/src/rd_core_cpp/rd_core_export.h ================================================ #ifndef RD_CORE_API_H #define RD_CORE_API_H #if defined(_WIN32) #ifdef RD_CORE_STATIC_DEFINE # define RD_CORE_API # define RD_CORE_NO_EXPORT #else # ifndef RD_CORE_API # ifdef rd_core_cpp_EXPORTS /* We are building this library */ # define RD_CORE_API __declspec(dllexport) # else /* We are using this library */ # define RD_CORE_API __declspec(dllimport) # endif # endif # ifndef RD_CORE_NO_EXPORT # define RD_CORE_NO_EXPORT # endif #endif #ifndef RD_CORE_DEPRECATED # define RD_CORE_DEPRECATED __declspec(deprecated) #endif #ifndef RD_CORE_DEPRECATED_EXPORT # define RD_CORE_DEPRECATED_EXPORT RD_CORE_API RD_CORE_DEPRECATED #endif #ifndef RD_CORE_DEPRECATED_NO_EXPORT # define RD_CORE_DEPRECATED_NO_EXPORT RD_CORE_NO_EXPORT RD_CORE_DEPRECATED #endif #if 0 /* DEFINE_NO_DEPRECATED */ # ifndef RD_CORE_NO_DEPRECATED # define RD_CORE_NO_DEPRECATED # endif #endif #endif #if defined(unix) || defined(__unix__) || defined(__unix) || defined(__APPLE__) #ifdef RD_CORE_STATIC_DEFINE # define RD_CORE_API # define RD_CORE_NO_EXPORT #else # ifndef RD_CORE_API # ifdef rd_core_cpp_EXPORTS /* We are building this library */ # define RD_CORE_API __attribute__((visibility("default"))) # else /* We are using this library */ # define RD_CORE_API __attribute__((visibility("default"))) # endif # endif # ifndef RD_CORE_NO_EXPORT # define RD_CORE_NO_EXPORT __attribute__((visibility("hidden"))) # endif #endif #ifndef RD_CORE_DEPRECATED # define RD_CORE_DEPRECATED __attribute__ ((__deprecated__)) #endif #ifndef RD_CORE_DEPRECATED_EXPORT # define RD_CORE_DEPRECATED_EXPORT RD_CORE_API RD_CORE_DEPRECATED #endif #ifndef RD_CORE_DEPRECATED_NO_EXPORT # define RD_CORE_DEPRECATED_NO_EXPORT RD_CORE_NO_EXPORT RD_CORE_DEPRECATED #endif #if 0 /* DEFINE_NO_DEPRECATED */ # ifndef RD_CORE_NO_DEPRECATED # define RD_CORE_NO_DEPRECATED # endif #endif #endif #endif /* RD_CORE_API_H */ ================================================ FILE: src/cpp/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/Lifetime.cpp ================================================ #include "Lifetime.h" #include #include #include #include namespace rd { /*thread_local */ Lifetime::Allocator Lifetime::allocator; LifetimeImpl* Lifetime::operator->() const { return ptr.operator->(); } std::once_flag onceFlag; Lifetime::Lifetime(bool is_eternal) : ptr(std::allocate_shared(allocator, is_eternal)) { std::call_once(onceFlag, [] { spdlog::set_default_logger(spdlog::stderr_color_mt("default", spdlog::color_mode::automatic)); }); } Lifetime Lifetime::create_nested() const { Lifetime lw(false); ptr->attach_nested(lw.ptr); return lw; } Lifetime const& Lifetime::Eternal() { static Lifetime ETERNAL(true); return ETERNAL; } bool operator==(Lifetime const& lw1, Lifetime const& lw2) { return lw1.ptr == lw2.ptr; } bool operator!=(Lifetime const& lw1, Lifetime const& lw2) { return !(lw1 == lw2); } } // namespace rd ================================================ FILE: src/cpp/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/Lifetime.h ================================================ #ifndef RD_CPP_CORE_LIFETIMEWRAPPER_H #define RD_CPP_CORE_LIFETIMEWRAPPER_H #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable:4251) #endif #include "LifetimeImpl.h" #include #include #include namespace rd { class Lifetime; template <> struct RD_CORE_API hash { size_t operator()(const Lifetime& value) const noexcept; }; class RD_CORE_API Lifetime final { private: using Allocator = std::allocator; static /*thread_local */ Allocator allocator; friend class LifetimeDefinition; friend struct hash; std::shared_ptr ptr; public: static Lifetime const& Eternal(); // region ctor/dtor Lifetime(Lifetime const& other) = default; Lifetime& operator=(Lifetime const& other) = default; Lifetime(Lifetime&& other) noexcept = default; Lifetime& operator=(Lifetime&& other) noexcept = default; ~Lifetime() = default; // endregion friend bool RD_CORE_API operator==(Lifetime const& lw1, Lifetime const& lw2); friend bool RD_CORE_API operator!=(Lifetime const& lw1, Lifetime const& lw2); explicit Lifetime(bool is_eternal = false); LifetimeImpl* operator->() const; Lifetime create_nested() const; }; inline size_t hash::operator()(const Lifetime& value) const noexcept { return hash >()(value.ptr); } } // namespace rd #if defined(_MSC_VER) #pragma warning(pop) #endif #endif // RD_CPP_CORE_LIFETIMEWRAPPER_H ================================================ FILE: src/cpp/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/LifetimeDefinition.cpp ================================================ #include "LifetimeDefinition.h" #include namespace rd { LifetimeDefinition::LifetimeDefinition(bool eternaled) : eternaled(eternaled), lifetime(eternaled) { } LifetimeDefinition::LifetimeDefinition(const Lifetime& parent) : LifetimeDefinition(false) { parent->attach_nested(lifetime.ptr); } bool LifetimeDefinition::is_terminated() const { return lifetime->is_terminated(); } void LifetimeDefinition::terminate() { lifetime->terminate(); } bool LifetimeDefinition::is_eternal() const { return lifetime->is_eternal(); } namespace { LifetimeDefinition ETERNAL(true); } std::shared_ptr LifetimeDefinition::get_shared_eternal() { return std::shared_ptr(&ETERNAL, [](LifetimeDefinition* /*ld*/) {}); } LifetimeDefinition::~LifetimeDefinition() { if (lifetime.ptr != nullptr) { // wasn't moved if (!is_eternal()) { if (!lifetime->is_terminated()) { lifetime->terminate(); } } } } } // namespace rd ================================================ FILE: src/cpp/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/LifetimeDefinition.h ================================================ #ifndef RD_CPP_CORE_LIFETIME_DEFINITION_H #define RD_CPP_CORE_LIFETIME_DEFINITION_H #include "util/core_traits.h" #include "LifetimeImpl.h" #include "Lifetime.h" #include #include #include namespace rd { class RD_CORE_API LifetimeDefinition { private: friend class SequentialLifetimes; bool eternaled = false; public: Lifetime lifetime; explicit LifetimeDefinition(bool is_eternal = false); explicit LifetimeDefinition(const Lifetime& parent); LifetimeDefinition(LifetimeDefinition const& other) = delete; LifetimeDefinition& operator=(LifetimeDefinition const& other) = delete; LifetimeDefinition(LifetimeDefinition&& other) = default; LifetimeDefinition& operator=(LifetimeDefinition&& other) = default; virtual ~LifetimeDefinition(); // static std::shared_ptr eternal; static std::shared_ptr get_shared_eternal(); bool is_terminated() const; bool is_eternal() const; void terminate(); template static auto use(F&& block) -> typename util::result_of_t { LifetimeDefinition definition(false); Lifetime lw = definition.lifetime.create_nested(); return block(lw); } }; } // namespace rd #endif // RD_CPP_CORE_LIFETIME_DEFINITION_H ================================================ FILE: src/cpp/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/LifetimeImpl.cpp ================================================ #include "LifetimeImpl.h" #include namespace rd { #if __cplusplus < 201703L LifetimeImpl::counter_t LifetimeImpl::get_id = 0; #endif LifetimeImpl::LifetimeImpl(bool is_eternal) : eternaled(is_eternal), id(LifetimeImpl::get_id++) { } void LifetimeImpl::terminate() { if (is_eternal()) return; terminated = true; // region thread-safety section actions_t actions_copy; { std::lock_guard guard(actions_lock); actions_copy = std::move(actions); actions.clear(); } // endregion for (auto it = actions_copy.rbegin(); it != actions_copy.rend(); ++it) { it->second(); } } bool LifetimeImpl::is_terminated() const { return terminated; } bool LifetimeImpl::is_eternal() const { return eternaled; } void LifetimeImpl::attach_nested(std::shared_ptr nested) { if (nested->is_terminated() || is_eternal()) return; std::function action = [nested] { nested->terminate(); }; counter_t action_id = add_action(action); nested->add_action([this, id = action_id] { actions.erase(id); }); } LifetimeImpl::~LifetimeImpl() { /*if (!is_eternal() && !is_terminated()) { spdlog::error("forget to terminate lifetime with id: {}", to_string(id)); terminate(); }*/ } } // namespace rd ================================================ FILE: src/cpp/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/LifetimeImpl.h ================================================ #ifndef RD_CPP_CORE_LIFETIME_H #define RD_CPP_CORE_LIFETIME_H #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable:4251) #endif #include #include #include #include #include #include #include #include #include namespace rd { class RD_CORE_API LifetimeImpl final { public: friend class LifetimeDefinition; friend class Lifetime; using counter_t = int32_t; private: bool eternaled = false; std::atomic terminated{false}; counter_t id = 0; counter_t action_id_in_map = 0; using actions_t = ordered_map, rd::hash>; actions_t actions; void terminate(); std::mutex actions_lock; public: // region ctor/dtor explicit LifetimeImpl(bool is_eternal = false); LifetimeImpl(LifetimeImpl const& other) = delete; ~LifetimeImpl(); // endregion template counter_t add_action(F&& action) { std::lock_guard guard(actions_lock); if (is_eternal()) { return -1; } if (is_terminated()) { throw std::invalid_argument("Already Terminated"); } actions[action_id_in_map] = std::forward(action); return action_id_in_map++; } void remove_action(counter_t i) { std::lock_guard guard(actions_lock); actions.erase(i); } #if __cplusplus >= 201703L static inline counter_t get_id = 0; #else static counter_t get_id; #endif template void bracket(F&& opening, G&& closing) { if (is_terminated()) return; opening(); add_action(std::forward(closing)); } bool is_terminated() const; bool is_eternal() const; void attach_nested(std::shared_ptr nested); }; } // namespace rd #if defined(_MSC_VER) #pragma warning(pop) #endif #endif // RD_CPP_CORE_LIFETIME_H ================================================ FILE: src/cpp/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/SequentialLifetimes.cpp ================================================ #include "SequentialLifetimes.h" namespace rd { SequentialLifetimes::SequentialLifetimes(Lifetime parent_lifetime) : parent_lifetime(std::move(parent_lifetime)) { this->parent_lifetime->add_action([this] { set_current_lifetime(LifetimeDefinition::get_shared_eternal()); }); } Lifetime SequentialLifetimes::next() { std::shared_ptr new_def = std::make_shared(parent_lifetime); set_current_lifetime(new_def); return current_def->lifetime; } void SequentialLifetimes::terminate_current() { set_current_lifetime(LifetimeDefinition::get_shared_eternal()); } bool SequentialLifetimes::is_terminated() const { return current_def->is_eternal() || current_def->is_terminated(); } void SequentialLifetimes::set_current_lifetime(std::shared_ptr new_def) { std::shared_ptr prev = current_def; current_def = new_def; prev->terminate(); } } // namespace rd ================================================ FILE: src/cpp/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/SequentialLifetimes.h ================================================ #ifndef RD_CPP_CORE_SEQUENTIAL_LIFETIMES_H #define RD_CPP_CORE_SEQUENTIAL_LIFETIMES_H #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable:4251) #endif #include "LifetimeDefinition.h" #include "Lifetime.h" #include namespace rd { class RD_CORE_API SequentialLifetimes { private: std::shared_ptr current_def = LifetimeDefinition::get_shared_eternal(); Lifetime parent_lifetime; public: // region ctor/dtor SequentialLifetimes() = delete; SequentialLifetimes(SequentialLifetimes const&) = delete; SequentialLifetimes& operator=(SequentialLifetimes const&) = delete; SequentialLifetimes(SequentialLifetimes&&) = delete; SequentialLifetimes& operator=(SequentialLifetimes&&) = delete; explicit SequentialLifetimes(Lifetime parent_lifetime); // endregion Lifetime next(); void terminate_current(); bool is_terminated() const; void set_current_lifetime(std::shared_ptr new_def); }; } // namespace rd #if defined(_MSC_VER) #pragma warning(pop) #endif #endif // RD_CPP_CORE_SEQUENTIAL_LIFETIMES_H ================================================ FILE: src/cpp/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/Property.h ================================================ #ifndef RD_CPP_CORE_PROPERTY_H #define RD_CPP_CORE_PROPERTY_H #include "base/IProperty.h" #include "reactive/base/SignalX.h" #include namespace rd { /** * \brief complete class which has \a Property 's properties. * \tparam T type of stored value (may be abstract) */ template class Property : public IProperty { using WT = typename IProperty::WT; public: // region ctor/dtor Property() = default; Property(Property&& other) = default; Property& operator=(Property&& other) = default; virtual ~Property() = default; template explicit Property(F&& value) : IProperty(std::forward(value)) { } // endregion T const& get() const override { RD_ASSERT_THROW_MSG(this->has_value(), "get of uninitialized value from property"); return *(this->value); } void set(WT new_value) const override { if (!this->has_value() || (this->get() != wrapper::get(new_value))) { if (this->has_value()) { this->before_change.fire(*(this->value)); } this->value = std::move(new_value); this->change.fire(*(this->value)); } } friend bool operator==(const Property& lhs, const Property& rhs) { return &lhs == &rhs; } friend bool operator!=(const Property& lhs, const Property& rhs) { return !(rhs == lhs); } friend std::string to_string(Property const& value) { return value.has_value() ? to_string(value.get()) : "empty property"s; } }; } // namespace rd static_assert(std::is_move_constructible>::value, "Is not move constructible from Property"); #endif // RD_CPP_CORE_PROPERTY_H ================================================ FILE: src/cpp/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/ViewableList.h ================================================ #ifndef RD_CPP_CORE_VIEWABLELIST_H #define RD_CPP_CORE_VIEWABLELIST_H #include "base/IViewableList.h" #include "reactive/base/SignalX.h" #include "util/core_util.h" #include #include #include namespace rd { /** * \brief complete class which has @code IViewableList's properties */ template > class ViewableList : public IViewableList { public: using Event = typename IViewableList::Event; private: using WA = typename std::allocator_traits::template rebind_alloc>; using data_t = std::vector, WA>; mutable data_t list; Signal change; protected: using WT = typename IViewableList::WT; const std::vector>& getList() const override { return list; } public: // region ctor/dtor ViewableList() = default; ViewableList(ViewableList&&) = default; ViewableList& operator=(ViewableList&&) = default; virtual ~ViewableList() = default; // endregion // region iterators public: class iterator { friend class ViewableList; typename data_t::iterator it_; explicit iterator(const typename data_t::iterator& it) : it_(it) { } public: using iterator_category = std::random_access_iterator_tag; using value_type = T; using difference_type = std::ptrdiff_t; using pointer = T const*; using reference = T const&; iterator(const iterator& other) = default; iterator(iterator&& other) noexcept = default; iterator& operator=(const iterator& other) = default; iterator& operator=(iterator&& other) noexcept = default; iterator& operator++() { ++it_; return *this; } iterator operator++(int) { auto it = *this; ++*this; return it; } iterator& operator--() { --it_; return *this; } iterator operator--(int) { auto it = *this; --*this; return it; } iterator& operator+=(difference_type delta) { it_ += delta; return *this; } iterator& operator-=(difference_type delta) { it_ -= delta; return *this; } iterator operator+(difference_type delta) const { auto it = *this; return it += delta; } iterator operator-(difference_type delta) const { auto it = *this; return it -= delta; } difference_type operator-(iterator const& other) const { return it_ - other.it_; } bool operator<(iterator const& other) const noexcept { return this->it_ < other.it_; } bool operator>(iterator const& other) const noexcept { return this->it_ > other.it_; } bool operator==(iterator const& other) const noexcept { return this->it_ == other.it_; } bool operator!=(iterator const& other) const noexcept { return !(*this == other); } bool operator<=(iterator const& other) const noexcept { return (this->it_ < other.it_) || (*this == other); } bool operator>=(iterator const& other) const noexcept { return (this->it_ > other.it_) || (*this == other); } reference operator*() noexcept { return **it_; } reference operator*() const noexcept { return **it_; } pointer operator->() noexcept { return (*it_).get(); } pointer operator->() const noexcept { return (*it_).get(); } }; using reverse_iterator = std::reverse_iterator; iterator begin() const { return iterator(list.begin()); } iterator end() const { return iterator(list.end()); } reverse_iterator rbegin() const { return reverse_iterator(end()); } reverse_iterator rend() const { return reverse_iterator(begin()); } // endregion void advise(Lifetime lifetime, std::function handler) const override { if (lifetime->is_terminated()) return; change.advise(lifetime, handler); for (int32_t i = 0; i < static_cast(size()); ++i) { handler(typename Event::Add(i, &(*list[i]))); } } bool add(WT element) const override { list.emplace_back(std::move(element)); change.fire(typename Event::Add(static_cast(size()) - 1, &(*list.back()))); return true; } bool add(size_t index, WT element) const override { list.emplace(list.begin() + index, std::move(element)); change.fire(typename Event::Add(static_cast(index), &(*list[index]))); return true; } WT removeAt(size_t index) const override { auto res = std::move(list[index]); list.erase(list.begin() + index); change.fire(typename Event::Remove(static_cast(index), &(*res))); return wrapper::unwrap(std::move(res)); } bool remove(T const& element) const override { auto it = std::find_if(list.begin(), list.end(), [&element](auto const& p) { return *p == element; }); if (it == list.end()) { return false; } ViewableList::removeAt(std::distance(list.begin(), it)); return true; } T const& get(size_t index) const override { return *list[index]; } WT set(size_t index, WT element) const override { auto old_value = std::move(list[index]); list[index] = Wrapper(std::move(element)); change.fire(typename Event::Update(static_cast(index), &(*old_value), &(*list[index]))); //??? return wrapper::unwrap(std::move(old_value)); } bool addAll(size_t index, std::vector elements) const override { for (auto& element : elements) { ViewableList::add(index, std::move(element)); ++index; } return true; } bool addAll(std::vector elements) const override { for (auto&& element : elements) { ViewableList::add(std::move(element)); } return true; } void clear() const override { std::vector changes; for (size_t i = size(); i > 0; --i) { changes.push_back(typename Event::Remove(static_cast(i - 1), &(*list[i - 1]))); } for (auto const& e : changes) { change.fire(e); } list.clear(); } bool removeAll(std::vector elements) const override { // TO-DO faster // std::unordered_set set(elements.begin(), elements.end()); bool res = false; for (size_t i = list.size(); i > 0; --i) { auto const& x = list[i - 1]; if (std::count_if(elements.begin(), elements.end(), [&x](auto const& elem) { return wrapper::TransparentKeyEqual()(elem, x); }) > 0) { removeAt(i - 1); res = true; } } return res; } size_t size() const override { return list.size(); } bool empty() const override { return list.empty(); } }; } // namespace rd static_assert(std::is_move_constructible>::value, "Is move constructible from ViewableList"); #endif // RD_CPP_CORE_VIEWABLELIST_H ================================================ FILE: src/cpp/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/ViewableMap.h ================================================ #ifndef RD_CPP_CORE_VIEWABLE_MAP_H #define RD_CPP_CORE_VIEWABLE_MAP_H #include "base/IViewableMap.h" #include "reactive/base/SignalX.h" #include #include #include #include #include namespace rd { /** * \brief complete class which has @code IViewableMap's properties */ template , typename VA = std::allocator> class ViewableMap : public IViewableMap { public: using Event = typename IViewableMap::Event; private: using WK = typename IViewableMap::WK; using WV = typename IViewableMap::WV; using OV = typename IViewableMap::OV; using PA = typename std::allocator_traits::template rebind_alloc, Wrapper>>; Signal change; using data_t = ordered_map, Wrapper, wrapper::TransparentHash, wrapper::TransparentKeyEqual, PA>; mutable data_t map; public: // region ctor/dtor ViewableMap() = default; ViewableMap(ViewableMap&&) = default; ViewableMap& operator=(ViewableMap&&) = default; virtual ~ViewableMap() = default; // endregion // region iterators public: class iterator { friend class ViewableMap; mutable typename data_t::iterator it_; explicit iterator(const typename data_t::iterator& it) : it_(it) { } public: using iterator_category = typename data_t::iterator::iterator_category; using key_type = K; using value_type = V; using difference_type = std::ptrdiff_t; using reference = V const&; using pointer = V const*; iterator(const iterator& other) = default; iterator(iterator&& other) noexcept = default; iterator& operator=(const iterator& other) = default; iterator& operator=(iterator&& other) noexcept = default; iterator& operator++() { ++it_; return *this; } iterator operator++(int) { auto it = *this; ++*this; return it; } iterator& operator--() { --it_; return *this; } iterator operator--(int) { auto it = *this; --*this; return it; } iterator& operator+=(difference_type delta) { it_ += delta; return *this; } iterator& operator-=(difference_type delta) { it_ -= delta; return *this; } iterator operator+(difference_type delta) const { auto it = *this; return it += delta; } iterator operator-(difference_type delta) const { auto it = *this; return it -= delta; } difference_type operator-(iterator const& other) const { return it_ - other.it_; } bool operator<(iterator const& other) const noexcept { return this->it_ < other.it_; } bool operator>(iterator const& other) const noexcept { return this->it_ > other.it_; } bool operator==(iterator const& other) const noexcept { return this->it_ == other.it_; } bool operator!=(iterator const& other) const noexcept { return !(*this == other); } bool operator<=(iterator const& other) const noexcept { return (this->it_ < other.it_) || (*this == other); } bool operator>=(iterator const& other) const noexcept { return (this->it_ > other.it_) || (*this == other); } reference operator*() const noexcept { return *it_.value(); } pointer operator->() const noexcept { return it_.value().get(); } key_type const& key() const { return *it_.key(); } value_type const& value() const { return *it_.value(); } }; class reverse_iterator : public std::reverse_iterator { using base_t = std::reverse_iterator; public: using iterator_category = typename iterator::iterator_category; using key_type = typename iterator::key_type; using value_type = typename iterator::value_type; using difference_type = typename iterator::difference_type; using reference = typename iterator::reference; using pointer = typename iterator::pointer; reverse_iterator(const reverse_iterator& other) = default; reverse_iterator& operator=(const reverse_iterator& other) = default; explicit reverse_iterator(const iterator& other) : base_t(other){}; reverse_iterator& operator=(const iterator& other) { static_cast(*this) = other; }; key_type const& key() const { auto it = base_t::current; return (--(it)).key(); } value_type const& value() const { auto it = base_t::current; return (--it).value(); } }; iterator begin() const { return iterator(map.begin()); } iterator end() const { return iterator(map.end()); } reverse_iterator rbegin() const { return reverse_iterator(end()); } reverse_iterator rend() const { return reverse_iterator(begin()); } // endregion void advise(Lifetime lifetime, std::function handler) const override { change.advise(lifetime, handler); /*for (auto const &[key, value] : map) {*/ for (auto const& it : map) { auto& key = it.first; auto& value = it.second; handler(Event(typename Event::Add(&(*key), &(*value)))); ; } } const V* get(K const& key) const override { auto it = map.find(key); if (it == map.end()) { return nullptr; } return &(*it->second); } const V* set(WK key, WV value) const override { if (map.count(key) == 0) { /*auto[it, success] = map.emplace(std::make_unique(std::move(key)), std::make_unique(std::move(value)));*/ auto node = map.emplace(std::move(key), std::move(value)); auto& it = node.first; auto const& key_ptr = it->first; auto const& value_ptr = it->second; change.fire(typename Event::Add(&(*key_ptr), &(*value_ptr))); return nullptr; } else { auto it = map.find(key); auto const& key_ptr = it->first; auto const& value_ptr = it->second; if (*value_ptr != wrapper::get(value)) { // TO-DO more effective Wrapper old_value = std::move(map.at(key)); map.at(key_ptr) = Wrapper(std::move(value)); change.fire(typename Event::Update(&(*key_ptr), &(*old_value), &(*value_ptr))); } return &*(value_ptr); } } OV remove(K const& key) const override { if (map.count(key) > 0) { Wrapper old_value = std::move(map.at(key)); change.fire(typename Event::Remove(&key, &(*old_value))); map.erase(key); return wrapper::unwrap(std::move(old_value)); } return nullopt; } void clear() const override { std::vector changes; /*for (auto const &[key, value] : map) {*/ for (auto const& it : map) { changes.push_back(typename Event::Remove(&(*it.first), &(*it.second))); } for (auto const& it : changes) { change.fire(it); } map.clear(); } size_t size() const override { return map.size(); } bool empty() const override { return map.empty(); } }; } // namespace rd static_assert(std::is_move_constructible>::value, "Is move constructible from ViewableMap"); #endif // RD_CPP_CORE_VIEWABLE_MAP_H ================================================ FILE: src/cpp/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/ViewableSet.h ================================================ #ifndef RD_CPP_CORE_VIEWABLESET_H #define RD_CPP_CORE_VIEWABLESET_H #include "base/IViewableSet.h" #include "reactive/base/SignalX.h" #include #include namespace rd { /** * \brief complete class which has @code IViewableSet's properties * \tparam T */ template > class ViewableSet : public IViewableSet { public: using Event = typename IViewableSet::Event; using IViewableSet::advise; private: using WT = typename IViewableSet::WT; using WA = typename std::allocator_traits::template rebind_alloc>; Signal change; using data_t = ordered_set, wrapper::TransparentHash, wrapper::TransparentKeyEqual, WA>; mutable data_t set; public: // region ctor/dtor ViewableSet() = default; ViewableSet(ViewableSet&&) = default; ViewableSet& operator=(ViewableSet&&) = default; virtual ~ViewableSet() = default; // endregion // region iterators public: class iterator { friend class ViewableSet; typename data_t::iterator it_; explicit iterator(const typename data_t::iterator& it) : it_(it) { } public: using iterator_category = std::random_access_iterator_tag; using value_type = T; using difference_type = std::ptrdiff_t; using pointer = T const*; using reference = T const&; iterator(const iterator& other) = default; iterator(iterator&& other) noexcept = default; iterator& operator=(const iterator& other) = default; iterator& operator=(iterator&& other) noexcept = default; iterator& operator++() { ++it_; return *this; } iterator operator++(int) { auto it = *this; ++*this; return it; } iterator& operator--() { --it_; return *this; } iterator operator--(int) { auto it = *this; --*this; return it; } iterator& operator+=(difference_type delta) { it_ += delta; return *this; } iterator& operator-=(difference_type delta) { it_ -= delta; return *this; } iterator operator+(difference_type delta) const { auto it = *this; return it += delta; } iterator operator-(difference_type delta) const { auto it = *this; return it -= delta; } difference_type operator-(iterator const& other) const { return it_ - other.it_; } bool operator<(iterator const& other) const noexcept { return this->it_ < other.it_; } bool operator>(iterator const& other) const noexcept { return this->it_ > other.it_; } bool operator==(iterator const& other) const noexcept { return this->it_ == other.it_; } bool operator!=(iterator const& other) const noexcept { return !(*this == other); } bool operator<=(iterator const& other) const noexcept { return (this->it_ < other.it_) || (*this == other); } bool operator>=(iterator const& other) const noexcept { return (this->it_ > other.it_) || (*this == other); } reference operator*() const noexcept { return **it_; } pointer operator->() const noexcept { return (*it_).get(); } }; using reverse_iterator = std::reverse_iterator; iterator begin() const { return iterator(set.begin()); } iterator end() const { return iterator(set.end()); } reverse_iterator rbegin() const { return reverse_iterator(end()); } reverse_iterator rend() const { return reverse_iterator(begin()); } // endregion bool add(WT element) const override { /*auto const &[it, success] = set.emplace(std::make_unique(std::move(element)));*/ auto const& it = set.emplace(std::move(element)); if (!it.second) { return false; } change.fire(Event(AddRemove::ADD, &(wrapper::get(*it.first)))); return true; } bool addAll(std::vector elements) const override { for (auto&& element : elements) { ViewableSet::add(std::move(element)); } return true; } void clear() const override { std::vector changes; for (auto const& element : set) { changes.push_back(Event(AddRemove::REMOVE, &(*element))); } for (auto const& e : changes) { change.fire(e); } set.clear(); } bool remove(T const& element) const override { if (!ViewableSet::contains(element)) { return false; } auto it = set.find(element); change.fire(Event(AddRemove::REMOVE, &(wrapper::get(*it)))); set.erase(it); return true; } void advise(Lifetime lifetime, std::function handler) const override { for (auto const& x : set) { handler(Event(AddRemove::ADD, &(*x))); } change.advise(lifetime, handler); } size_t size() const override { return set.size(); } bool contains(T const& element) const override { return set.count(element) > 0; } bool empty() const override { return set.empty(); } template bool emplace_add(Args&&... args) const { return add(WT{std::forward(args)...}); } }; } // namespace rd static_assert(std::is_move_constructible>::value, "Is move constructible from ViewableSet"); #endif // RD_CPP_CORE_VIEWABLESET_H ================================================ FILE: src/cpp/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IProperty.h ================================================ #ifndef RD_CPP_IPROPERTY_H #define RD_CPP_IPROPERTY_H #include "SignalX.h" #include "IPropertyBase.h" #include #include #include namespace rd { /** * \brief A mutable property. * \tparam T type of stored value (may be abstract) */ template class IProperty : public IPropertyBase { protected: using WT = typename IPropertyBase::WT; public: // region ctor/dtor IProperty() = default; IProperty(IProperty&& other) = default; IProperty& operator=(IProperty&& other) = default; explicit IProperty(T const& value) : IPropertyBase(value) { } template explicit IProperty(F&& value) : IPropertyBase(std::forward(value)) { } virtual ~IProperty() = default; // endregion virtual T const& get() const = 0; private: void advise0(Lifetime lifetime, std::function handler, Signal const& signal) const { if (lifetime->is_terminated()) { return; } signal.advise(lifetime, handler); if (this->has_value()) { handler(this->get()); } } void advise_before(Lifetime lifetime, std::function handler) const override { advise0(lifetime, handler, this->before_change); } public: void advise(Lifetime lifetime, std::function handler) const override { advise0(lifetime, std::move(handler), this->change); } /** * \brief set value of type T or derived type to it. */ virtual void set(value_or_wrapper) const = 0; /** * \brief construct value of type T and delegate call to set */ template void emplace(Args&&... args) const { set(value_or_wrapper{std::forward(args)...}); } void set_if_empty(WT new_value) const { if (!this->has_value()) { set(std::move(new_value)); } } }; } // namespace rd #endif // RD_CPP_IPROPERTY_H ================================================ FILE: src/cpp/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IPropertyBase.h ================================================ #ifndef RD_CPP_IPROPERTYBASE_H #define RD_CPP_IPROPERTYBASE_H #include "interfaces.h" #include "SignalX.h" #include #include #include "thirdparty.hpp" namespace rd { template class IPropertyBase : public ISource, public IViewable { protected: mutable property_storage value; Signal change, before_change; using WT = value_or_wrapper; public: bool has_value() const { return (bool) (value); } // region ctor/dtor IPropertyBase() = default; IPropertyBase(IPropertyBase&& other) = default; IPropertyBase& operator=(IPropertyBase&& other) = default; template explicit IPropertyBase(F&& value) : value(std::forward(value)) { } virtual ~IPropertyBase() = default; // endregion virtual void advise_before(Lifetime lifetime, std::function handler) const = 0; void view(Lifetime lifetime, std::function handler) const override { if (lifetime->is_terminated()) return; Lifetime lf = lifetime.create_nested(); std::shared_ptr seq = std::make_shared(lf); this->advise_before(lf, [lf, seq](T const& /*v*/) { if (!lf->is_terminated()) { seq->terminate_current(); } }); this->advise(lf, [lf, seq, handler](T const& v) { if (!lf->is_terminated()) { handler(seq->next(), v); } }); } }; } // namespace rd #endif // RD_CPP_IPROPERTYBASE_H ================================================ FILE: src/cpp/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IViewableList.h ================================================ #ifndef RD_CPP_IVIEWABLELIST_H #define RD_CPP_IVIEWABLELIST_H #include "interfaces.h" #include "viewable_collections.h" #include #include #include #include #include #include #include #include "thirdparty.hpp" namespace rd { namespace detail { template class ListEvent { public: class Add { public: int32_t index; T const* new_value; Add(int32_t index, T const* new_value) : index(index), new_value(new_value) { } }; class Update { public: int32_t index; T const* old_value; T const* new_value; Update(int32_t index, T const* old_value, T const* new_value) : index(index), old_value(old_value), new_value(new_value) { } }; class Remove { public: int32_t index; T const* old_value; Remove(int32_t index, T const* old_value) : index(index), old_value(old_value) { } }; variant v; ListEvent(Add x) : v(x) { } ListEvent(Update x) : v(x) { } ListEvent(Remove x) : v(x) { } int32_t get_index() const { return visit(util::make_visitor([](Add const& e) { return e.index; }, [](Update const& e) { return e.index; }, [](Remove const& e) { return e.index; }), v); } T const* get_new_value() const { return visit(util::make_visitor([](Add const& e) { return e.new_value; }, [](Update const& e) { return e.new_value; }, [](Remove const& /*e*/) { return static_cast(nullptr); }), v); } friend std::string to_string(ListEvent const& e) { std::string res = visit( util::make_visitor( [](typename ListEvent::Add const& e) { return "Add " + std::to_string(e.index) + ":" + to_string(*e.new_value); }, [](typename ListEvent::Update const& e) { return "Update " + std::to_string(e.index) + ":" + // to_string(e.old_value) + ":" + to_string(*e.new_value); }, [](typename ListEvent::Remove const& e) { return "Remove " + std::to_string(e.index); }), e.v); return res; } }; } // namespace detail /** * \brief A list allowing its contents to be observed. * \tparam T type of stored values (may be abstract) */ template class IViewableList : public IViewable>, public ISource> { protected: using WT = value_or_wrapper; public: /** * \brief Represents an addition, update or removal of an element in the list. */ using Event = typename detail::ListEvent; protected: mutable rd::unordered_map> lifetimes; public: // region ctor/dtor IViewableList() = default; IViewableList(IViewableList&&) = default; IViewableList& operator=(IViewableList&&) = default; virtual ~IViewableList() = default; // endregion /** * \brief Adds a subscription to additions and removals of list elements. When a list element is updated, * the [handler] is called twice: to report the removal of the old element and the addition of the new one. * \param lifetime lifetime of subscription. * \param handler to be called. */ void advise_add_remove(Lifetime lifetime, std::function handler) const { advise(lifetime, [handler](Event e) { visit(util::make_visitor([handler](typename Event::Add const& e) { handler(AddRemove::ADD, e.index, *e.new_value); }, [handler](typename Event::Update const& e) { handler(AddRemove::REMOVE, e.index, *e.old_value); handler(AddRemove::ADD, e.index, *e.new_value); }, [handler](typename Event::Remove const& e) { handler(AddRemove::REMOVE, e.index, *e.old_value); }), e.v); }); } /** * \brief Adds a subscription to changes of the contents of the list. * \param lifetime lifetime of subscription. * \param handler to be called. */ void view(Lifetime lifetime, std::function const&)> handler) const override { view(lifetime, [handler](Lifetime lt, size_t idx, T const& v) { handler(lt, std::make_pair(idx, &v)); }); } /** * \brief @see view above */ void view(Lifetime lifetime, std::function handler) const { advise_add_remove(lifetime, [this, lifetime, handler](AddRemove kind, size_t idx, T const& value) { switch (kind) { case AddRemove::ADD: { LifetimeDefinition def(lifetime); std::vector& v = lifetimes[lifetime]; auto it = v.emplace(v.begin() + idx, std::move(def)); handler(it->lifetime, idx, value); break; } case AddRemove::REMOVE: { LifetimeDefinition def = std::move(lifetimes.at(lifetime)[idx]); std::vector& v = lifetimes.at(lifetime); v.erase(v.begin() + idx); def.terminate(); break; } } }); } void advise(Lifetime lifetime, std::function handler) const override = 0; virtual bool add(WT element) const = 0; virtual bool add(size_t index, WT element) const = 0; virtual WT removeAt(size_t index) const = 0; virtual bool remove(T const& element) const = 0; virtual T const& get(size_t index) const = 0; virtual WT set(size_t index, WT element) const = 0; virtual bool addAll(size_t index, std::vector elements) const = 0; virtual bool addAll(std::vector elements) const = 0; virtual void clear() const = 0; virtual bool removeAll(std::vector elements) const = 0; virtual size_t size() const = 0; virtual bool empty() const = 0; template bool emplace_add(Args&&... args) const { return add(WT{std::forward(args)...}); } template bool emplace_add(size_t index, Args&&... args) const { return add(index, WT{std::forward(args)...}); } template WT emplace_set(size_t index, Args&&... args) const { return set(index, WT{std::forward(args)...}); } template friend typename std::enable_if<(!std::is_abstract::value), std::vector>::type convert_to_list( IViewableList const& list); protected: virtual const std::vector>& getList() const = 0; }; template typename std::enable_if<(!std::is_abstract::value), std::vector>::type convert_to_list(IViewableList const& list) { std::vector res(list.size()); std::transform(list.getList().begin(), list.getList().end(), res.begin(), [](Wrapper const& ptr) { return *ptr; }); return res; } } // namespace rd static_assert( std::is_move_constructible::Event>::value, "Is move constructible from IViewableList::Event"); #endif // RD_CPP_IVIEWABLELIST_H ================================================ FILE: src/cpp/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IViewableMap.h ================================================ #ifndef RD_CPP_IVIEWABLEMAP_H #define RD_CPP_IVIEWABLEMAP_H #include "lifetime/LifetimeDefinition.h" #include "util/overloaded.h" #include "interfaces.h" #include "viewable_collections.h" #include "util/core_util.h" #include "std/unordered_map.h" #include "thirdparty.hpp" namespace rd { namespace detail { template class MapEvent { public: class Add { public: K const* key; V const* new_value; Add(K const* key, V const* new_value) : key(key), new_value(new_value) { } }; class Update { public: K const* key; V const* old_value; V const* new_value; Update(K const* key, V const* old_value, V const* new_value) : key(key), old_value(old_value), new_value(new_value) { } }; class Remove { public: K const* key; V const* old_value; Remove(K const* key, V const* old_value) : key(key), old_value(old_value) { } }; variant v; MapEvent(Add x) : v(x) { } MapEvent(Update x) : v(x) { } MapEvent(Remove x) : v(x) { } K const* get_key() const { return visit( util::make_visitor([](typename MapEvent::Add const& e) { return e.key; }, [](typename MapEvent::Update const& e) { return e.key; }, [](typename MapEvent::Remove const& e) { return e.key; }), v); } V const* get_old_value() const { return visit(util::make_visitor([](typename MapEvent::Add const&) { return static_cast(nullptr); }, [](typename MapEvent::Update const& e) { return e.old_value; }, [](typename MapEvent::Remove const& e) { e.old_value; }), v); } V const* get_new_value() const { return visit(util::make_visitor([](typename MapEvent::Add const& e) { return e.new_value; }, [](typename MapEvent::Update const& e) { return e.new_value; }, [](typename MapEvent::Remove const& /*e*/) { return static_cast(nullptr); }), v); } friend std::string to_string(MapEvent const& e) { std::string res = visit(util::make_visitor([](typename MapEvent::Add const& e) -> std::string { return "Add " + to_string(*e.key) + ":" + to_string(*e.new_value); }, [](typename MapEvent::Update const& e) -> std::string { return "Update " + to_string(*e.key) + ":" + // to_string(e.old_value) + ":" + to_string(*e.new_value); }, [](typename MapEvent::Remove const& e) -> std::string { return "Remove " + to_string(*e.key); }), e.v); return res; } }; } // namespace detail /** * \brief A set allowing its contents to be observed. * \tparam K type of stored keys (may be abstract) * \tparam V type of stored values (may be abstract) */ template class IViewableMap : public IViewable>, public ISource> { protected: using WK = value_or_wrapper; using WV = value_or_wrapper; using OV = opt_or_wrapper; mutable rd::unordered_map, wrapper::TransparentKeyEqual>> lifetimes; public: /** * \brief Represents an addition, update or removal of an element in the map. */ using Event = typename detail::MapEvent; // region ctor/dtor IViewableMap() = default; IViewableMap(IViewableMap&&) = default; IViewableMap& operator=(IViewableMap&&) = default; virtual ~IViewableMap() = default; // endregion void view(Lifetime lifetime, std::function const&) > handler) const override { advise_add_remove(lifetime, [this, lifetime, handler](AddRemove kind, K const& key, V const& value) { const std::pair entry = std::make_pair(&key, &value); switch (kind) { case AddRemove::ADD: { if (lifetimes[lifetime].count(key) == 0) { /*auto const &[it, inserted] = lifetimes[lifetime].emplace(key, LifetimeDefinition(lifetime));*/ auto const& pair = lifetimes[lifetime].emplace(&key, LifetimeDefinition(lifetime)); auto& it = pair.first; auto& inserted = pair.second; RD_ASSERT_MSG(inserted, "lifetime definition already exists in viewable map by key:" + to_string(key)); handler(it->second.lifetime, entry); } break; } case AddRemove::REMOVE: { RD_ASSERT_MSG(lifetimes.at(lifetime).count(key) > 0, "attempting to remove non-existing lifetime in viewable map by key:" + to_string(key)); LifetimeDefinition def = std::move(lifetimes.at(lifetime).at(key)); lifetimes.at(lifetime).erase(key); def.terminate(); break; } } }); } /** * \brief Adds a subscription to additions and removals of map elements. When a map element is updated, the [handler] * is called twice: to report the removal of the old element and the addition of the new one. * \param lifetime lifetime of subscription. * \param handler to be called. */ void advise_add_remove(Lifetime lifetime, std::function handler) const { advise(lifetime, [handler](Event e) { visit(util::make_visitor([handler](typename Event::Add const& e) { handler(AddRemove::ADD, *e.key, *e.new_value); }, [handler](typename Event::Update const& e) { handler(AddRemove::REMOVE, *e.key, *e.old_value); handler(AddRemove::ADD, *e.key, *e.new_value); }, [handler](typename Event::Remove const& e) { handler(AddRemove::REMOVE, *e.key, *e.old_value); }), e.v); }); } /** * \brief Adds a subscription to changes of the contents of the map, with the handler receiving keys and values * as separate parameters. * * \details When [handler] is initially added, it is called receiving all keys and values currently in the map. * Every time a key/value pair is added to the map, the [handler] is called receiving the new key and value. * The [Lifetime] instance passed to the handler expires when the key/value pair is removed from the map. * * \param lifetime lifetime of subscription. * \param handler to be called. */ void view(Lifetime lifetime, std::function handler) const { view(lifetime, [handler](Lifetime lf, const std::pair entry) { handler(lf, *entry.first, *entry.second); }); } void advise(Lifetime lifetime, std::function handler) const override = 0; virtual const V* get(K const&) const = 0; virtual const V* set(WK, WV) const = 0; virtual OV remove(K const&) const = 0; virtual void clear() const = 0; virtual size_t size() const = 0; virtual bool empty() const = 0; template const V* emplace_set(WK wk, Args&&... args) const { return set(std::move(wk), WV{std::forward(args)...}); } }; } // namespace rd static_assert(std::is_move_constructible::Event>::value, "Is move constructible from IViewableMap::Event"); #endif // RD_CPP_IVIEWABLEMAP_H ================================================ FILE: src/cpp/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IViewableSet.h ================================================ #ifndef RD_CPP_IVIEWABLESET_H #define RD_CPP_IVIEWABLESET_H #include "interfaces.h" #include "viewable_collections.h" #include #include #include #include namespace rd { namespace detail { template class SetEvent { public: SetEvent(AddRemove kind, T const* value) : kind(kind), value(value) { } AddRemove kind; T const* value; friend std::string to_string(SetEvent const& e) { return to_string(e.kind) + ":" + to_string(*e.value); } }; } // namespace detail /** * \brief A set allowing its contents to be observed. * \tparam T type of stored values (may be abstract) */ template > class IViewableSet : public IViewable, public ISource> { protected: using WT = value_or_wrapper; mutable rd::unordered_map, wrapper::TransparentKeyEqual>> lifetimes; public: // region ctor/dtor IViewableSet() = default; IViewableSet(IViewableSet&&) = default; IViewableSet& operator=(IViewableSet&&) = default; virtual ~IViewableSet() = default; // endregion /** * \brief Represents an addition or removal of an element in the set. */ using Event = typename detail::SetEvent; /** * \brief Adds a subscription for additions and removals of set elements. When the subscription is initially * added, [handler] is called with [AddRemove::Add] events for all elements currently in the set. * * \param lifetime lifetime of subscription. * \param handler to be called. */ void advise(Lifetime lifetime, std::function handler) const { this->advise(lifetime, [handler](Event e) { handler(e.kind, *e.value); }); } /** * \brief Adds a subscription to changes of the contents of the set. * * \details When [handler] is initially added, it is called receiving all elements currently in the set. * Every time an object is added to the set, the [handler] is called receiving the new element. * The [Lifetime] instance passed to the handler expires when the element is removed from the set. * * \param lifetime * \param handler */ void view(Lifetime lifetime, std::function handler) const override { advise(lifetime, [this, lifetime, handler](AddRemove kind, T const& key) { switch (kind) { case AddRemove::ADD: { /*auto const &[it, inserted] = lifetimes[lifetime].emplace(key, LifetimeDefinition(lifetime));*/ auto const& it = lifetimes[lifetime].emplace(&key, lifetime); RD_ASSERT_MSG(it.second, "lifetime definition already exists in viewable set by key:" + to_string(key)); handler(it.first->second.lifetime, key); break; } case AddRemove::REMOVE: { RD_ASSERT_MSG(lifetimes.at(lifetime).count(key) > 0, "attempting to remove non-existing lifetime in viewable set by key:" + to_string(key)); LifetimeDefinition def = std::move(lifetimes.at(lifetime).at(key)); lifetimes.at(lifetime).erase(key); def.terminate(); break; } } }); } /** * \brief Adds a subscription for additions and removals of set elements. When the subscription is initially * added, [handler] is called with [AddRemove.Add] events for all elements currently in the set. * * \param lifetime lifetime of subscription. * \param handler to be called. */ void advise(Lifetime lifetime, std::function handler) const override = 0; virtual bool add(WT) const = 0; virtual bool addAll(std::vector elements) const = 0; virtual void clear() const = 0; virtual bool remove(T const&) const = 0; virtual size_t size() const = 0; virtual bool contains(T const&) const = 0; virtual bool empty() const = 0; template bool emplace_add(Args&&... args) const { return add(WT{std::forward(args)...}); } }; } // namespace rd static_assert( std::is_move_constructible::Event>::value, "Is move constructible from IViewableSet::Event"); #endif // RD_CPP_IVIEWABLESET_H ================================================ FILE: src/cpp/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/SignalCookie.cpp ================================================ #include "SignalCookie.h" #include namespace { std::atomic cookie; } void rd_signal_cookie_inc() { ++cookie; } void rd_signal_cookie_dec() { --cookie; } int32_t rd_signal_cookie_get() { return cookie; } ================================================ FILE: src/cpp/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/SignalCookie.h ================================================ #ifndef RD_CPP_SIGNALCOOKIE_H #define RD_CPP_SIGNALCOOKIE_H #include #include extern "C" void RD_CORE_API rd_signal_cookie_inc(); extern "C" void RD_CORE_API rd_signal_cookie_dec(); extern "C" int32_t RD_CORE_API rd_signal_cookie_get(); #endif // RD_CPP_SIGNALCOOKIE_H ================================================ FILE: src/cpp/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/SignalX.h ================================================ #ifndef RD_CPP_CORE_SIGNAL_H #define RD_CPP_CORE_SIGNAL_H #include "interfaces.h" #include "SignalCookie.h" #include #include #include #include #include namespace rd { /** * \brief complete class which has \a Signal 's properties */ template class Signal final : public ISignal { private: using WT = typename ISignal::WT; class Event { private: std::function action; Lifetime lifetime; public: // region ctor/dtor Event() = delete; template Event(F&& action, Lifetime lifetime) : action(std::forward(action)), lifetime(lifetime) { } Event(Event&&) = default; // endregion bool is_alive() const { return !lifetime->is_terminated(); } void execute_if_alive(T const& value) const { if (is_alive()) { action(value); } } }; using counter_t = int32_t; using listeners_t = std::map; mutable counter_t advise_id = 0; mutable listeners_t listeners, priority_listeners; static void cleanup(listeners_t& queue) { util::erase_if(queue, [](Event const& e) -> bool { return !e.is_alive(); }); } void fire_impl(T const& value, listeners_t& queue) const { for (auto const& p : queue) { auto const& event = p.second; event.execute_if_alive(value); } cleanup(queue); } template void advise0(const Lifetime& lifetime, F&& handler, listeners_t& queue) const { if (lifetime->is_terminated()) return; counter_t id = advise_id /*.load()*/; queue.emplace(id, Event(std::forward(handler), lifetime)); ++advise_id; } public: // region ctor/dtor Signal() = default; Signal(Signal const& other) = delete; Signal& operator=(Signal const& other) = delete; Signal(Signal&&) = default; Signal& operator=(Signal&&) = default; virtual ~Signal() = default; // endregion using ISignal::fire; void fire(T const& value) const override { fire_impl(value, priority_listeners); fire_impl(value, listeners); } using ISignal::advise; void advise(Lifetime lifetime, std::function handler) const override { advise0(lifetime, std::move(handler), isPriorityAdvise() ? priority_listeners : listeners); } static bool isPriorityAdvise() { return rd_signal_cookie_get() > 0; } }; template void priorityAdviseSection(F&& block) { rd_signal_cookie_inc(); block(); rd_signal_cookie_dec(); } } // namespace rd static_assert(std::is_move_constructible>::value, "Is not move constructible from Signal"); static_assert(std::is_move_constructible>::value, "Is not move constructible from Signal"); #endif // RD_CPP_CORE_SIGNAL_H ================================================ FILE: src/cpp/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/interfaces.h ================================================ #ifndef RD_CPP_CORE_INTERFACES_H #define RD_CPP_CORE_INTERFACES_H #include #include #include #include #include namespace rd { /** * \brief An object that allows to subscribe to events. * \tparam T type of events */ template class ISource { public: virtual ~ISource() = default; /** * \brief Adds an event subscription. * \param lifetime lifetime of subscription. * \param handler to be called, every time an event occurs. */ virtual void advise(Lifetime lifetime, std::function handler) const = 0; /** * \brief @code advise with Eternal lifetime */ template void advise_eternal(F&& handler) const { advise(Lifetime::Eternal(), std::forward(handler)); } /** * \brief @code Void specialisation of @code advise method, at @tparam T=Void */ void advise(Lifetime lifetime, std::function handler) const { advise(lifetime, [handler = std::move(handler)](Void) { handler(); }); } }; /** * \brief An object that allows to subscribe to changes of its contents. * \tparam T type of content */ template class IViewable { public: virtual ~IViewable() = default; virtual void view(Lifetime lifetime, std::function handler) const = 0; }; /** * \brief An object which has a collection of event listeners and can broadcast an event to the listeners. * \tparam T type of events */ template class ISignal : public ISource { protected: using WT = value_or_wrapper; public: virtual ~ISignal() = default; virtual void fire(T const& value) const = 0; /** * \brief @code fire specialisation at T=Void */ template typename std::enable_if_t> fire() const { fire(Void{}); } }; } // namespace rd #endif // RD_CPP_CORE_INTERFACES_H ================================================ FILE: src/cpp/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/viewable_collections.h ================================================ #ifndef RD_CPP_VIEWABLE_COLLECTIONS_H #define RD_CPP_VIEWABLE_COLLECTIONS_H #include namespace rd { enum class AddRemove { ADD, REMOVE }; inline std::string to_string(AddRemove kind) { switch (kind) { case AddRemove::ADD: return "Add"; case AddRemove::REMOVE: return "Remove"; default: return ""; } } enum class Op { ADD, UPDATE, REMOVE, ACK }; inline std::string to_string(Op op) { switch (op) { case Op::ADD: return "Add"; case Op::UPDATE: return "Update"; case Op::REMOVE: return "Remove"; case Op::ACK: return "Ack"; default: return ""; } } } // namespace rd #endif // RD_CPP_VIEWABLE_COLLECTIONS_H ================================================ FILE: src/cpp/RiderLink/Source/RD/src/rd_core_cpp/src/main/std/allocator.h ================================================ #ifndef RD_CPP_ALLOCATOR_H #define RD_CPP_ALLOCATOR_H #include namespace rd { template using allocator = std::allocator; } #endif // RD_CPP_ALLOCATOR_H ================================================ FILE: src/cpp/RiderLink/Source/RD/src/rd_core_cpp/src/main/std/hash.h ================================================ #ifndef RD_CPP_HASH_H #define RD_CPP_HASH_H #include #include namespace rd { template struct hash { size_t operator()(const T& value) const noexcept { return std::hash()(value); } }; } // namespace rd #endif // RD_CPP_HASH_H ================================================ FILE: src/cpp/RiderLink/Source/RD/src/rd_core_cpp/src/main/std/list.h ================================================ #ifndef RD_CPP_LIST_H #define RD_CPP_LIST_H #include #include namespace rd { template int32_t size(T const& value) = delete; // c++17 has std::size for std::vector #if __cplusplus < 201703L template int32_t size(std::vector const& value) { return static_cast(value.size()); } #else template int32_t size(std::vector const& value) { return std::size(value); } #endif template void resize(std::vector& value, int32_t size) { value.resize(size); } } // namespace rd #endif // RD_CPP_LIST_H ================================================ FILE: src/cpp/RiderLink/Source/RD/src/rd_core_cpp/src/main/std/to_string.h ================================================ // ReSharper disable CppUE4CodingStandardNamingViolationWarning #ifndef RD_CPP_TO_STRING_H #define RD_CPP_TO_STRING_H #include #include #include #include #include #include #include "ww898/utf_converters.hpp" #include namespace rd { namespace detail { using std::to_string; inline std::string to_string(std::string const& val) { return val; } inline std::string to_string(const char* val) { return val; } inline std::string to_string(std::wstring const& val) { return ww898::utf::conv(val); } inline std::string to_string(std::thread::id const& id) { std::ostringstream ss; ss << id; return ss.str(); } inline std::string to_string(std::exception const& e) { return std::string(e.what()); } inline std::string to_string(std::future_status const& status) { switch (status) { case std::future_status::ready: return "ready"; case std::future_status::timeout: return "timeout"; case std::future_status::deferred: return "deferred"; default: return "unknown"; } } template inline std::string to_string(std::chrono::duration const& time) { return std::to_string(std::chrono::duration_cast(time).count()) + "ms"; } template inline std::string to_string(T const* val) { return val ? to_string(*val) : "nullptr"; } template inline std::string to_string(std::atomic const& value) { return to_string(value.load()); } template inline std::string to_string(optional const& val) { if (val.has_value()) { return to_string(*val); } else { return "nullopt"; } } template inline std::string to_string(const std::pair p) { return "(" + to_string(p.first) + ", " + to_string(p.second) + ")"; } template