From cc486f73279695b4f55217259dc3673dc4d894e9 Mon Sep 17 00:00:00 2001 From: James Thistlewood Date: Sat, 20 Mar 2021 01:13:03 +0000 Subject: [PATCH 01/14] initial linux port --- .gitignore | 7 +- linux/plugins/Makefile | 23 + linux/plugins/README.md | 19 + linux/plugins/install.sh | 4 + linux/plugins/read_util.cpp | 63 ++ linux/plugins/telemetry_mem.cpp | 268 +++++++ linux/sdk/include/amtrucks/scssdk_ats.h | 22 + .../include/amtrucks/scssdk_telemetry_ats.h | 73 ++ .../common/scssdk_telemetry_common_channels.h | 52 ++ .../common/scssdk_telemetry_common_configs.h | 654 ++++++++++++++++ .../scssdk_telemetry_common_gameplay_events.h | 203 +++++ .../scssdk_telemetry_job_common_channels.h | 24 + ...scssdk_telemetry_trailer_common_channels.h | 79 ++ .../scssdk_telemetry_truck_common_channels.h | 720 ++++++++++++++++++ linux/sdk/include/eurotrucks2/scssdk_eut2.h | 22 + .../eurotrucks2/scssdk_telemetry_eut2.h | 103 +++ linux/sdk/include/scssdk.h | 202 +++++ linux/sdk/include/scssdk_telemetry.h | 107 +++ linux/sdk/include/scssdk_telemetry_channel.h | 104 +++ linux/sdk/include/scssdk_telemetry_event.h | 239 ++++++ linux/sdk/include/scssdk_value.h | 247 ++++++ linux/sdk/readme.txt | 56 ++ linux/sdk/sdk_license.txt | 20 + linux/server/main.py | 88 +++ 24 files changed, 3398 insertions(+), 1 deletion(-) create mode 100644 linux/plugins/Makefile create mode 100644 linux/plugins/README.md create mode 100755 linux/plugins/install.sh create mode 100644 linux/plugins/read_util.cpp create mode 100644 linux/plugins/telemetry_mem.cpp create mode 100644 linux/sdk/include/amtrucks/scssdk_ats.h create mode 100644 linux/sdk/include/amtrucks/scssdk_telemetry_ats.h create mode 100644 linux/sdk/include/common/scssdk_telemetry_common_channels.h create mode 100644 linux/sdk/include/common/scssdk_telemetry_common_configs.h create mode 100644 linux/sdk/include/common/scssdk_telemetry_common_gameplay_events.h create mode 100644 linux/sdk/include/common/scssdk_telemetry_job_common_channels.h create mode 100644 linux/sdk/include/common/scssdk_telemetry_trailer_common_channels.h create mode 100644 linux/sdk/include/common/scssdk_telemetry_truck_common_channels.h create mode 100644 linux/sdk/include/eurotrucks2/scssdk_eut2.h create mode 100644 linux/sdk/include/eurotrucks2/scssdk_telemetry_eut2.h create mode 100644 linux/sdk/include/scssdk.h create mode 100644 linux/sdk/include/scssdk_telemetry.h create mode 100644 linux/sdk/include/scssdk_telemetry_channel.h create mode 100644 linux/sdk/include/scssdk_telemetry_event.h create mode 100644 linux/sdk/include/scssdk_value.h create mode 100644 linux/sdk/readme.txt create mode 100644 linux/sdk/sdk_license.txt create mode 100644 linux/server/main.py diff --git a/.gitignore b/.gitignore index 95fbdce9c..cff458908 100644 --- a/.gitignore +++ b/.gitignore @@ -42,4 +42,9 @@ crashlytics-build.properties fabric.properties server/packages/** -*.exe \ No newline at end of file +*.exe + +# Linux stuff +linux/plugins/telemetry.so +linux/plugins/read_util +linux/server/read_util diff --git a/linux/plugins/Makefile b/linux/plugins/Makefile new file mode 100644 index 000000000..4a1a6d2c9 --- /dev/null +++ b/linux/plugins/Makefile @@ -0,0 +1,23 @@ +SDK_HEADERS=\ + ../sdk/include/*.h \ + ../sdk/include/common/*.h \ + ../sdk/include/amtrucks/*.h \ + ../sdk/include/eurotrucks2/*.h + +SDK_INCLUDES=\ + -I../sdk/include \ + -I../sdk/include/common/ \ + -I../sdk/include/amtrucks/ \ + -I../sdk/include/eurotrucks2 + +all: telemetry.so read_util + +telemetry.so: telemetry_mem.cpp $(SDK_HEADERS) + g++ -o $@ -fPIC -Wall --shared -Wl,-soname,$@ $(SDK_INCLUDES) telemetry_mem.cpp -lrt + +read_util: read_util.cpp $(SDK_HEADERS) + g++ -o $@ -Wall $(SDK_INCLUDES) read_util.cpp -lrt + +.PHONY: clean +clean: + @rm -f -- *.so read_util diff --git a/linux/plugins/README.md b/linux/plugins/README.md new file mode 100644 index 000000000..b0c4c1789 --- /dev/null +++ b/linux/plugins/README.md @@ -0,0 +1,19 @@ +# ETS2 Local Radio unofficial Linux port + +## Installation instructions + +In this directory, run the command: + +```bash +make all && ./install.sh +``` + +You will need to copy the plugin `telemetry.so` manually to your plugins directory if ETS is not installed via Steam on your main partition, or if you are using ATS. + +## Running + +Go to the `server` directory and run: + +```bash +python3 main.py +``` diff --git a/linux/plugins/install.sh b/linux/plugins/install.sh new file mode 100755 index 000000000..a090480fa --- /dev/null +++ b/linux/plugins/install.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +cp read_util "../server" +cp telemetry.so "$HOME/.local/share/Steam/steamapps/common/Euro Truck Simulator 2/bin/linux_x64/plugins" diff --git a/linux/plugins/read_util.cpp b/linux/plugins/read_util.cpp new file mode 100644 index 000000000..cad97dd1b --- /dev/null +++ b/linux/plugins/read_util.cpp @@ -0,0 +1,63 @@ +/** + * @brief Utility that will output the value of the position in the shared memory. + */ + +#include +#include +#include +#include +#include +#include + +// SDK + +#include "scssdk_telemetry.h" +#include "eurotrucks2/scssdk_eut2.h" +#include "eurotrucks2/scssdk_telemetry_eut2.h" +#include "amtrucks/scssdk_ats.h" +#include "amtrucks/scssdk_telemetry_ats.h" + + +static char shm_name[] = "/ets2radiolinux"; + +void print_position(scs_value_dplacement_t placement) { + scs_value_dvector_t pos = placement.position; + fprintf(stdout, "%f,%f,%f\n", pos.x, pos.y, pos.z); + fflush(stdout); +} + +int main() { + // We only need one page of memory + size_t MAP_SIZE = getpagesize(); + + // Open shared memory + int handle = shm_open(shm_name, O_RDONLY, S_IRUSR); + if (handle < 0) { + fprintf(stderr, "ERR Could not open shared memory\n"); + return 1; + } + + void* mapped_region = mmap( + 0, + MAP_SIZE, + PROT_READ, + MAP_SHARED, + handle, + 0 + ); + + if (mapped_region == MAP_FAILED) { + fprintf(stderr, "ERR Could not mmap shared memory\n"); + return 1; + } + + scs_value_dplacement_t current; + memset(¤t, 0, sizeof(scs_value_dplacement_t)); + while (1) { + memcpy(¤t, mapped_region, MAP_SIZE); + print_position(current); + usleep(500000); + } + + return 0; +} \ No newline at end of file diff --git a/linux/plugins/telemetry_mem.cpp b/linux/plugins/telemetry_mem.cpp new file mode 100644 index 000000000..92b9473a7 --- /dev/null +++ b/linux/plugins/telemetry_mem.cpp @@ -0,0 +1,268 @@ +/** + * @brief TODO + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +// SDK + +#include "scssdk_telemetry.h" +#include "eurotrucks2/scssdk_eut2.h" +#include "eurotrucks2/scssdk_telemetry_eut2.h" +#include "amtrucks/scssdk_ats.h" +#include "amtrucks/scssdk_telemetry_ats.h" + +#define UNUSED(x) + +/** + * @brief Tracking of paused state of the game. + */ +bool output_paused = true; + +/** + * @brief Useful telemetry data. + */ +struct telemetry_state_t +{ + scs_value_dplacement_t world_position; +} telemetry; + +/** + * @brief Time since last mmap + */ +scs_timestamp_t elapsed = 0; +scs_timestamp_t last_timestamp = -1; +static scs_timestamp_t update_interval = 5e5; // every 0.5s + +/** + * @brief Opaque pointer to use for mmap + */ +void* mapped_region = NULL; +size_t MAP_SIZE = -1; + +static char shm_name[] = "/ets2radiolinux"; + +/** + * @brief Function writing message to the game internal log. + */ +scs_log_t game_log = NULL; + +// Handling of individual events. + +SCSAPI_VOID telemetry_frame_start(const scs_event_t UNUSED(event), const void *const event_info, const scs_context_t UNUSED(context)) +{ + const struct scs_telemetry_frame_start_t* const info = static_cast(event_info); + + if (last_timestamp == static_cast(-1)) { + last_timestamp = 0; + } + + // The timer might be sometimes restarted (e.g. after load) while + // we want to provide continuous time on our output. + if (info->flags & SCS_TELEMETRY_FRAME_START_FLAG_timer_restart) { + last_timestamp = 0; + } + + elapsed += info->paused_simulation_time - last_timestamp; + last_timestamp = info->paused_simulation_time; +} + +SCSAPI_VOID telemetry_frame_end(const scs_event_t UNUSED(event), const void *const UNUSED(event_info), const scs_context_t UNUSED(context)) +{ + if (output_paused) { + return; + } + + // We only copy once per interval, to prevent loads of unneccessary writes + if (elapsed < update_interval) { + return; + } + + elapsed -= update_interval; + + // Copy telemetry data, after zeroing dest + memset(mapped_region, 0, MAP_SIZE); + memcpy(mapped_region, &telemetry, sizeof(telemetry)); +} + +SCSAPI_VOID telemetry_pause(const scs_event_t event, const void *const UNUSED(event_info), const scs_context_t UNUSED(context)) +{ + output_paused = (event == SCS_TELEMETRY_EVENT_paused); +} + +// Handling of individual channels. + +SCSAPI_VOID telemetry_store_dplacement(const scs_string_t name, const scs_u32_t index, const scs_value_t *const value, const scs_context_t context) +{ + if (!context) { + game_log(SCS_LOG_TYPE_error, "Local radio: no context!"); + return; + } + if (!value) { + game_log(SCS_LOG_TYPE_error, "Local radio: no value!"); + return; + } + if (value->type != SCS_VALUE_TYPE_dplacement) { + game_log(SCS_LOG_TYPE_error, "Local radio: value wrong type!"); + return; + } + + scs_value_dplacement_t *const placement = static_cast(context); + *placement = value->value_dplacement; +} + +/** + * @brief Telemetry API initialization function. + * + * ENTRY POINT. + * + * See scssdk_telemetry.h + */ +SCSAPI_RESULT scs_telemetry_init(const scs_u32_t version, const scs_telemetry_init_params_t *const params) +{ + // We currently support only one version. + + if (version != SCS_TELEMETRY_VERSION_CURRENT) { + return SCS_RESULT_unsupported; + } + + const scs_telemetry_init_params_v101_t *const version_params = static_cast(params); + + // Remember the function we will use for logging. + game_log = version_params->common.log; + game_log(SCS_LOG_TYPE_message, "Local radio: begin initialization"); + + // Check application version. Note that this example uses fairly basic channels which are likely to be supported + // by any future SCS trucking game however more advanced application might want to at least warn the user if there + // is game or version they do not support. + if (strcmp(version_params->common.game_id, SCS_GAME_ID_EUT2) == 0) { + + // Below the minimum version there might be some missing features (only minor change) or + // incompatible values (major change). + + const scs_u32_t MINIMAL_VERSION = SCS_TELEMETRY_EUT2_GAME_VERSION_1_00; + if (version_params->common.game_version < MINIMAL_VERSION) { + // Might behave incorrectly + } + + // Future versions are fine as long the major version is not changed. + + const scs_u32_t IMPLEMENTED_VERSION = SCS_TELEMETRY_EUT2_GAME_VERSION_CURRENT; + if (SCS_GET_MAJOR_VERSION(version_params->common.game_version) > SCS_GET_MAJOR_VERSION(IMPLEMENTED_VERSION)) { + // Might behave incorrectly + } + } + else if (strcmp(version_params->common.game_id, SCS_GAME_ID_ATS) == 0) { + + // Below the minimum version there might be some missing features (only minor change) or + // incompatible values (major change). + + const scs_u32_t MINIMAL_VERSION = SCS_TELEMETRY_ATS_GAME_VERSION_1_00; + if (version_params->common.game_version < MINIMAL_VERSION) { + // Might behave incorrectly + } + + // Future versions are fine as long the major version is not changed. + + const scs_u32_t IMPLEMENTED_VERSION = SCS_TELEMETRY_ATS_GAME_VERSION_CURRENT; + if (SCS_GET_MAJOR_VERSION(version_params->common.game_version) > SCS_GET_MAJOR_VERSION(IMPLEMENTED_VERSION)) { + // Might behave incorrectly + } + } + else { + // Unsupported game + } + + // Register for events. Note that failure to register those basic events + // likely indicates invalid usage of the api or some critical problem. As the + // example requires all of them, we can not continue if the registration fails. + + const bool events_registered = + (version_params->register_for_event(SCS_TELEMETRY_EVENT_frame_start, telemetry_frame_start, NULL) == SCS_RESULT_ok) && + (version_params->register_for_event(SCS_TELEMETRY_EVENT_frame_end, telemetry_frame_end, NULL) == SCS_RESULT_ok) && + (version_params->register_for_event(SCS_TELEMETRY_EVENT_paused, telemetry_pause, NULL) == SCS_RESULT_ok) && + (version_params->register_for_event(SCS_TELEMETRY_EVENT_started, telemetry_pause, NULL) == SCS_RESULT_ok) + ; + if (! events_registered) { + + // Registrations created by unsuccessfull initialization are + // cleared automatically so we can simply exit. + + version_params->common.log(SCS_LOG_TYPE_error, "Local radio: unable to register event callbacks"); + return SCS_RESULT_generic_error; + } + + // Register for channels. The channel might be missing if the game does not support + // it (SCS_RESULT_not_found) or if does not support the requested type + // (SCS_RESULT_unsupported_type). For purpose of this example we ignore the failues + // so the unsupported channels will remain at theirs default value. + version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_world_placement, SCS_U32_NIL, SCS_VALUE_TYPE_dplacement, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_dplacement, &telemetry.world_position); + + // Set the structure with defaults. + memset(&telemetry, 0, sizeof(telemetry)); + + // We only need one page of memory + MAP_SIZE = getpagesize(); + + // Open shared memory + int handle = shm_open(shm_name, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (handle < 0) { + game_log(SCS_LOG_TYPE_error, "Local radio: could not open shared memory"); + return SCS_RESULT_generic_error; + } + + if (ftruncate(handle, MAP_SIZE) < 0) { + game_log(SCS_LOG_TYPE_error, "Local radio: could not truncate shared memery"); + return SCS_RESULT_generic_error; + } + + mapped_region = mmap( + 0, + MAP_SIZE, + PROT_READ | PROT_WRITE, + MAP_SHARED, + handle, + 0 + ); + + if (mapped_region == MAP_FAILED) { + game_log(SCS_LOG_TYPE_error, "Local radio: could not mmap shared memory"); + return SCS_RESULT_generic_error; + } + + game_log(SCS_LOG_TYPE_message, "Local radio: initialized successfully"); + + // Initially the game is paused. + output_paused = true; + return SCS_RESULT_ok; +} + +/** + * @brief Telemetry API deinitialization function. + * + * See scssdk_telemetry.h + */ +SCSAPI_VOID scs_telemetry_shutdown(void) +{ + // Any cleanup needed. The registrations will be removed automatically + // so there is no need to do that manually. + game_log(SCS_LOG_TYPE_message, "Local radio: shutdown"); + + game_log = NULL; + shm_unlink(shm_name); + mapped_region = NULL; +} + +// Cleanup + +void __attribute__ ((destructor)) unload(void) +{ + scs_telemetry_shutdown(); +} diff --git a/linux/sdk/include/amtrucks/scssdk_ats.h b/linux/sdk/include/amtrucks/scssdk_ats.h new file mode 100644 index 000000000..e6461edf1 --- /dev/null +++ b/linux/sdk/include/amtrucks/scssdk_ats.h @@ -0,0 +1,22 @@ +/** + * @file scssdk_ats.h + * + * @brief ATS specific constants. + */ +#ifndef SCSSDK_ATS_H +#define SCSSDK_ATS_H + +#include "../scssdk.h" + +SCSSDK_HEADER + +/** + * @brief Value used in the scs_sdk_init_params_t::game_id to identify this game. + */ +#define SCS_GAME_ID_ATS "ats" + +SCSSDK_FOOTER + +#endif // SCSSDK_ATS_H + +/* eof */ diff --git a/linux/sdk/include/amtrucks/scssdk_telemetry_ats.h b/linux/sdk/include/amtrucks/scssdk_telemetry_ats.h new file mode 100644 index 000000000..bc145bc1c --- /dev/null +++ b/linux/sdk/include/amtrucks/scssdk_telemetry_ats.h @@ -0,0 +1,73 @@ +/** + * @file scssdk_telemetry_ats.h + * + * @brief ATS telemetry specific constants. + */ +#ifndef SCSSDK_TELEMETRY_ATS_H +#define SCSSDK_TELEMETRY_ATS_H + +#include "../scssdk.h" +#include "../common/scssdk_telemetry_common_configs.h" +#include "../common/scssdk_telemetry_common_channels.h" +#include "../common/scssdk_telemetry_truck_common_channels.h" +#include "../common/scssdk_telemetry_trailer_common_channels.h" +#include "../common/scssdk_telemetry_job_common_channels.h" + +SCSSDK_HEADER + +/** + * @name Value used in the scs_sdk_init_params_t::game_version + * + * Changes in the major version indicate incompatible changes (e.g. changed interpretation + * of the channel value). Change of major version is highly discouraged, creation of + * alternative channel is preferred solution if necessary. + * Changes in the minor version indicate compatible changes (e.g. added channel, more supported + * value types). Removal of channel is also compatible change however it is recommended + * to keep the channel with some default value. + * + * Changes: + * 1.00 - initial version - corresponds to 1.12 in ETS2 + * 1.01 - added support for multiple trailers (doubles, triples), trailer ownership support, + * gameplay events support added + * 1.02 - added planned_distance_km to active job info + * 1.03 - added support for 'avoid_inspection', 'illegal_border_crossing' and 'hard_shoulder_violation' offence type in 'player.fined' gameplay event + */ +//@{ +#define SCS_TELEMETRY_ATS_GAME_VERSION_1_00 SCS_MAKE_VERSION(1, 0) +#define SCS_TELEMETRY_ATS_GAME_VERSION_1_01 SCS_MAKE_VERSION(1, 1) +#define SCS_TELEMETRY_ATS_GAME_VERSION_1_02 SCS_MAKE_VERSION(1, 2) // Patch 1.36 +#define SCS_TELEMETRY_ATS_GAME_VERSION_1_03 SCS_MAKE_VERSION(1, 3) // Patch 1.36 +#define SCS_TELEMETRY_ATS_GAME_VERSION_CURRENT SCS_TELEMETRY_ATS_GAME_VERSION_1_03 +//@} + +// Game specific units. +// +// @li The game uses US Dolars as internal currency provided +// by the telemetry unless documented otherwise. + +// Channels defined in scssdk_telemetry_common_channels.h, +// scssdk_telemetry_job_common_channels.h, +// scssdk_telemetry_truck_common_channels.h and +// scssdk_telemetry_trailer_common_channels.h are supported +// with following exceptions and limitations as of v1.00: +// +// @li Adblue related channels are not supported. +// @li The fuel_average_consumption is currently mostly static and depends +// on presence of the trailer and skills of the driver instead +// of the workload of the engine. +// @li Rolling rotation of trailer wheels is determined from linear +// movement. +// @li The pressures, temperatures and voltages are not simulated. +// They are very loosely approximated. + +// Configurations defined in scssdk_telemetry_common_configs.h are +// supported with following exceptions and limitations as of v1.00: +// +// @li The localized strings are not updated when different in-game +// language is selected. + +SCSSDK_FOOTER + +#endif // SCSSDK_TELEMETRY_ATS_H + +/* eof */ diff --git a/linux/sdk/include/common/scssdk_telemetry_common_channels.h b/linux/sdk/include/common/scssdk_telemetry_common_channels.h new file mode 100644 index 000000000..90b4a61a7 --- /dev/null +++ b/linux/sdk/include/common/scssdk_telemetry_common_channels.h @@ -0,0 +1,52 @@ +/** + * @file scssdk_telemetry_common_channels.h + * + * @brief Telemetry specific channels which might be used by more than one game. + */ +#ifndef SCSSDK_TELEMETRY_COMMON_CHANNELS_H +#define SCSSDK_TELEMETRY_COMMON_CHANNELS_H + +#include "../scssdk.h" + +SCSSDK_HEADER + +/** + * @brief Scale applied to distance and time to compensate + * for the scale of the map (e.g. 1s of real time corresponds to local_scale + * seconds of simulated game time). + * + * Games which use real 1:1 maps will not provide this + * channel. + * + * Type: float + */ +#define SCS_TELEMETRY_CHANNEL_local_scale "local.scale" + +/** + * @brief Absolute in-game time. + * + * Represented in number of in-game minutes since beginning (i.e. 00:00) + * of the first in-game day. + * + * Type: u32 + */ +#define SCS_TELEMETRY_CHANNEL_game_time "game.time" + +/** + * @brief Time until next rest stop. + * + * When the fatique simulation is disabled, the behavior of this channel + * is implementation dependent. The game might provide the value which would + * apply if it was enabled or provide no value at all. + * + * Represented in in-game minutes. + * + * Type: s32 + */ +#define SCS_TELEMETRY_CHANNEL_next_rest_stop "rest.stop" + +SCSSDK_FOOTER + +#endif // SCSSDK_TELEMETRY_COMMON_CHANNELS_H + +/* eof */ diff --git a/linux/sdk/include/common/scssdk_telemetry_common_configs.h b/linux/sdk/include/common/scssdk_telemetry_common_configs.h new file mode 100644 index 000000000..89657525f --- /dev/null +++ b/linux/sdk/include/common/scssdk_telemetry_common_configs.h @@ -0,0 +1,654 @@ +/** + * @file scssdk_telemetry_common_configs.h + * + * @brief Telemetry specific constants for configs. + * + * This file defines truck specific telemetry constants which + * might be used by more than one SCS game. See game-specific + * file to determine which constants are supported by specific + * game. + */ +#ifndef SCSSDK_TELEMETRY_COMMON_CONFIGS_H +#define SCSSDK_TELEMETRY_COMMON_CONFIGS_H + +#include "../scssdk.h" + +SCSSDK_HEADER + +/** + * @brief The count of the trailers supported by SDK. + * + * The maximum number of trailers that can be returned by the telemetry SDK. + */ +#define SCS_TELEMETRY_trailers_count 10 + +/** + * @brief Configuration of the substances. + * + * Attribute index is index of the substance. + * + * Supported attributes: + * @li id + * TODO: Whatever additional info necessary. + */ +#define SCS_TELEMETRY_CONFIG_substances "substances" + +/** + * @brief Static configuration of the controls. + * + * @li shifter_type + */ +#define SCS_TELEMETRY_CONFIG_controls "controls" + +/** + * @brief Configuration of the h-shifter. + * + * When evaluating the selected gear, find slot which matches + * the handle position and bitmask of on/off state of selectors. + * If one is found, it contains the resulting gear. Otherwise + * a neutral is assumed. + * + * Supported attributes: + * @li selector_count + * @li resulting gear index for each slot + * @li handle position index for each slot + * @li bitmask of selectors for each slot + */ +#define SCS_TELEMETRY_CONFIG_hshifter "hshifter" + +/** + * @brief Static configuration of the truck. + * + * If empty set of attributes is returned, there is no configured truck. + * + * Supported attributes: + * @li brand_id + * @li brand + * @li id + * @li name + * @li fuel_capacity + * @li fuel_warning_factor + * @li adblue_capacity + * @li ablue_warning_factor + * @li air_pressure_warning + * @li air_pressure_emergency + * @li oil_pressure_warning + * @li water_temperature_warning + * @li battery_voltage_warning + * @li rpm_limit + * @li foward_gear_count + * @li reverse_gear_count + * @li retarder_step_count + * @li cabin_position + * @li head_position + * @li hook_position + * @li license_plate + * @li license_plate_country + * @li license_plate_country_id + * @li wheel_count + * @li wheel positions for wheel_count wheels + */ +#define SCS_TELEMETRY_CONFIG_truck "truck" + +/** + * @brief Backward compatibility static configuration of the first trailer (attributes are equal to trailer.0). + * + * The trailers configurations are returned using trailer.[index] + * (e.g. trailer.0, trailer.1, ... trailer.9 ...) + * + * SDK currently can return up to @c SCS_TELEMETRY_trailers_count trailers. + * + * If there are less trailers in game than @c SCS_TELEMETRY_trailers_count + * telemetry will return all configurations however starting from the trailer after last + * existing one its attributes will be empty. + * + * Supported attributes: + * @li id + * @li cargo_accessory_id + * @li hook_position + * @li brand_id + * @li brand + * @li name + * @li chain_type (reported only for first trailer) + * @li body_type (reported only for first trailer) + * @li license_plate + * @li license_plate_country + * @li license_plate_country_id + * @li wheel_count + * @li wheel offsets for wheel_count wheels + */ +#define SCS_TELEMETRY_CONFIG_trailer "trailer" + +/** + * @brief Static configuration of the job. + * + * If empty set of attributes is returned, there is no job. + * + * Supported attributes: + * @li cargo_id + * @li cargo + * @li cargo_mass + * @li destination_city_id + * @li destination_city + * @li source_city_id + * @li source_city + * @li destination_company_id (only available for non special transport jobs) + * @li destination_company (only available for non special transport jobs) + * @li source_company_id (only available for non special transport jobs) + * @li source_company (only available for non special transport jobs) + * @li income - represents expected income for the job without any penalties + * @li delivery_time + * @li is_cargo_loaded + * @li job_market + * @li special_job + * @li planned_distance_km + */ +#define SCS_TELEMETRY_CONFIG_job "job" + + // Attributes + + /** + * @brief Brand id for configuration purposes. + * + * Limited to C-identifier characters. + * + * Type: string + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_brand_id "brand_id" + + /** + * @brief Brand for display purposes. + * + * Localized using the current in-game language. + * + * Type: string + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_brand "brand" + +/** + * @brief Name for internal use by code. + * + * Limited to C-identifier characters and dots. + * + * Type: string + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_id "id" + +/** + * @brief Name of cargo accessory for internal use by code. + * + * Limited to C-identifier characters and dots. + * + * Type: string + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_cargo_accessory_id "cargo.accessory.id" + +/** + * @brief Name of trailer chain type. + * + * Limited to C-identifier characters and dots. + * + * Type: string + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_chain_type "chain.type" + +/** + * @brief Name of trailer body type. + * + * Limited to C-identifier characters and dots. + * + * Type: string + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_body_type "body.type" + +/** + * @brief Vehicle license plate. + * + * Type: string + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_license_plate "license.plate" + +/** + * @brief The id representing license plate country. + * + * Type: string + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_license_plate_country_id "license.plate.country.id" + +/** + * @brief The name of the license plate country. + * + * Type: string + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_license_plate_country "license.plate.country" + +/** + * @brief Name for display purposes. + * + * Localized using the current in-game language. + * + * Type: string + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_name "name" + +/** + * @brief Fuel tank capacity in litres. + * + * Type: float + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_fuel_capacity "fuel.capacity" + +/** + * @brief Fraction of the fuel capacity below which + * is activated the fuel warning. + * + * Type: float + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_fuel_warning_factor "fuel.warning.factor" + +/** + * @brief AdBlue tank capacity in litres. + * + * Type: float + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_adblue_capacity "adblue.capacity" + +/** + * @brief Fraction of the adblue capacity below which + * is activated the adblue warning. + * + * Type: float + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_adblue_warning_factor "adblue.warning.factor" + +/** + * @brief Pressure of the air in the tank below which + * the warning activates. + * + * Type: float + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_air_pressure_warning "brake.air.pressure.warning" + +/** + * @brief Pressure of the air in the tank below which + * the emergency brakes activate. + * + * Type: float + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_air_pressure_emergency "brake.air.pressure.emergency" + +/** + * @brief Pressure of the oil below which the warning activates. + * + * Type: float + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_oil_pressure_warning "oil.pressure.warning" + +/** + * @brief Temperature of the water above which the warning activates. + * + * Type: float + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_water_temperature_warning "water.temperature.warning" + +/** + * @brief Voltage of the battery below which the warning activates. + * + * Type: float + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_battery_voltage_warning "battery.voltage.warning" + +/** + * @brief Maximum rpm value. + * + * Type: float + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_rpm_limit "rpm.limit" + +/** + * @brief Number of forward gears on undamaged truck. + * + * Type: u32 + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_forward_gear_count "gears.forward" + +/** + * @brief Number of reversee gears on undamaged truck. + * + * Type: u32 + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_reverse_gear_count "gears.reverse" + +/** + * @brief Differential ratio of the truck. + * + * Type: float + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_differential_ratio "differential.ratio" + +/** + * @brief Number of steps in the retarder. + * + * Set to zero if retarder is not mounted to the truck. + * + * Type: u32 + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_retarder_step_count "retarder.steps" + +/** + * @brief Forward transmission ratios. + * + * Type: indexed float + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_forward_ratio "forward.ratio" + +/** + * @brief Reverse transmission ratios. + * + * Type: indexed float + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_reverse_ratio "reverse.ratio" + +/** + * @brief Position of the cabin in the vehicle space. + * + * This is position of the joint around which the cabin rotates. + * This attribute might be not present if the vehicle does not + * have a separate cabin. + * + * Type: fvector + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_cabin_position "cabin.position" + +/** + * @brief Default position of the head in the cabin space. + * + * Type: fvector + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_head_position "head.position" + +/** + * @brief Position of the trailer connection hook in vehicle + * space. + * + * Type: fvector + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_hook_position "hook.position" + +/** + * @brief Number of wheels + * + * Type: u32 + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_wheel_count "wheels.count" + +/** + * @brief Position of respective wheels in the vehicle space. + * + * Type: indexed fvector + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_wheel_position "wheel.position" + +/** + * @brief Is the wheel steerable? + * + * Type: indexed bool + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_wheel_steerable "wheel.steerable" + +/** + * @brief Is the wheel physicaly simulated? + * + * Type: indexed bool + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_wheel_simulated "wheel.simulated" + +/** + * @brief Radius of the wheel + * + * Type: indexed float + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_wheel_radius "wheel.radius" + +/** + * @brief Is the wheel powered? + * + * Type: indexed bool + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_wheel_powered "wheel.powered" + +/** + * @brief Is the wheel liftable? + * + * Type: indexed bool + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_wheel_liftable "wheel.liftable" + +/** + * @brief Number of selectors (e.g. range/splitter toggles). + * + * Type: u32 + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_selector_count "selector.count" + +/** + * @brief Gear selected when requirements for this h-shifter slot are meet. + * + * Type: indexed s32 + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_slot_gear "slot.gear" + +/** + * @brief Position of h-shifter handle. + * + * Zero corresponds to neutral position. Mapping to physical position of + * the handle depends on input setup. + * + * Type: indexed u32 + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_slot_handle_position "slot.handle.position" + +/** + * @brief Bitmask of required on/off state of selectors. + * + * Only first selector_count bits are relevant. + * + * Type: indexed u32 + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_slot_selectors "slot.selectors" + +/** + * @brief Type of the shifter. + * + * One from SCS_SHIFTER_TYPE_* values. + * + * Type: string + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_shifter_type "shifter.type" + +#define SCS_SHIFTER_TYPE_arcade "arcade" +#define SCS_SHIFTER_TYPE_automatic "automatic" +#define SCS_SHIFTER_TYPE_manual "manual" +#define SCS_SHIFTER_TYPE_hshifter "hshifter" + + // Attributes + + /** + * @brief Id of the cargo for internal use by code. + * + * Limited to C-identifier characters and dots. + * + * Type: string + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_cargo_id "cargo.id" + +/** + * @brief Name of the cargo for display purposes. + * + * Localized using the current in-game language. + * + * Type: string + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_cargo "cargo" + +/** + * @brief Mass of the cargo in kilograms. + * + * Type: float + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_cargo_mass "cargo.mass" + +/** + * @brief Mass of the single unit of the cargo in kilograms. + * + * Type: float + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_cargo_unit_mass "cargo.unit.mass" + +/** + * @brief How many units of the cargo the job consist of. + * + * Type: u32 + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_cargo_unit_count "cargo.unit.count" + + +/** + * @brief Id of the destination city for internal use by code. + * + * Limited to C-identifier characters and dots. + * + * Type: string + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_destination_city_id "destination.city.id" + +/** + * @brief Name of the destination city for display purposes. + * + * Localized using the current in-game language. + * + * Type: string + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_destination_city "destination.city" + +/** + * @brief Id of the destination company for internal use by code. + * + * Limited to C-identifier characters and dots. + * + * Type: string + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_destination_company_id "destination.company.id" + +/** + * @brief Name of the destination company for display purposes. + * + * Localized using the current in-game language. + * + * Type: string + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_destination_company "destination.company" + +/** + * @brief Id of the source city for internal use by code. + * + * Limited to C-identifier characters and dots. + * + * Type: string + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_source_city_id "source.city.id" + +/** + * @brief Name of the source city for display purposes. + * + * Localized using the current in-game language. + * + * Type: string + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_source_city "source.city" + +/** + * @brief Id of the source company for internal use by code. + * + * Limited to C-identifier characters and dots. + * + * Type: string + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_source_company_id "source.company.id" + +/** + * @brief Name of the source company for display purposes. + * + * Localized using the current in-game language. + * + * Type: string + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_source_company "source.company" + +/** + * @brief Reward in internal game-specific currency. + * + * For detailed information about the currency see "Game specific units" + * documentation in scssdk_telemetry_.h + * + * Type: u64 + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_income "income" + +/** + * @brief Absolute in-game time of end of job delivery window. + * + * Delivering the job after this time will cause it be late. + * + * See SCS_TELEMETRY_CHANNEL_game_time for more info about absolute time. + * Time remaining for delivery can be obtained like (delivery_time - game_time). + * + * Type: u32 + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_delivery_time "delivery.time" + +/** + * @brief Planned job distance in simulated kilometers. + * + * Does not include distance driven using ferry. + * + * Type: u32 + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_planned_distance_km "planned_distance.km" + +/** + * @brief Is cargo loaded on the trailer? + * + * For non cargo market jobs this is always true + * + * Type: bool + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_is_cargo_loaded "cargo.loaded" + +/** + * @brief The job market this job is from. + * + * The value is a string representing the type of the job market. + * Possible values: + * @li cargo_market + * @li quick_job + * @li freight_market + * @li external_contracts + * @li external_market + * + * Type: string + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_job_market "job.market" + +/** + * @brief Flag indicating that the job is special transport job. + * + * Type: bool + */ +#define SCS_TELEMETRY_CONFIG_ATTRIBUTE_special_job "is.special.job" + +SCSSDK_FOOTER + +#endif // SCSSDK_TELEMETRY_COMMON_CONFIGS_H + +/* eof */ diff --git a/linux/sdk/include/common/scssdk_telemetry_common_gameplay_events.h b/linux/sdk/include/common/scssdk_telemetry_common_gameplay_events.h new file mode 100644 index 000000000..9d6f018af --- /dev/null +++ b/linux/sdk/include/common/scssdk_telemetry_common_gameplay_events.h @@ -0,0 +1,203 @@ +/** + * @file scssdk_telemetry_common_gameplay_events.h + * + * @brief Telemetry specific gameplay events which might be used by more than one game. + */ +#ifndef SCSSDK_TELEMETRY_COMMON_GAMEPLAY_EVENTS_H +#define SCSSDK_TELEMETRY_COMMON_GAMEPLAY_EVENTS_H + +#include "../scssdk.h" + +SCSSDK_HEADER + +/** + * @brief Event called when job is cancelled. + * + * Attributes: + * @li cancel_penalty + */ +#define SCS_TELEMETRY_GAMEPLAY_EVENT_job_cancelled "job.cancelled" + +/** + * @brief Event called when job is delivered. + * + * Attributes: + * @li revenue + * @li earned_xp + * @li cargo_damage + * @li distance_km + * @li delivery_time + * @li autopark_used + * @li autoload_used + */ +#define SCS_TELEMETRY_GAMEPLAY_EVENT_job_delivered "job.delivered" + +/** + * @brief Event called when player gets fined. + * + * Attributes: + * @li fine_offence + * @li fine_amount + */ +#define SCS_TELEMETRY_GAMEPLAY_EVENT_player_fined "player.fined" + +/** + * @brief Event called when player pays for a tollgate. + * + * Attributes: + * @li pay_amount + */ +#define SCS_TELEMETRY_GAMEPLAY_EVENT_player_tollgate_paid "player.tollgate.paid" + +/** + * @brief Event called when player uses a ferry. + * + * Attributes: + * @li pay_amount + * @li source_name + * @li target_name + * @li source_id + * @li target_id + */ +#define SCS_TELEMETRY_GAMEPLAY_EVENT_player_use_ferry "player.use.ferry" + +/** + * @brief Event called when player uses a train. + * + * Attributes: + * @li pay_amount + * @li source_name + * @li target_name + * @li source_id + * @li target_id + */ +#define SCS_TELEMETRY_GAMEPLAY_EVENT_player_use_train "player.use.train" + +// Attributes + +/** + * @brief The penalty for cancelling the job in native game currency. (Can be 0) + * + * Type: s64 + */ +#define SCS_TELEMETRY_GAMEPLAY_EVENT_ATTRIBUTE_cancel_penalty "cancel.penalty" + +/** + * @brief The job revenue in native game currency. + * + * Type: s64 + */ +#define SCS_TELEMETRY_GAMEPLAY_EVENT_ATTRIBUTE_revenue "revenue" + +/** + * @brief How much XP player received for the job. + * + * Type: s32 + */ +#define SCS_TELEMETRY_GAMEPLAY_EVENT_ATTRIBUTE_earned_xp "earned.xp" + +/** + * @brief Total cargo damage. (Range <0.0, 1.0>) + * + * Type: float + */ +#define SCS_TELEMETRY_GAMEPLAY_EVENT_ATTRIBUTE_cargo_damage "cargo.damage" + +/** + * @brief The real distance in km on the job. + * + * Type: float + */ +#define SCS_TELEMETRY_GAMEPLAY_EVENT_ATTRIBUTE_distance_km "distance.km" + +/** + * @brief Total time spend on the job in game minutes. + * + * Type: u32 + */ +#define SCS_TELEMETRY_GAMEPLAY_EVENT_ATTRIBUTE_delivery_time "delivery.time" + +/** + * @brief Was auto parking used on this job? + * + * Type: bool + */ +#define SCS_TELEMETRY_GAMEPLAY_EVENT_ATTRIBUTE_auto_park_used "auto.park.used" + +/** + * @brief Was auto loading used on this job? (always @c true for non cargo market jobs) + * + * Type: bool + */ +#define SCS_TELEMETRY_GAMEPLAY_EVENT_ATTRIBUTE_auto_load_used "auto.load.used" + +/** + * @brief Fine offence type. + * + * Possible values: + * @li crash + * @li avoid_sleeping + * @li wrong_way + * @li speeding_camera + * @li no_lights + * @li red_signal + * @li speeding + * @li avoid_weighing + * @li illegal_trailer + * @li avoid_inspection + * @li illegal_border_crossing + * @li hard_shoulder_violation + * @li damaged_vehicle_usage + * @li generic + * + * Type: string + */ +#define SCS_TELEMETRY_GAMEPLAY_EVENT_ATTRIBUTE_fine_offence "fine.offence" + +/** + * @brief Fine offence amount in native game currency. + * + * Type: s64 + */ +#define SCS_TELEMETRY_GAMEPLAY_EVENT_ATTRIBUTE_fine_amount "fine.amount" + +/** + * @brief How much player was charged for this action (in native game currency) + * + * Type: s64 + */ +#define SCS_TELEMETRY_GAMEPLAY_EVENT_ATTRIBUTE_pay_amount "pay.amount" + +/** + * @brief The name of the transportation source. + * + * Type: string + */ +#define SCS_TELEMETRY_GAMEPLAY_EVENT_ATTRIBUTE_source_name "source.name" + +/** + * @brief The name of the transportation target. + * + * Type: string + */ +#define SCS_TELEMETRY_GAMEPLAY_EVENT_ATTRIBUTE_target_name "target.name" + +/** + * @brief The id of the transportation source. + * + * Type: string + */ +#define SCS_TELEMETRY_GAMEPLAY_EVENT_ATTRIBUTE_source_id "source.id" + +/** + * @brief The id of the transportation target. + * + * Type: string + */ +#define SCS_TELEMETRY_GAMEPLAY_EVENT_ATTRIBUTE_target_id "target.id" + +SCSSDK_FOOTER + +#endif // SCSSDK_TELEMETRY_COMMON_GAMEPLAY_EVENTS_H + +/* eof */ diff --git a/linux/sdk/include/common/scssdk_telemetry_job_common_channels.h b/linux/sdk/include/common/scssdk_telemetry_job_common_channels.h new file mode 100644 index 000000000..018b6f442 --- /dev/null +++ b/linux/sdk/include/common/scssdk_telemetry_job_common_channels.h @@ -0,0 +1,24 @@ +/** + * @file scssdk_telemetry_job_common_channels.h + * + * @brief Job telemetry specific constants for channels. + */ +#ifndef SCSSDK_TELEMETRY_JOB_COMMON_CHANNELS_H +#define SCSSDK_TELEMETRY_JOB_COMMON_CHANNELS_H + +#include "../scssdk.h" + +SCSSDK_HEADER + +/** + * @brief The total damage of the cargo in range 0.0 to 1.0. + * + * Type: float + */ +#define SCS_TELEMETRY_JOB_CHANNEL_cargo_damage "job.cargo.damage" + +SCSSDK_FOOTER + +#endif // SCSSDK_TELEMETRY_JOB_COMMON_CHANNELS_H + +/* eof */ diff --git a/linux/sdk/include/common/scssdk_telemetry_trailer_common_channels.h b/linux/sdk/include/common/scssdk_telemetry_trailer_common_channels.h new file mode 100644 index 000000000..4acbb9346 --- /dev/null +++ b/linux/sdk/include/common/scssdk_telemetry_trailer_common_channels.h @@ -0,0 +1,79 @@ +/** + * @file scssdk_telemetry_trailer_common_channels.h + * + * @brief Trailer telemetry specific constants for channels. + * + * See scssdk_telemetry_truck_common_channels.h for more info. + */ +#ifndef SCSSDK_TELEMETRY_TRAILER_COMMON_CHANNELS_H +#define SCSSDK_TELEMETRY_TRAILER_COMMON_CHANNELS_H + +#include "../scssdk.h" + +SCSSDK_HEADER + +/** + * Telemetry SDK supports multiple trailers. + * + * To get information about more trailers replace "trailer." with "trailer.[index].". + * Connected state for trailers would be: + * + * First trailer: "trailer.0.connected" + * Second trailer: "trailer.1.connected" + * ... + * Six-th trailer: "trailer.5.connected" + * etc + * + * Maximum number of trailers that can be reported by telemetry SDK + * is defined by @c SCS_TELEMETRY_trailers_count. + */ + +/** + * @brief Is the trailer connected to the truck? + * + * Type: bool + */ +#define SCS_TELEMETRY_TRAILER_CHANNEL_connected "trailer.connected" + +/** + * @brief How much is the cargo damaged that is loaded to this trailer in <0.0, 1.0> range. + * + * Type: float + */ +#define SCS_TELEMETRY_TRAILER_CHANNEL_cargo_damage "trailer.cargo.damage" + +/** + * @name Channels similar to the truck ones + * + * See scssdk_telemetry_truck_common_channels.h for description of + * corresponding truck channels + */ +//@{ +#define SCS_TELEMETRY_TRAILER_CHANNEL_world_placement "trailer.world.placement" +#define SCS_TELEMETRY_TRAILER_CHANNEL_local_linear_velocity "trailer.velocity.linear" +#define SCS_TELEMETRY_TRAILER_CHANNEL_local_angular_velocity "trailer.velocity.angular" +#define SCS_TELEMETRY_TRAILER_CHANNEL_local_linear_acceleration "trailer.acceleration.linear" +#define SCS_TELEMETRY_TRAILER_CHANNEL_local_angular_acceleration "trailer.acceleration.angular" + +// Damage. + +#define SCS_TELEMETRY_TRAILER_CHANNEL_wear_chassis "trailer.wear.chassis" +#define SCS_TELEMETRY_TRAILER_CHANNEL_wear_wheels "trailer.wear.wheels" + +// Wheels. + +#define SCS_TELEMETRY_TRAILER_CHANNEL_wheel_susp_deflection "trailer.wheel.suspension.deflection" +#define SCS_TELEMETRY_TRAILER_CHANNEL_wheel_on_ground "trailer.wheel.on_ground" +#define SCS_TELEMETRY_TRAILER_CHANNEL_wheel_substance "trailer.wheel.substance" +#define SCS_TELEMETRY_TRAILER_CHANNEL_wheel_velocity "trailer.wheel.angular_velocity" +#define SCS_TELEMETRY_TRAILER_CHANNEL_wheel_steering "trailer.wheel.steering" +#define SCS_TELEMETRY_TRAILER_CHANNEL_wheel_rotation "trailer.wheel.rotation" +#define SCS_TELEMETRY_TRAILER_CHANNEL_wheel_lift "trailer.wheel.lift" +#define SCS_TELEMETRY_TRAILER_CHANNEL_wheel_lift_offset "trailer.wheel.lift.offset" +//@} + +SCSSDK_FOOTER + +#endif // SCSSDK_TELEMETRY_TRAILER_COMMON_CHANNELS_H + +/* eof */ diff --git a/linux/sdk/include/common/scssdk_telemetry_truck_common_channels.h b/linux/sdk/include/common/scssdk_telemetry_truck_common_channels.h new file mode 100644 index 000000000..1506fa818 --- /dev/null +++ b/linux/sdk/include/common/scssdk_telemetry_truck_common_channels.h @@ -0,0 +1,720 @@ +/** + * @file scssdk_telemetry_truck_common_channels.h + * + * @brief Truck telemetry specific constants for channels. + * + * This file defines truck specific telemetry constants which + * might be used by more than one SCS game. See game-specific + * file to determine which constants are supported by specific + * game. + * + * Unless state otherwise, following rules apply. + * @li Whenever channel has float based type (float, fvector, fplacement) + * is can also provide double based values (double, dvector, dplacement) + * and vice versa. Note that using the non-native type might incur + * conversion costs or cause precision loss (double->float in + * world-space context). + * @li Whenever channel has u32 type is can also provide u64 value. + * Note that using the non-native type might incur conversion costs. + * @li Whenever channel uses placement based type (dplacement, fplacement), + * it also supports euler type containg just the rotational part and + * dvector/fvector type containing just the positional part. + * @li Indexed entries are using zero-based indices. + */ +#ifndef SCSSDK_TELEMETRY_TRUCK_COMMON_CHANNELS_H +#define SCSSDK_TELEMETRY_TRUCK_COMMON_CHANNELS_H + +#include "../scssdk.h" + +SCSSDK_HEADER + +// Movement. + +/** + * @brief Represents world space position and orientation of the truck. + * + * Type: dplacement + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_world_placement "truck.world.placement" + +/** + * @brief Represents vehicle space linear velocity of the truck measured + * in meters per second. + * + * Type: fvector + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_local_linear_velocity "truck.local.velocity.linear" + +/** + * @brief Represents vehicle space angular velocity of the truck measured + * in rotations per second. + * + * Type: fvector + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_local_angular_velocity "truck.local.velocity.angular" + +/** + * @brief Represents vehicle space linear acceleration of the truck measured + * in meters per second^2 + * + * Type: fvector + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_local_linear_acceleration "truck.local.acceleration.linear" + + /** + * @brief Represents vehicle space angular acceleration of the truck meassured + * in rotations per second^2 + * + * Type: fvector + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_local_angular_acceleration "truck.local.acceleration.angular" + +/** + * @brief Represents a vehicle space position and orientation delta + * of the cabin from its default position. + * + * Type: fplacement + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_cabin_offset "truck.cabin.offset" + +/** + * @brief Represents cabin space angular velocity of the cabin measured + * in rotations per second. + * + * Type: fvector + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_cabin_angular_velocity "truck.cabin.velocity.angular" + + /** + * @brief Represents cabin space angular acceleration of the cabin + * measured in rotations per second^2 + * + * Type: fvector + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_cabin_angular_acceleration "truck.cabin.acceleration.angular" + +/** + * @brief Represents a cabin space position and orientation delta + * of the driver head from its default position. + * + * Note that this value might change rapidly as result of + * the user switching between cameras or camera presets. + * + * Type: fplacement + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_head_offset "truck.head.offset" + +/** + * @brief Speedometer speed in meters per second. + * + * Uses negative value to represent reverse movement. + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_speed "truck.speed" + +// Powertrain related + +/** + * @brief RPM of the engine. + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_engine_rpm "truck.engine.rpm" + +/** + * @brief Gear currently selected in the engine. + * + * @li >0 - Forwad gears + * @li 0 - Neutral + * @li <0 - Reverse gears + * + * Type: s32 + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_engine_gear "truck.engine.gear" + +/** + * @brief Gear currently displayed on dashboard. + * + * @li >0 - Forwad gears + * @li 0 - Neutral + * @li <0 - Reverse gears + * + * Type: s32 + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_displayed_gear "truck.displayed.gear" + +// Driving + +/** + * @brief Steering received from input <-1;1>. + * + * Note that it is interpreted counterclockwise. + * + * If the user presses the steer right button on digital input + * (e.g. keyboard) this value goes immediatelly to -1.0 + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_input_steering "truck.input.steering" + +/** + * @brief Throttle received from input <0;1> + * + * If the user presses the forward button on digital input + * (e.g. keyboard) this value goes immediatelly to 1.0 + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_input_throttle "truck.input.throttle" + +/** + * @brief Brake received from input <0;1> + * + * If the user presses the brake button on digital input + * (e.g. keyboard) this value goes immediatelly to 1.0 + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_input_brake "truck.input.brake" + +/** + * @brief Clutch received from input <0;1> + * + * If the user presses the clutch button on digital input + * (e.g. keyboard) this value goes immediatelly to 1.0 + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_input_clutch "truck.input.clutch" + +/** + * @brief Steering as used by the simulation <-1;1> + * + * Note that it is interpreted counterclockwise. + * + * Accounts for interpolation speeds and simulated + * counterfoces for digital inputs. + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_effective_steering "truck.effective.steering" + +/** + * @brief Throttle pedal input as used by the simulation <0;1> + * + * Accounts for the press attack curve for digital inputs + * or cruise-control input. + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_effective_throttle "truck.effective.throttle" + +/** + * @brief Brake pedal input as used by the simulation <0;1> + * + * Accounts for the press attack curve for digital inputs. Does + * not contain retarder, parking or engine brake. + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_effective_brake "truck.effective.brake" + +/** + * @brief Clutch pedal input as used by the simulation <0;1> + * + * Accounts for the automatic shifting or interpolation of + * player input. + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_effective_clutch "truck.effective.clutch" + +/** + * @brief Speed selected for the cruise control in m/s + * + * Is zero if cruise control is disabled. + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_cruise_control "truck.cruise_control" + +// Gearbox related + +/** + * @brief Gearbox slot the h-shifter handle is currently in. + * + * 0 means that no slot is selected. + * + * Type: u32 + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_hshifter_slot "truck.hshifter.slot" + +/** + * @brief Enabled state of range/splitter selector toggles. + * + * Mapping between the range/splitter functionality and + * selector index is described by HSHIFTER configuration. + * + * Type: indexed bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_hshifter_selector "truck.hshifter.select" + + // Brakes. + +/** + * @brief Is the parking brake enabled? + * + * Type: bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_parking_brake "truck.brake.parking" + +/** + * @brief Is the engine brake enabled? + * + * Type: bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_motor_brake "truck.brake.motor" + +/** + * @brief Current level of the retarder. + * + * <0;max> where 0 is disabled retarder and max is maximum + * value found in TRUCK configuration. + * + * Type: u32 + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_retarder_level "truck.brake.retarder" + +/** + * @brief Pressure in the brake air tank in psi + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_brake_air_pressure "truck.brake.air.pressure" + +/** + * @brief Is the air pressure warning active? + * + * Type: bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_brake_air_pressure_warning "truck.brake.air.pressure.warning" + +/** + * @brief Are the emergency brakes active as result of low air pressure? + * + * Type: bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_brake_air_pressure_emergency "truck.brake.air.pressure.emergency" + +/** + * @brief Temperature of the brakes in degrees celsius. + * + * Aproximated for entire truck, not at the wheel level. + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_brake_temperature "truck.brake.temperature" + +// Various "consumables" + +/** + * @brief Amount of fuel in liters + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_fuel "truck.fuel.amount" + +/** + * @brief Is the low fuel warning active? + * + * Type: bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_fuel_warning "truck.fuel.warning" + +/** + * @brief Average consumption of the fuel in liters/km + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_fuel_average_consumption "truck.fuel.consumption.average" + +/** + * @brief Estimated range of truck with current amount of fuel in km + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_fuel_range "truck.fuel.range" + +/** + * @brief Amount of AdBlue in liters + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_adblue "truck.adblue" + +/** + * @brief Is the low adblue warning active? + * + * Type: bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_adblue_warning "truck.adblue.warning" + +/** + * @brief Average consumption of the adblue in liters/km + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_adblue_average_consumption "truck.adblue.consumption.average" + +// Oil + +/** + * @brief Pressure of the oil in psi + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_oil_pressure "truck.oil.pressure" + +/** + * @brief Is the oil pressure warning active? + * + * Type: bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_oil_pressure_warning "truck.oil.pressure.warning" + +/** + * @brief Temperature of the oil in degrees celsius. + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_oil_temperature "truck.oil.temperature" + +// Temperature in various systems. + +/** + * @brief Temperature of the water in degrees celsius. + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_water_temperature "truck.water.temperature" + +/** + * @brief Is the water temperature warning active? + * + * Type: bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_water_temperature_warning "truck.water.temperature.warning" + +// Battery + +/** + * @brief Voltage of the battery in volts. + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_battery_voltage "truck.battery.voltage" + +/** + * @brief Is the battery voltage/not charging warning active? + * + * Type: bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_battery_voltage_warning "truck.battery.voltage.warning" + +// Enabled state of various elements. + +/** + * @brief Is the electric enabled? + * + * Type: bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_electric_enabled "truck.electric.enabled" + +/** + * @brief Is the engine enabled? + * + * Type: bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_engine_enabled "truck.engine.enabled" + +/** + * @brief Is the left blinker enabled? + * + * This represents the logical enable state of the blinker. It + * it is true as long the blinker is enabled regardless of the + * physical enabled state of the light (i.e. it does not blink + * and ignores enable state of electric). + * + * Type: bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_lblinker "truck.lblinker" + +/** + * @brief Is the right blinker enabled? + * + * This represents the logical enable state of the blinker. It + * it is true as long the blinker is enabled regardless of the + * physical enabled state of the light (i.e. it does not blink + * and ignores enable state of electric). + * + * Type: bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_rblinker "truck.rblinker" + +/** + * @brief Is the light in the left blinker currently on? + * + * Type: bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_light_lblinker "truck.light.lblinker" + +/** + * @brief Is the light in the right blinker currently on? + * + * Type: bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_light_rblinker "truck.light.rblinker" + +/** + * @brief Are the parking lights enabled? + * + * Type: bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_light_parking "truck.light.parking" + +/** + * @brief Are the low beam lights enabled? + * + * Type: bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_light_low_beam "truck.light.beam.low" + +/** + * @brief Are the high beam lights enabled? + * + * Type: bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_light_high_beam "truck.light.beam.high" + +/** + * @brief Are the auxiliary front lights active? + * + * Those lights have several intensity levels: + * @li 1 - dimmed state + * @li 2 - full state + * + * Type: u32 + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_light_aux_front "truck.light.aux.front" + +/** + * @brief Are the auxiliary roof lights active? + * + * Those lights have several intensity levels: + * @li 1 - dimmed state + * @li 2 - full state + * + * Type: u32 + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_light_aux_roof "truck.light.aux.roof" + +/** + * @brief Are the beacon lights enabled? + * + * Type: bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_light_beacon "truck.light.beacon" + +/** + * @brief Is the brake light active? + * + * Type: bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_light_brake "truck.light.brake" + +/** + * @brief Is the reverse light active? + * + * Type: bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_light_reverse "truck.light.reverse" + +/** + * @brief Are the wipers enabled? + * + * Type: bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_wipers "truck.wipers" + +/** + * @brief Intensity of the dashboard backlight as factor <0;1> + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_dashboard_backlight "truck.dashboard.backlight" + +// Wear info. + +/** + * @brief Wear of the engine accessory as <0;1> + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_wear_engine "truck.wear.engine" + +/** + * @brief Wear of the transmission accessory as <0;1> + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_wear_transmission "truck.wear.transmission" + +/** + * @brief Wear of the cabin accessory as <0;1> + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_wear_cabin "truck.wear.cabin" + +/** + * @brief Wear of the chassis accessory as <0;1> + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_wear_chassis "truck.wear.chassis" + +/** + * @brief Average wear across the wheel accessories as <0;1> + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_wear_wheels "truck.wear.wheels" + +/** + * @brief The value of the odometer in km. + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_odometer "truck.odometer" + +/** + * @brief The value of truck's navigation distance (in meters). + * + * This is the value used by the advisor. + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_navigation_distance "truck.navigation.distance" + +/** + * @brief The value of truck's navigation eta (in second). + * + * This is the value used by the advisor. + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_navigation_time "truck.navigation.time" + +/** + * @brief The value of truck's navigation speed limit (in m/s). + * + * This is the value used by the advisor and respects the + * current state of the "Route Advisor speed limit" option. + * + * Type: float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_navigation_speed_limit "truck.navigation.speed.limit" + +// Wheels. + +/** + * @brief Vertical displacement of the wheel from its + * axis in meters. + * + * Type: indexed float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_wheel_susp_deflection "truck.wheel.suspension.deflection" + +/** + * @brief Is the wheel in contact with ground? + * + * Type: indexed bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_wheel_on_ground "truck.wheel.on_ground" + +/** + * @brief Substance below the whell. + * + * Index of substance as delivered trough SUBSTANCE config. + * + * Type: indexed u32 + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_wheel_substance "truck.wheel.substance" + +/** + * @brief Angular velocity of the wheel in rotations per + * second. + * + * Positive velocity corresponds to forward movement. + * + * Type: indexed float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_wheel_velocity "truck.wheel.angular_velocity" + +/** + * @brief Steering rotation of the wheel in rotations. + * + * Value is from <-0.25,0.25> range in counterclockwise direction + * when looking from top (e.g. 0.25 corresponds to left and + * -0.25 corresponds to right). + * + * Set to zero for non-steered wheels. + * + * Type: indexed float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_wheel_steering "truck.wheel.steering" + +/** + * @brief Rolling rotation of the wheel in rotations. + * + * Value is from <0.0,1.0) range in which value + * increase corresponds to forward movement. + * + * Type: indexed float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_wheel_rotation "truck.wheel.rotation" + +/** + * @brief Lift state of the wheel <0;1> + * + * For use with simple lifted/non-lifted test or logical + * visualization of the lifting progress. + * + * Value of 0 corresponds to non-lifted axle. + * Value of 1 corresponds to fully lifted axle. + * + * Set to zero or not provided for non-liftable axles. + * + * Type: indexed float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_wheel_lift "truck.wheel.lift" + +/** + * @brief Vertical displacement of the wheel axle + * from its normal position in meters as result of + * lifting. + * + * Might have non-linear relation to lift ratio. + * + * Set to zero or not provided for non-liftable axles. + * + * Type: indexed float + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_wheel_lift_offset "truck.wheel.lift.offset" + +SCSSDK_FOOTER + +#endif // SCSSDK_TELEMETRY_TRUCK_COMMON_CHANNELS_H + +/* eof */ diff --git a/linux/sdk/include/eurotrucks2/scssdk_eut2.h b/linux/sdk/include/eurotrucks2/scssdk_eut2.h new file mode 100644 index 000000000..473ce762d --- /dev/null +++ b/linux/sdk/include/eurotrucks2/scssdk_eut2.h @@ -0,0 +1,22 @@ +/** + * @file scssdk_eut2.h + * + * @brief ETS 2 specific constants. + */ +#ifndef SCSSDK_EUT2_H +#define SCSSDK_EUT2_H + +#include "../scssdk.h" + +SCSSDK_HEADER + +/** + * @brief Value used in the scs_sdk_init_params_t::game_id to identify this game. + */ +#define SCS_GAME_ID_EUT2 "eut2" + +SCSSDK_FOOTER + +#endif // SCSSDK_EUT2_H + +/* eof */ diff --git a/linux/sdk/include/eurotrucks2/scssdk_telemetry_eut2.h b/linux/sdk/include/eurotrucks2/scssdk_telemetry_eut2.h new file mode 100644 index 000000000..6577531b5 --- /dev/null +++ b/linux/sdk/include/eurotrucks2/scssdk_telemetry_eut2.h @@ -0,0 +1,103 @@ +/** + * @file scssdk_telemetry_eut2.h + * + * @brief ETS 2 telemetry specific constants. + */ +#ifndef SCSSDK_TELEMETRY_EUT2_H +#define SCSSDK_TELEMETRY_EUT2_H + +#include "../scssdk.h" +#include "../common/scssdk_telemetry_common_configs.h" +#include "../common/scssdk_telemetry_common_channels.h" +#include "../common/scssdk_telemetry_truck_common_channels.h" +#include "../common/scssdk_telemetry_trailer_common_channels.h" +#include "../common/scssdk_telemetry_job_common_channels.h" +#include "../common/scssdk_telemetry_common_gameplay_events.h" + +SCSSDK_HEADER + +/** + * @name Value used in the scs_sdk_init_params_t::game_version + * + * Changes in the major version indicate incompatible changes (e.g. changed interpretation + * of the channel value). Change of major version is highly discouraged, creation of + * alternative channel is preferred solution if necessary. + * Changes in the minor version indicate compatible changes (e.g. added channel, more supported + * value types). Removal of channel is also compatible change however it is recommended + * to keep the channel with some default value. + * + * Changes: + * 1.01 - added brake_air_pressure_emergency channel and air_pressure_emergency config + * 1.02 - replaced cabin_orientation channel with cabin_offset channel + * 1.03 - fixed reporting of invalid index value for wheels.count attribute + * 1.04 - added lblinker_light and rblinker_light channels + * 1.05 - fixed content of brand_id and brand attributes + * 1.06 - fixed index value for selector_count attribute. It is now SCS_U32_NIL as the + * attribute is not indexed. For backward compatibility additional copy with + * index 0 is also present however it will be removed in the future. + * 1.07 - fixed calculation of cabin_angular_acceleration channel. + * 1.08 - a empty truck/trailer configuration event is generated when truck is removed + * (e.g. after completion of quick job) + * 1.09 - added time and job related info + * 1.10 - added information about liftable axes + * 1.11 - u32 channels can provide u64 as documented, added displayed_gear channel, increased + * maximum number of supported wheel channels to 14 + * 1.12 - added information about transmission (differential_ratio, forward_ratio, reverse_ratio), + * navigation channels (navigation_distance, navigation_time, navigation_speed_limit) + * and adblue related data are now provided. + * 1.13 - fixed values of id and cargo_accessory_id attributes in trailer config broken by + * ETS2 1.25 update. Note that the new values will be different from the ones returned + * by ETS2 1.24 and older. + * 1.14 - added support for multiple trailers (doubles, triples), trailer ownership support, + * gameplay events support added + * 1.15 - added planned_distance_km to active job info + * 1.16 - added support for 'avoid_inspection', 'illegal_border_crossing' and 'hard_shoulder_violation' offence type in 'player.fined' gameplay event + */ +//@{ +#define SCS_TELEMETRY_EUT2_GAME_VERSION_1_00 SCS_MAKE_VERSION(1, 0) +#define SCS_TELEMETRY_EUT2_GAME_VERSION_1_01 SCS_MAKE_VERSION(1, 1) +#define SCS_TELEMETRY_EUT2_GAME_VERSION_1_02 SCS_MAKE_VERSION(1, 2) +#define SCS_TELEMETRY_EUT2_GAME_VERSION_1_03 SCS_MAKE_VERSION(1, 3) +#define SCS_TELEMETRY_EUT2_GAME_VERSION_1_04 SCS_MAKE_VERSION(1, 4) +#define SCS_TELEMETRY_EUT2_GAME_VERSION_1_05 SCS_MAKE_VERSION(1, 5) // Patch 1.4 +#define SCS_TELEMETRY_EUT2_GAME_VERSION_1_06 SCS_MAKE_VERSION(1, 6) +#define SCS_TELEMETRY_EUT2_GAME_VERSION_1_07 SCS_MAKE_VERSION(1, 7) // Patch 1.6 +#define SCS_TELEMETRY_EUT2_GAME_VERSION_1_08 SCS_MAKE_VERSION(1, 8) // Patch 1.9 +#define SCS_TELEMETRY_EUT2_GAME_VERSION_1_09 SCS_MAKE_VERSION(1, 9) // Patch 1.14 beta +#define SCS_TELEMETRY_EUT2_GAME_VERSION_1_10 SCS_MAKE_VERSION(1, 10) // Patch 1.14 +#define SCS_TELEMETRY_EUT2_GAME_VERSION_1_11 SCS_MAKE_VERSION(1, 11) +#define SCS_TELEMETRY_EUT2_GAME_VERSION_1_12 SCS_MAKE_VERSION(1, 12) // Patch 1.17 +#define SCS_TELEMETRY_EUT2_GAME_VERSION_1_13 SCS_MAKE_VERSION(1, 13) // Patch 1.27 +#define SCS_TELEMETRY_EUT2_GAME_VERSION_1_14 SCS_MAKE_VERSION(1, 14) // Patch 1.35 +#define SCS_TELEMETRY_EUT2_GAME_VERSION_1_15 SCS_MAKE_VERSION(1, 15) // Patch 1.36 +#define SCS_TELEMETRY_EUT2_GAME_VERSION_1_16 SCS_MAKE_VERSION(1, 16) // Patch 1.36 +#define SCS_TELEMETRY_EUT2_GAME_VERSION_CURRENT SCS_TELEMETRY_EUT2_GAME_VERSION_1_16 +//@} + +// Game specific units. +// +// @li The game uses Euro as internal currency provided +// by the telemetry unless documented otherwise. + +// Channels defined in scssdk_telemetry_common_channels.h, +// scssdk_telemetry_job_common_channels.h, +// scssdk_telemetry_truck_common_channels.h and +// scssdk_telemetry_trailer_common_channels.h are supported +// with following exceptions and limitations as of v1.00: +// +// @li Rolling rotation of trailer wheels is determined from linear +// movement. +// @li The pressures, temperatures and voltages are not simulated. +// They are very loosely approximated. + +// Configurations defined in scssdk_telemetry_common_configs.h are +// supported with following exceptions and limitations as of v1.00: +// +// @li The localized strings are not updated when different in-game +// language is selected. + +SCSSDK_FOOTER + +#endif // SCSSDK_TELEMETRY_EUT2_H + +/* eof */ diff --git a/linux/sdk/include/scssdk.h b/linux/sdk/include/scssdk.h new file mode 100644 index 000000000..95dabc544 --- /dev/null +++ b/linux/sdk/include/scssdk.h @@ -0,0 +1,202 @@ +/** + * @file scssdk.h + * + * @brief Common SDK types and structures. + */ +#ifndef SCSSDK_H +#define SCSSDK_H + +#define SCSSDK_HEADER extern "C" { +#define SCSSDK_FOOTER } + +SCSSDK_HEADER + +// Types used trough the SDK. + +#if defined(_MSC_VER) + +typedef unsigned __int8 scs_u8_t; +typedef unsigned __int16 scs_u16_t; +typedef signed __int32 scs_s32_t; +typedef unsigned __int32 scs_u32_t; +typedef unsigned __int64 scs_u64_t; +typedef signed __int64 scs_s64_t; +typedef float scs_float_t; +typedef double scs_double_t; +typedef const char * scs_string_t; +#define SCSAPIFUNC __stdcall + +#if defined(_WIN64) +#define SCS_ARCHITECTURE_x64 +#else +#define SCS_ARCHITECTURE_x86 +#endif + +#define SCS_PF_U64 "I64u" +#define SCS_PF_S64 "I64d" + +#elif defined(__GNUG__) + +#include +typedef uint8_t scs_u8_t; +typedef uint16_t scs_u16_t; +typedef int32_t scs_s32_t; +typedef uint32_t scs_u32_t; +typedef uint64_t scs_u64_t; +typedef int64_t scs_s64_t; +typedef float scs_float_t; +typedef double scs_double_t; +typedef const char * scs_string_t; +#define SCSAPIFUNC + +#if defined(__x86_64__) +#define SCS_ARCHITECTURE_x64 +#define SCS_PF_U64 "lu" +#define SCS_PF_S64 "ld" +#elif defined(__i386__) +#define SCS_ARCHITECTURE_x86 +#define SCS_PF_U64 "llu" +#define SCS_PF_S64 "lld" +#else +#error "Unknown architecture." +#endif + +#else +#error "Unknown compiler." +#endif + +const scs_u32_t SCS_U32_NIL = static_cast(-1); + +/** + * @brief Type of value provided during callback registration and passed back + * to the callback. + */ +typedef void *scs_context_t; + +/** + * @brief Timestamp value. + * + * Value is expressed in microseconds. + */ +typedef scs_u64_t scs_timestamp_t; + +// Common return codes. + +typedef scs_s32_t scs_result_t; +const scs_result_t SCS_RESULT_ok = 0; // Operation succeeded. +const scs_result_t SCS_RESULT_unsupported = -1; // Operation or specified parameters are not supported. (e.g. the plugin does not support the requested version of the API) +const scs_result_t SCS_RESULT_invalid_parameter = -2; // Specified parameter is not valid (e.g. null value of callback, invalid combination of flags). +const scs_result_t SCS_RESULT_already_registered = -3; // There is already a registered callback for the specified function (e.g. event/channel). +const scs_result_t SCS_RESULT_not_found = -4; // Specified item (e.g. channel) was not found. +const scs_result_t SCS_RESULT_unsupported_type = -5; // Specified value type is not supported (e.g. channel does not provide that value type). +const scs_result_t SCS_RESULT_not_now = -6; // Action (event/callback registration) is not allowed in the current state. Indicates incorrect use of the api. +const scs_result_t SCS_RESULT_generic_error = -7; // Error not covered by other existing code. + +// Function definition macros. + +#define SCSAPI_RESULT scs_result_t SCSAPIFUNC +#define SCSAPI_VOID void SCSAPIFUNC + +#define SCSAPI_RESULT_FPTR(function_name) scs_result_t (SCSAPIFUNC *function_name) +#define SCSAPI_VOID_FPTR(function_name) void (SCSAPIFUNC *function_name) + +// Compile time checks. + +#define SCS_CONCAT2(prefix, suffix) prefix##suffix +#define SCS_CONCAT(prefix, suffix) SCS_CONCAT2(prefix, suffix) +#define scs_static_check(expr) typedef int SCS_CONCAT(some_requirement_failed_at_, __LINE__)[(expr) ? 1 : -1] + +#if defined(SCS_ARCHITECTURE_x86) +#define scs_check_size(structure, expected_32, expected_64) scs_static_check(sizeof(structure) == expected_32) +#elif defined(SCS_ARCHITECTURE_x64) +#define scs_check_size(structure, expected_32, expected_64) scs_static_check(sizeof(structure) == expected_64) +#endif + +// Types of messages printed to log. + +typedef scs_s32_t scs_log_type_t; +const scs_log_type_t SCS_LOG_TYPE_message = 0; +const scs_log_type_t SCS_LOG_TYPE_warning = 1; +const scs_log_type_t SCS_LOG_TYPE_error = 2; + +// Version support. + +#define SCS_MAKE_VERSION(major, minor) (((major) << 16) | (minor)) +#define SCS_GET_MAJOR_VERSION(version) (((version) >> 16) & 0xffff) +#define SCS_GET_MINOR_VERSION(version) ((version) & 0xffff) + +/** + * @brief Logs specified message to the game log. + * + * @param type Type of message. Controls generated prefixes and colors in console. + * @param message Message to log. + */ +typedef SCSAPI_VOID_FPTR(scs_log_t)(const scs_log_type_t type, const scs_string_t message); + +// Common initialization structures. + +/** + * @brief Initialization parameters common to most APIs provided + * by the SDK. + */ +struct scs_sdk_init_params_v100_t +{ + /** + * @brief Name of the game for display purposes. + * + * This is UTF8 encoded string containing name of the game + * for display to the user. The exact format is not defined, + * might be changed between versions and should be not parsed. + * + * This pointer will be never NULL. + */ + scs_string_t game_name; + + /** + * @brief Identification of the game. + * + * If the library wants to identify the game to do any + * per-game configuration, this is the field which should + * be used. + * + * This string contains only following characters: + * @li lower-cased letters + * @li digits + * @li underscore + * + * This pointer will be never NULL. + */ + scs_string_t game_id; + + /** + * @brief Version of the game for purpose of the specific api + * which is being initialized. + * + * Does NOT match the patch level of the game. + */ + scs_u32_t game_version; + +#ifdef SCS_ARCHITECTURE_x64 + /** + * @brief Explicit alignment for the 64 bit pointer. + */ + scs_u32_t _padding; +#endif + + /** + * @brief Function used to write messages to the game log. + * + * Each message is printed on a separate line. + * + * This pointer will be never NULL. + */ + scs_log_t log; +}; + +scs_check_size(scs_sdk_init_params_v100_t, 16, 32); + +SCSSDK_FOOTER + +#endif // SCSSDK_H + +/* eof */ diff --git a/linux/sdk/include/scssdk_telemetry.h b/linux/sdk/include/scssdk_telemetry.h new file mode 100644 index 000000000..626d42158 --- /dev/null +++ b/linux/sdk/include/scssdk_telemetry.h @@ -0,0 +1,107 @@ +/** + * @file scssdk_telemetry.h + * + * @brief Telemetry SDK. + */ +#ifndef SCSSDK_TELEMETRY_H +#define SCSSDK_TELEMETRY_H + +#include "scssdk.h" +#include "scssdk_value.h" +#include "scssdk_telemetry_event.h" +#include "scssdk_telemetry_channel.h" + +SCSSDK_HEADER + +/** + * @name Versions of the telemetry SDK + * + * Changes in the major version indicate incompatible changes in the API. + * Changes in the minor version indicate additions (e.g. more events, defined + * types as long layout of existing fields in scs_value_t does not change). + * + * 1.01 version - added s64 type support, added gameplay events + */ +//@{ +#define SCS_TELEMETRY_VERSION_1_00 SCS_MAKE_VERSION(1, 0) +#define SCS_TELEMETRY_VERSION_1_01 SCS_MAKE_VERSION(1, 1) +#define SCS_TELEMETRY_VERSION_CURRENT SCS_TELEMETRY_VERSION_1_01 +//@} + +// Structures used to pass additional data to the initialization function. + +/** + * @brief Common ancestor to all structures providing parameters to the telemetry + * initialization. + */ +struct scs_telemetry_init_params_t +{ + void method_indicating_this_is_not_a_c_struct(void); +}; + +/** + * @brief Initialization parameters for the 1.00 version of the telemetry API. + */ +struct scs_telemetry_init_params_v100_t : public scs_telemetry_init_params_t +{ + /** + * @brief Common initialization parameters. + */ + scs_sdk_init_params_v100_t common; + + /** + * @name Functions used to handle registration of event callbacks. + */ + //@{ + scs_telemetry_register_for_event_t register_for_event; + scs_telemetry_unregister_from_event_t unregister_from_event; + //@} + + /** + * @name Functions used to handle registration of telemetry callbacks. + */ + //@{ + scs_telemetry_register_for_channel_t register_for_channel; + scs_telemetry_unregister_from_channel_t unregister_from_channel; + //@} +}; +scs_check_size(scs_telemetry_init_params_v100_t, 32, 64); + +/** + * @brief Initialization parameters for the 1.01 version of the telemetry API. + */ +typedef scs_telemetry_init_params_v100_t scs_telemetry_init_params_v101_t; + +// Functions which should be exported by the dynamic library serving as +// recipient of the telemetry. + +/** + * @brief Initializes telemetry support. + * + * This function must be provided by the library if it wants to support telemetry API. + * + * The engine will call this function with API versions it supports starting from the latest + * until the function returns SCS_RESULT_ok or error other than SCS_RESULT_unsupported or it + * runs out of supported versions. + * + * At the time this function is called, the telemetry is in the paused state. + * + * @param version Version of the API to initialize. + * @param params Structure with additional initialization data specific to the specified API version. + * @return SCS_RESULT_ok if version is supported and library was initialized. Error code otherwise. + */ +SCSAPI_RESULT scs_telemetry_init (const scs_u32_t version, const scs_telemetry_init_params_t *const params); + +/** + * @brief Shuts down the telemetry support. + * + * The engine will call this function if available and if the scs_telemetry_init indicated + * success. + */ +SCSAPI_VOID scs_telemetry_shutdown (void); + +SCSSDK_FOOTER + +#endif // SCSSDK_TELEMETRY_H + +/* eof */ diff --git a/linux/sdk/include/scssdk_telemetry_channel.h b/linux/sdk/include/scssdk_telemetry_channel.h new file mode 100644 index 000000000..019fe4f5c --- /dev/null +++ b/linux/sdk/include/scssdk_telemetry_channel.h @@ -0,0 +1,104 @@ +/** + * @file scssdk_telemetry_channel.h + * + * @brief Telemetry SDK - channels. + */ +#ifndef SCSSDK_TELEMETRY_CHANNEL_H +#define SCSSDK_TELEMETRY_CHANNEL_H + +#include "scssdk.h" +#include "scssdk_value.h" + +SCSSDK_HEADER + +/** + * @name Telemetry channel flags. + */ +//@{ + +/** + * @brief No specific flags. + */ +const scs_u32_t SCS_TELEMETRY_CHANNEL_FLAG_none = 0x00000000; + +/** + * @brief Call the callback even if the value did not change. + * + * The default behavior is to only call the callback if the + * value changes. Note that there might be some special situations + * where the callback might be called even if the value did not + * change and this flag is not present. For example when the + * provider of the channel value is reconfigured or when the value + * changes so frequently that filtering would be only waste of time. + * + * Note that even this flag does not guarantee that the + * callback will be called. For example it might be not called + * when the value is currently unavailable and the + * SCS_TELEMETRY_CHANNEL_FLAG_no_value flag was not provided. + */ +const scs_u32_t SCS_TELEMETRY_CHANNEL_FLAG_each_frame = 0x00000001; + +/** + * @brief Call the callback even if the value is currently + * unavailable. + * + * By default the callback is only called when the value is + * available. If this flag is specified, the callback will be + * called even when the value is unavailable. In that case + * the value parameter of the callback will be set to NULL. + */ +const scs_u32_t SCS_TELEMETRY_CHANNEL_FLAG_no_value = 0x00000002; + +//@} + +/** + * @brief Type of function registered to be called with value of single telemetry channel. + * + * @param name Name of the channel. Intended for debugging purposes only. + * @param index Index of entry for array-like channels. + * @param value Current value of the channel. Will use the type provided during the registration. + * Will be NULL if and only if the SCS_TELEMETRY_CHANNEL_FLAG_no_value flag was specified + * during registration and the value is currently unavailable. + * @param context Context information passed during callback registration. + */ +typedef SCSAPI_VOID_FPTR(scs_telemetry_channel_callback_t)(const scs_string_t name, const scs_u32_t index, const scs_value_t *const value, const scs_context_t context); + +/** + * @brief Registers callback to be called with value of specified telemetry channel. + * + * At most one callback can be registered for each combination of channel name, index and type. + * + * Note that order in which the registered callbacks are called is undefined. + * + * This funtion can be called from scs_telemetry_init or from within any + * event (NOT channel) callback. + * + * @param name Name of channel to register to. + * @param index Index of entry for array-like channels. Set to SCS_U32_NIL for normal channels. + * @param type Desired type of the value. Only some types are supported (see documentation of specific channel). If the channel can not be returned using that type a SCS_RESULT_unsupported_type will be returned. + * @param flags Flags controlling delivery of the channel. + * @param callback Callback to register. + * @param context Context value passed to the callback. + * @return SCS_RESULT_ok on successful registration. Error code otherwise. + */ +typedef SCSAPI_RESULT_FPTR(scs_telemetry_register_for_channel_t)(const scs_string_t name, const scs_u32_t index, const scs_value_type_t type, const scs_u32_t flags, const scs_telemetry_channel_callback_t callback, const scs_context_t context); + +/** + * @brief Unregisters callback registered for specified telemetry channel. + * + * This function can be called from scs_telemetry_shutdown, scs_telemetry_init + * or from within any event (NOT channel) callback. Any channel left registered + * after scs_telemetry_shutdown ends will be unregistered automatically. + * + * @param name Name of channel to register from. + * @param index Index of entry for array-like channels. Set to SCS_U32_NIL for normal channels. + * @param type Type of value to unregister from. + * @return SCS_RESULT_ok on successful unregistration. Error code otherwise. + */ +typedef SCSAPI_RESULT_FPTR(scs_telemetry_unregister_from_channel_t)(const scs_string_t name, const scs_u32_t index, const scs_value_type_t type); + +SCSSDK_FOOTER + +#endif // SCSSDK_TELEMETRY_CHANNEL_H + +/* eof */ diff --git a/linux/sdk/include/scssdk_telemetry_event.h b/linux/sdk/include/scssdk_telemetry_event.h new file mode 100644 index 000000000..5864f4c3a --- /dev/null +++ b/linux/sdk/include/scssdk_telemetry_event.h @@ -0,0 +1,239 @@ +/** + * @file scssdk_telemetry_event.h + * + * @brief Telemetry SDK - events. + */ +#ifndef SCSSDK_TELEMETRY_EVENT_H +#define SCSSDK_TELEMETRY_EVENT_H + +#include "scssdk.h" + +SCSSDK_HEADER + +typedef scs_u32_t scs_event_t; + +/** + * @name Telemetry event types. + */ +//@{ + +/** + * @brief Used to mark invalid value of event type. + */ +const scs_event_t SCS_TELEMETRY_EVENT_invalid = 0; + +/** + * @brief Generated before any telemetry data for current frame. + * + * The event_info parameter for this event points to + * scs_telemetry_frame_start_t structure. + */ +const scs_event_t SCS_TELEMETRY_EVENT_frame_start = 1; + +/** + * @brief Generated after all telemetry data for current frame. + */ +const scs_event_t SCS_TELEMETRY_EVENT_frame_end = 2; + +/** + * @brief Indicates that the game entered paused state (e.g. menu) + * + * If the recipient generates some form of force feedback effects, + * it should probably stop them until SCS_TELEMETRY_EVENT_started + * event is received. + * + * After sending this event, the game stop sending telemetry data + * unless specified otherwise in description of specific telemetry. + * The frame start and event events are still generated. + */ +const scs_event_t SCS_TELEMETRY_EVENT_paused = 3; + +/** + * @brief Indicates that the player is now driving. + */ +const scs_event_t SCS_TELEMETRY_EVENT_started = 4; + +/** + * @brief Provides set of attributes which change only + * in special situations (e.g. parameters of the vehicle). + * + * The event_info parameter for this event points to + * scs_telemetry_configuration_t structure. + * + * The initial configuration info is delivered to the plugin + * after its scs_telemetry_init() function succeeds and before + * any other callback is called. If the the plugin is interested + * in the configuration info, it must register for this event + * during its initialization call to ensure that it does + * not miss it. Future changes in configuration are + * delivered as described in the event sequence below. + */ +const scs_event_t SCS_TELEMETRY_EVENT_configuration = 5; + +/** + * @brief An event called when a gameplay event such as job finish happens. + * + * The event_info parameter for this event points to scs_telemetry_gameplay_event_t structure. + */ +const scs_event_t SCS_TELEMETRY_EVENT_gameplay = 6; + +//@} + +// Sequence of events during frame. +// +// @li Optionally one or more CONFIGURATION events if the configuration changed. +// @li Optionally one from PAUSED or STARTED if there was change since last frame. +// @li FRAME_START +// @li Optionally one or more GAMEPLAY events. +// @li Channel callbacks +// @li FRAME_END + +/** + * @brief Indicates that timers providing the frame timing info + * were restarted since last frame. + * + * When timer is restarted, it will start counting from zero. + */ +const scs_u32_t SCS_TELEMETRY_FRAME_START_FLAG_timer_restart = 0x00000001; + +/** + * @brief Parameters the for SCS_TELEMETRY_EVENT_frame_start event callback. + */ +struct scs_telemetry_frame_start_t +{ + /** + * @brief Additional information about this event. + * + * Combination of SCS_TELEMETRY_FRAME_START_FLAG_* values. + */ + scs_u32_t flags; + + /** + * @brief Explicit alignment for the 64 bit timestamps. + */ + scs_u32_t _padding; + + /** + * @brief Time controlling the visualization. + * + * Its step changes depending on rendering FPS. + */ + scs_timestamp_t render_time; + + /** + * @brief Time controlling the physical simulation. + * + * Usually changes with fixed size steps so it oscilates + * around the render time. This value changes even if the + * physics simulation is currently paused. + */ + scs_timestamp_t simulation_time; + + /** + * @brief Similar to simulation time however it stops + * when the physics simulation is paused. + */ + scs_timestamp_t paused_simulation_time; +}; + +scs_check_size(scs_telemetry_frame_start_t, 32, 32); + +/** + * @brief Parameters for the SCS_TELEMETRY_EVENT_configuration event callback. + */ +struct scs_telemetry_configuration_t +{ + /** + * @brief Set of logically grouped configuration parameters this + * event describes (e.g. truck configuration, trailer configuration). + * + * See SCS_TELEMETRY_CONFIGURATION_ID_* constants for the game in question. + * + * This pointer will be never NULL. + */ + scs_string_t id; + + /** + * @brief Array of individual attributes. + * + * The array is terminated by entry whose name pointer is set to NULL. + * + * Names of the attributes are the SCS_TELEMETRY_CONFIG_ATTRIBUTE_* constants + * for the game in question. + * + * This pointer will be never NULL. + */ + const scs_named_value_t *attributes; +}; + +scs_check_size(scs_telemetry_configuration_t, 8, 16); + +/** + * @brief Parameters for the SCS_TELEMETRY_EVENT_gameplay event callback. + */ +struct scs_telemetry_gameplay_event_t +{ + /** + * @brief The event id. + * + * The event ID name - check SCS_TELEMETRY_GAMEPLAY_EVENT_* for possible names. + */ + scs_string_t id; + + /** + * @brief Array of individual attributes. + * + * The array is terminated by entry whose name pointer is set to NULL. + * + * Names of the attributes are the SCS_TELEMETRY_GAMEPLAY_EVENT_ATTRIBUTE_* constants + * for the game in question. + * + * This pointer will be never NULL. + */ + const scs_named_value_t *attributes; +}; + +scs_check_size(scs_telemetry_gameplay_event_t, 8, 16); + +/** + * @brief Type of function registered to be called for event. + * + * @param event Event in question. Allows use of single callback with more than one event. + * @param event_info Structure with additional event information about the event. + * @param context Context information passed during callback registration. + */ +typedef SCSAPI_VOID_FPTR(scs_telemetry_event_callback_t)(const scs_event_t event, const void *const event_info, const scs_context_t context); + +/** + * @brief Registers callback to be called when specified event happens. + * + * At most one callback can be registered for each event. + * + * This funtion can be called from scs_telemetry_init or from within any + * event callback other than the callback for the event itself. + * + * @param event Event to register for. + * @param callback Callback to register. + * @param context Context value passed to the callback. + * @return SCS_RESULT_ok on successful registration. Error code otherwise. + */ +typedef SCSAPI_RESULT_FPTR(scs_telemetry_register_for_event_t)(const scs_event_t event, const scs_telemetry_event_callback_t callback, const scs_context_t context); + +/** + * @brief Unregisters callback registered for specified event. + * + * This function can be called from scs_telemetry_shutdown, scs_telemetry_init + * or from within any event callback. Including callback of the event itself. + * Any event left registered after scs_telemetry_shutdown ends will + * be unregistered automatically. + * + * @param event Event to unregister from. + * @return SCS_RESULT_ok on successful unregistration. Error code otherwise. + */ +typedef SCSAPI_RESULT_FPTR(scs_telemetry_unregister_from_event_t)(const scs_event_t event); + +SCSSDK_FOOTER + +#endif // SCSSDK_TELEMETRY_EVENT_H + +/* eof */ diff --git a/linux/sdk/include/scssdk_value.h b/linux/sdk/include/scssdk_value.h new file mode 100644 index 000000000..364aebb9f --- /dev/null +++ b/linux/sdk/include/scssdk_value.h @@ -0,0 +1,247 @@ +/** + * @file scssdk_value.h + * + * @brief Structures representing varying type values in the SDK. + */ +#ifndef SCSSDK_VALUE_H +#define SCSSDK_VALUE_H + +#include "scssdk.h" + +SCSSDK_HEADER + +typedef scs_u32_t scs_value_type_t; +const scs_value_type_t SCS_VALUE_TYPE_INVALID = 0; +const scs_value_type_t SCS_VALUE_TYPE_bool = 1; +const scs_value_type_t SCS_VALUE_TYPE_s32 = 2; +const scs_value_type_t SCS_VALUE_TYPE_u32 = 3; +const scs_value_type_t SCS_VALUE_TYPE_u64 = 4; +const scs_value_type_t SCS_VALUE_TYPE_float = 5; +const scs_value_type_t SCS_VALUE_TYPE_double = 6; +const scs_value_type_t SCS_VALUE_TYPE_fvector = 7; +const scs_value_type_t SCS_VALUE_TYPE_dvector = 8; +const scs_value_type_t SCS_VALUE_TYPE_euler = 9; +const scs_value_type_t SCS_VALUE_TYPE_fplacement = 10; +const scs_value_type_t SCS_VALUE_TYPE_dplacement = 11; +const scs_value_type_t SCS_VALUE_TYPE_string = 12; +const scs_value_type_t SCS_VALUE_TYPE_s64 = 13; +const scs_value_type_t SCS_VALUE_TYPE_LAST = SCS_VALUE_TYPE_s64; + +/** + * @name Simple data types. + */ +//@{ +struct scs_value_bool_t +{ + scs_u8_t value; //< Nonzero value is true, zero false. +}; + +struct scs_value_s32_t +{ + scs_s32_t value; +}; + +struct scs_value_u32_t +{ + scs_u32_t value; +}; + +struct scs_value_u64_t +{ + scs_u64_t value; +}; + +struct scs_value_s64_t +{ + scs_s64_t value; +}; + +struct scs_value_float_t +{ + scs_float_t value; +}; + +struct scs_value_double_t +{ + scs_double_t value; +}; +//@} + +/** + * @brief String value. + * + * The provided value is UTF8 encoded however in some documented + * cases only limited ASCII compatible subset might be present. + * + * The pointer is never NULL. + */ +struct scs_value_string_t +{ + scs_string_t value; +}; + +/** + * @name Vector types. + * + * In local space the X points to right, Y up and Z backwards. + * In world space the X points to east, Y up and Z south. + */ +//@{ +struct scs_value_fvector_t +{ + scs_float_t x; + scs_float_t y; + scs_float_t z; +}; + +struct scs_value_dvector_t +{ + scs_double_t x; + scs_double_t y; + scs_double_t z; +}; +//@} + +/** + * @brief Orientation of object. + */ +struct scs_value_euler_t +{ + /** + * @brief Heading. + * + * Stored in unit range where <0,1) corresponds to <0,360). + * + * The angle is measured counterclockwise in horizontal plane when looking + * from top where 0 corresponds to forward (north), 0.25 to left (west), + * 0.5 to backward (south) and 0.75 to right (east). + */ + scs_float_t heading; + + /** + * @brief Pitch + * + * Stored in unit range where <-0.25,0.25> corresponds to <-90,90>. + * + * The pitch angle is zero when in horizontal direction, + * with positive values pointing up (0.25 directly to zenith), + * and negative values pointing down (-0.25 directly to nadir). + */ + scs_float_t pitch; + + /** + * @brief Roll + * + * Stored in unit range where <-0.5,0.5> corresponds to <-180,180>. + * + * The angle is measured in counterclockwise when looking in direction of + * the roll axis. + */ + scs_float_t roll; +}; + +/** + * @name Combination of position and orientation. + */ +//@{ +struct scs_value_fplacement_t +{ + scs_value_fvector_t position; + scs_value_euler_t orientation; +}; + +struct scs_value_dplacement_t +{ + scs_value_dvector_t position; + scs_value_euler_t orientation; + scs_u32_t _padding; // Explicit padding. +}; +//@} + +/** + * @brief Varying type storage for values. + */ +struct scs_value_t +{ + /** + * @brief Type of the value. + */ + scs_value_type_t type; + + /** + * @brief Explicit alignment for the union. + */ + scs_u32_t _padding; + + /** + * @brief Storage. + */ + union { + scs_value_bool_t value_bool; + scs_value_s32_t value_s32; + scs_value_u32_t value_u32; + scs_value_u64_t value_u64; + scs_value_s64_t value_s64; + scs_value_float_t value_float; + scs_value_double_t value_double; + scs_value_fvector_t value_fvector; + scs_value_dvector_t value_dvector; + scs_value_euler_t value_euler; + scs_value_fplacement_t value_fplacement; + scs_value_dplacement_t value_dplacement; + scs_value_string_t value_string; + }; +}; + +scs_check_size(scs_value_s32_t, 4, 4); +scs_check_size(scs_value_u32_t, 4, 4); +scs_check_size(scs_value_u64_t, 8, 8); +scs_check_size(scs_value_s64_t, 8, 8); +scs_check_size(scs_value_float_t, 4, 4); +scs_check_size(scs_value_double_t, 8, 8); +scs_check_size(scs_value_fvector_t, 12, 12); +scs_check_size(scs_value_dvector_t, 24, 24); +scs_check_size(scs_value_fplacement_t, 24, 24); +scs_check_size(scs_value_dplacement_t, 40, 40); +scs_check_size(scs_value_string_t, 4, 8); +scs_check_size(scs_value_t, 48, 48); + +/** + * @brief Combination of value and its name. + */ +struct scs_named_value_t +{ + /** + * @brief Name of this value. + * + * ASCII subset of UTF-8. + */ + scs_string_t name; + + /** + * @brief Zero-based index of the value for array-like values. + * + * For non-array values it is set to SCS_U32_NIL. + */ + scs_u32_t index; + +#ifdef SCS_ARCHITECTURE_x64 + /** + * @brief Explicit 8-byte alignment for the value part. + */ + scs_u32_t _padding; +#endif + + /** + * @brief The value itself. + */ + scs_value_t value; +}; + +scs_check_size(scs_named_value_t, 56, 64); + +SCSSDK_FOOTER + +#endif // SCSSDK_VALUE_H + +/* eof */ diff --git a/linux/sdk/readme.txt b/linux/sdk/readme.txt new file mode 100644 index 000000000..accf05d7f --- /dev/null +++ b/linux/sdk/readme.txt @@ -0,0 +1,56 @@ +========== Introduction ========== + +This SDK provides information and APIs necessary for interaction between a third party software and the Euro Truck Simulator 2 (ETS2), American Truck Simulator (ATS) and possible other future games from SCS Software. At this time the only supported API is telemetry output. In the future additional APIs might be implemented. + +========== Plugin modules ========== + +The plugins are implemented as a dynamically loaded libraries (DLL on Windows, SO on Linux) which MUST export set of functions required by the APIs they want to use. The game loads all plugins found by combination of following methods: + +1) By gathering all libraries from "plugins" subdirectory of the directory containing the game binary (e.g. /bin/win_x86/plugins). +2) (Windows only) By processing all string values stored in registry key "HKEY_LOCAL_MACHINE\SOFTWARE\SCS Software\\Plugins" (GAME_NAME is "Euro Truck Simulator 2" for ETS2 and "American Truck Simulator" for ATS). When targeting 32bit version of the game on 64bit OS, the registry key is stored in "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\SCS Software\\Plugins". The data of those values are interpreted as paths to the libraries, one path per value. The names of the values are not relevant however it is recommended to use something identifying your product to avoid possible conflicts. When installing your product you can create this value including all the keys in the path if they do not exist yet to allow the game to find the plugin even if the user installs the game latter. When uninstalling your product you should remove the value. The keys can be removed if and only if they are empty. + +If one plugin is specified more than once, the behavior is undefined. When the game wants to activate specific API, it will call the initialization function for that API in all plugins which export the required set of functions for that API. When the game wants to deactivate specific API, it will call the de-initialization function in all plugins which indicated success during the initialization and which export that function. Once all APIs are de-initialized, the game might unload the plugins. The plugins MUST be written with assumption that the initialization/de-initialization cycle might happen several times for single load/unload cycle and that there might be several load/unload cycles per lifetime of the game process. + +The ETS2/ATS provides several console commands to manipulate the APIs to simplify development. Note that those commands work only if there was at least one telemetry plugin during the startup as otherwise the game might skip initializations which are only necessary when the API is active. The commands are: + +sdk reinit +Does de-initialize/initialize cycle for all APIs on all plugins. Useful if the plugin is loading some configuration from file during initialization. + +sdk unload +De-initializes all APIs and unloads all plugins. Useful if you want to build a new version of the plugin. Use "sdk reload" to load the new plugin. + +sdk reload +De-initializes all APIs, unloads all plugins, reloads all plugins and initializes the APIs. + +========== Calling conventions and threading ========== + +The APIs are not thread safe. The game calls all functions inside the plugin from single thread (main thread) and all calls back to the game (e.g. to register telemetry channel callback) MUST happen only on that thread and only while the plugin code runs as result of direct call from the game (e.g. during execution of callback registered previously as opposed to window message handler running as result of game processing its message loop). Some API functions might have additional limitations on situations in which they can be called. + +The general flow is that the plugin's initialization function registers callback functions to be called in various situations and the game the calls them back as necessary. For some APIs it is possible to modify the registrations latter when the game calls one from the callbacks. Check documentation of specific API for more information about relevant limitations. + +========== Versioning ========== + +To provide compatibility with the future changes of the API there is a version negotiation mechanism. The API initialization function in the plugin is provided with suggested version of the API. If it returns SCS_RESULT_unsupported, the game will try next lower version it supports until the initialization succeeds, fails with other return code or until the game runs out of supported API versions. + +The API version number is composed from major and minor components. Major version is increased when there is incompatible change. The minor version is increased when there is a backwards compatible change (e.g. addition of event or value type if it does not change the layout of the scs_value_t structure for existing types). + +In addition to the API version, the initialization parameter contains identification of the game and API-specific game version (e.g. there might be one version for telemetry API and different one for some future API). This information is mainly intended to be used to allow customization of the plugin behavior for specific game. As with the APi version there are both major and minor components however there are several differences. There is no negotiation mechanism and the game supports exactly one game version. Removal of telemetry channel is considered a backward compatible change as the plugin is notified about that by failure of the callback registration and can assume default value. + +========== Telemetry API ========== +Telemetry API provides output of various dynamic (e.g. truck speed) or semi-dynamic (e.g. truck parameters) information. Telemetry supports two kinds of callbacks: + +-- Events +Global callbacks called to indicate specific phase of processing (e.g. start/end of telemetry per frame data) or to notify about change in semi-dynamic data (e.g. changed truck parameters because user switched to a different truck). The event callbacks are provided with id of the event, pointer to event-specific structure with additional information and context information specified during callback registration. It is possible to register channel callbacks from within event callback. This is useful when configuration events (e.g. truck change) are received. + +-- Channels +Associated with individual sources of telemetry information (e.g. position, speed, rpm). The callback is called when value in corresponding source is changed or is assumed to be changed (e.g. for some inputs the game assumes that the value is changing most of the time anyway and will not filter the calls). The callback is provided with identification of the channel, its new value and context information specified during callback registration. Some sources can provide the value in more than one format (e.g. as double or float). In that case the callback will receive value in the format specified during callback registration. + +Note that the game might contain additional channels and configurations with "dev." prefix which are not documented. They are used for development purposes or contain unrealistic values and might be changed or removed at any time so the plugins should ignore them. If you would need information provided by those objects, please let us know. + +===== Scale ETS2/ATS + +The map scale varies depending on situation (e.g. it is different in cities than in other parts of the map and different in UK than in the continental Europe). To maintain the apparent distances, the game scales delta applied to the time-of-day and to the distance driven (e.g. odometer) by factor derived from the current situation. The current value of the scale is provided trough the SCS_TELEMETRY_CHANNEL_local_scale channel. + +===== Timing ETS2/ATS + +The rendering time advances by variable steps depending on effective FPS. This time controls the animations and processing of inputs happens at start of each rendering frame. The physics tries to run with a fixed step staying at most one step ahead of the current rendering time. Each physics step is considered a frame for telemetry purposes. For rendering purposes the physically simulated position and orientation is interpolated from two surrounding physics steps. diff --git a/linux/sdk/sdk_license.txt b/linux/sdk/sdk_license.txt new file mode 100644 index 000000000..ef8bc63f8 --- /dev/null +++ b/linux/sdk/sdk_license.txt @@ -0,0 +1,20 @@ +SCS SDK +Copyright (C) 2016 SCS Software + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/linux/server/main.py b/linux/server/main.py new file mode 100644 index 000000000..7767f024c --- /dev/null +++ b/linux/server/main.py @@ -0,0 +1,88 @@ +import threading +from http.server import SimpleHTTPRequestHandler +import socketserver +import subprocess +import json +import signal + +PORT = 3141 + +last_value = (0, 0, 0) +alive = True +httpd = None + +def start_read(): + global last_value + global alive + + read_util = subprocess.Popen(["./read_util"], stdout=subprocess.PIPE, bufsize=1, universal_newlines=True) + while read_util.poll() is None and alive: + txt = read_util.stdout.readline().strip() + if not txt: + continue + last_value = tuple([float(x) for x in txt.split(",")]) + + read_util.stdout.close() + read_util.send_signal(signal.SIGKILL) + +class CustomHandler(SimpleHTTPRequestHandler): + def __init__(self, *args): + super().__init__(*args, directory="../../web/") + + def handle_api(self): + payload = { + "Physics": { + "CoordinateX": last_value[0], + "CoordinateY": last_value[1], + "CoordinateZ": last_value[2], + } + } + + self.send_response(200) + self.send_header("Content-Type", "application/json") + self.send_header("Content-Encoding", "utf-8") + self.end_headers() + self.wfile.write(json.dumps(payload).encode("utf-8")) + + def do_GET(self): + if self.path[:4] == "/api": + self.handle_api() + elif self.path[:9] == "/commands": + self.send_response(200) + self.end_headers() + else: + super().do_GET() + +def run_server(): + global last_value + global alive + global httpd + global PORT + + with socketserver.TCPServer(("", PORT), CustomHandler) as httpd: + print("Server open on port", PORT) + httpd.serve_forever() + +def signal_handler(sig, frame): + global alive + global httpd + + print("Interrupted, shutting down") + alive = False + + if httpd is not None: + httpd.shutdown() + + read_thread.join() + server_thread.join() + + print("Done.") + +signal.signal(signal.SIGINT, signal_handler) + +read_thread = threading.Thread(target=start_read) +server_thread = threading.Thread(target=run_server) + +read_thread.start() +server_thread.start() + From 93730a56bbfcd13d3966d1ef9554bfad001e917d Mon Sep 17 00:00:00 2001 From: James Thistlewood Date: Sat, 20 Mar 2021 10:27:40 +0000 Subject: [PATCH 02/14] rename some things --- .gitignore | 2 +- linux/plugins/Makefile | 6 +++--- linux/plugins/install.sh | 2 +- linux/plugins/{telemetry_mem.cpp => plugin.cpp} | 0 linux/server/main.py | 1 + 5 files changed, 6 insertions(+), 5 deletions(-) rename linux/plugins/{telemetry_mem.cpp => plugin.cpp} (100%) diff --git a/.gitignore b/.gitignore index cff458908..fb373e9af 100644 --- a/.gitignore +++ b/.gitignore @@ -45,6 +45,6 @@ server/packages/** *.exe # Linux stuff -linux/plugins/telemetry.so +linux/plugins/ets2-telemetry-lin.so linux/plugins/read_util linux/server/read_util diff --git a/linux/plugins/Makefile b/linux/plugins/Makefile index 4a1a6d2c9..f629ff500 100644 --- a/linux/plugins/Makefile +++ b/linux/plugins/Makefile @@ -10,10 +10,10 @@ SDK_INCLUDES=\ -I../sdk/include/amtrucks/ \ -I../sdk/include/eurotrucks2 -all: telemetry.so read_util +all: ets2-telemetry-lin.so read_util -telemetry.so: telemetry_mem.cpp $(SDK_HEADERS) - g++ -o $@ -fPIC -Wall --shared -Wl,-soname,$@ $(SDK_INCLUDES) telemetry_mem.cpp -lrt +ets2-telemetry-lin.so: plugin.cpp $(SDK_HEADERS) + g++ -o $@ -fPIC -Wall --shared -Wl,-soname,$@ $(SDK_INCLUDES) plugin.cpp -lrt read_util: read_util.cpp $(SDK_HEADERS) g++ -o $@ -Wall $(SDK_INCLUDES) read_util.cpp -lrt diff --git a/linux/plugins/install.sh b/linux/plugins/install.sh index a090480fa..eadbe64da 100755 --- a/linux/plugins/install.sh +++ b/linux/plugins/install.sh @@ -1,4 +1,4 @@ #!/usr/bin/env bash cp read_util "../server" -cp telemetry.so "$HOME/.local/share/Steam/steamapps/common/Euro Truck Simulator 2/bin/linux_x64/plugins" +cp ets2-telemetry-lin.so "$HOME/.local/share/Steam/steamapps/common/Euro Truck Simulator 2/bin/linux_x64/plugins" diff --git a/linux/plugins/telemetry_mem.cpp b/linux/plugins/plugin.cpp similarity index 100% rename from linux/plugins/telemetry_mem.cpp rename to linux/plugins/plugin.cpp diff --git a/linux/server/main.py b/linux/server/main.py index 7767f024c..ed383505f 100644 --- a/linux/server/main.py +++ b/linux/server/main.py @@ -30,6 +30,7 @@ def __init__(self, *args): super().__init__(*args, directory="../../web/") def handle_api(self): + global last_value payload = { "Physics": { "CoordinateX": last_value[0], From 898186ff75f93a98eb9b63a05ef582ddbfd8fe9e Mon Sep 17 00:00:00 2001 From: James Thistlewood Date: Sat, 20 Mar 2021 14:26:10 +0000 Subject: [PATCH 03/14] add function to pause radio when turning engine off --- linux/plugins/Makefile | 2 +- linux/plugins/plugin.cpp | 22 +++++++++++++++++++++ linux/plugins/read_util.cpp | 22 +++++++++++++-------- linux/server/main.py | 36 +++++++++++++++++++++++++++++----- web/lib/js/main.js | 38 ++++++++++++++++++++++++++++++++++++ web/lib/js/remote-control.js | 3 +++ 6 files changed, 109 insertions(+), 14 deletions(-) diff --git a/linux/plugins/Makefile b/linux/plugins/Makefile index f629ff500..1c40d9f16 100644 --- a/linux/plugins/Makefile +++ b/linux/plugins/Makefile @@ -16,7 +16,7 @@ ets2-telemetry-lin.so: plugin.cpp $(SDK_HEADERS) g++ -o $@ -fPIC -Wall --shared -Wl,-soname,$@ $(SDK_INCLUDES) plugin.cpp -lrt read_util: read_util.cpp $(SDK_HEADERS) - g++ -o $@ -Wall $(SDK_INCLUDES) read_util.cpp -lrt + g++ -g -o $@ -Wall $(SDK_INCLUDES) read_util.cpp -lrt .PHONY: clean clean: diff --git a/linux/plugins/plugin.cpp b/linux/plugins/plugin.cpp index 92b9473a7..2372c50a1 100644 --- a/linux/plugins/plugin.cpp +++ b/linux/plugins/plugin.cpp @@ -32,6 +32,7 @@ bool output_paused = true; struct telemetry_state_t { scs_value_dplacement_t world_position; + scs_value_bool_t electricity; } telemetry; /** @@ -118,6 +119,26 @@ SCSAPI_VOID telemetry_store_dplacement(const scs_string_t name, const scs_u32_t *placement = value->value_dplacement; } +SCSAPI_VOID telemetry_store_bool(const scs_string_t name, const scs_u32_t index, const scs_value_t *const value, const scs_context_t context) +{ + if (!context) { + game_log(SCS_LOG_TYPE_error, "Local radio: no context!"); + return; + } + if (!value) { + game_log(SCS_LOG_TYPE_error, "Local radio: no value!"); + return; + } + if (value->type != SCS_VALUE_TYPE_bool) { + game_log(SCS_LOG_TYPE_error, "Local radio: value wrong type!"); + return; + } + + scs_value_bool_t *const setting = static_cast(context); + *setting = value->value_bool; +} + + /** * @brief Telemetry API initialization function. * @@ -204,6 +225,7 @@ SCSAPI_RESULT scs_telemetry_init(const scs_u32_t version, const scs_telemetry_in // (SCS_RESULT_unsupported_type). For purpose of this example we ignore the failues // so the unsupported channels will remain at theirs default value. version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_world_placement, SCS_U32_NIL, SCS_VALUE_TYPE_dplacement, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_dplacement, &telemetry.world_position); + version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_electric_enabled, SCS_U32_NIL, SCS_VALUE_TYPE_bool, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_bool, &telemetry.electricity); // Set the structure with defaults. memset(&telemetry, 0, sizeof(telemetry)); diff --git a/linux/plugins/read_util.cpp b/linux/plugins/read_util.cpp index cad97dd1b..110047656 100644 --- a/linux/plugins/read_util.cpp +++ b/linux/plugins/read_util.cpp @@ -17,12 +17,18 @@ #include "amtrucks/scssdk_ats.h" #include "amtrucks/scssdk_telemetry_ats.h" - static char shm_name[] = "/ets2radiolinux"; -void print_position(scs_value_dplacement_t placement) { - scs_value_dvector_t pos = placement.position; - fprintf(stdout, "%f,%f,%f\n", pos.x, pos.y, pos.z); +typedef struct telemetry_state_t +{ + scs_value_dplacement_t world_position; + scs_value_bool_t electricity; +} telemetry_state_t; + +void print_info(telemetry_state_t info) { + scs_value_dvector_t pos = info.world_position.position; + int elec = info.electricity.value > 0 ? 1 : 0; + fprintf(stdout, "%f,%f,%f;%d\n", pos.x, pos.y, pos.z, elec); fflush(stdout); } @@ -51,11 +57,11 @@ int main() { return 1; } - scs_value_dplacement_t current; - memset(¤t, 0, sizeof(scs_value_dplacement_t)); + telemetry_state_t telemetry; + memset(&telemetry, 0, sizeof(telemetry_state_t)); while (1) { - memcpy(¤t, mapped_region, MAP_SIZE); - print_position(current); + memcpy(&telemetry, mapped_region, MAP_SIZE); + print_info(telemetry); usleep(500000); } diff --git a/linux/server/main.py b/linux/server/main.py index ed383505f..574b9f2ac 100644 --- a/linux/server/main.py +++ b/linux/server/main.py @@ -4,15 +4,17 @@ import subprocess import json import signal +import time -PORT = 3141 +PORT = 3142 last_value = (0, 0, 0) +has_elec = [False, False] # current, last read by command handler alive = True httpd = None def start_read(): - global last_value + global last_value, has_elec global alive read_util = subprocess.Popen(["./read_util"], stdout=subprocess.PIPE, bufsize=1, universal_newlines=True) @@ -20,7 +22,10 @@ def start_read(): txt = read_util.stdout.readline().strip() if not txt: continue - last_value = tuple([float(x) for x in txt.split(",")]) + + parts = txt.split(";") + last_value = tuple([float(x) for x in parts[0].split(",")]) + has_elec[0] = int(parts[1]) > 0 read_util.stdout.close() read_util.send_signal(signal.SIGKILL) @@ -45,12 +50,30 @@ def handle_api(self): self.end_headers() self.wfile.write(json.dumps(payload).encode("utf-8")) + def handle_cmd(self): + global has_elec + + payload = { + "id": time.time(), + "language": "en-GB", + "game": "ets2", + } + + if has_elec[0] != has_elec[1]: + has_elec[1] = has_elec[0] + payload["action"] = "engineOn" if has_elec[0] else "engineOff" + + self.send_response(200) + self.send_header("Content-Type", "application/json") + self.send_header("Content-Encoding", "utf-8") + self.end_headers() + self.wfile.write(json.dumps(payload).encode("utf-8")) + def do_GET(self): if self.path[:4] == "/api": self.handle_api() elif self.path[:9] == "/commands": - self.send_response(200) - self.end_headers() + self.handle_cmd() else: super().do_GET() @@ -68,6 +91,9 @@ def signal_handler(sig, frame): global alive global httpd + if not alive: + return + print("Interrupted, shutting down") alive = False diff --git a/web/lib/js/main.js b/web/lib/js/main.js index 42f0a3434..94c73c1ce 100644 --- a/web/lib/js/main.js +++ b/web/lib/js/main.js @@ -145,6 +145,13 @@ function initialise() { } } } + + if (data.action === "engineOff") { + setPlay(false, false); + } + if (data.action === "engineOn") { + setPlay(true, false); + } } }); }, 250); @@ -495,6 +502,37 @@ function togglePlay(fromRemote) { } } +function setPlay(value, fromRemote) { + if(typeof fromRemote === "undefined") fromRemote = false; + if(controlRemote){ + if(!fromRemote) { + conn.send(JSON.stringify({ + type: "setPlay", + value: value, + })); + } + togglePlayVisual(!value); + } else { + if (value) { + var src = document.getElementById('player').src; + document.getElementById('player').src = "about:blank"; + document.getElementById('player').src = src; + document.getElementById('player').play(); + document.getElementById('whitenoise').play(); + } else { + document.getElementById('player').pause(); + document.getElementById('whitenoise').pause(); + } + togglePlayVisual(!value); + if(!fromRemote && conn !== null && conn.open) { + conn.send(JSON.stringify({ + type: "setPlay", + value: value, + })); + } + } +} + function togglePlayVisual(showPause) { if(showPause){ $("#stopPlayback").attr("src", "lib/img/stop-button.png"); diff --git a/web/lib/js/remote-control.js b/web/lib/js/remote-control.js index 60272f7b3..ebfcad426 100644 --- a/web/lib/js/remote-control.js +++ b/web/lib/js/remote-control.js @@ -89,6 +89,9 @@ function receiveCommand(data) { if(response.type == "togglePlay"){ togglePlay(true); } + if(response.type == "setPlay"){ + setPlay(response.value, true); + } } function connect(peerID) { From 935882a3bd6848eda6e6a11de06393d074671103 Mon Sep 17 00:00:00 2001 From: James Thistlewood Date: Sat, 20 Mar 2021 15:00:55 +0000 Subject: [PATCH 04/14] allow specification of port through command line --- linux/server/main.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/linux/server/main.py b/linux/server/main.py index 574b9f2ac..758f98e4e 100644 --- a/linux/server/main.py +++ b/linux/server/main.py @@ -5,8 +5,7 @@ import json import signal import time - -PORT = 3142 +import argparse last_value = (0, 0, 0) has_elec = [False, False] # current, last read by command handler @@ -110,6 +109,12 @@ def signal_handler(sig, frame): read_thread = threading.Thread(target=start_read) server_thread = threading.Thread(target=run_server) +parser = argparse.ArgumentParser() +parser.add_argument("--port", type=int, default=3141) + +args = parser.parse_args() +PORT = args.port + read_thread.start() server_thread.start() From b163fbae748bbf5ca4208365e0b7d381808f8075 Mon Sep 17 00:00:00 2001 From: James Thistlewood Date: Sat, 20 Mar 2021 15:02:16 +0000 Subject: [PATCH 05/14] remove debug flags, add optimization --- linux/plugins/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linux/plugins/Makefile b/linux/plugins/Makefile index 1c40d9f16..9ee14bece 100644 --- a/linux/plugins/Makefile +++ b/linux/plugins/Makefile @@ -13,10 +13,10 @@ SDK_INCLUDES=\ all: ets2-telemetry-lin.so read_util ets2-telemetry-lin.so: plugin.cpp $(SDK_HEADERS) - g++ -o $@ -fPIC -Wall --shared -Wl,-soname,$@ $(SDK_INCLUDES) plugin.cpp -lrt + g++ -O2 -o $@ -fPIC -Wall --shared -Wl,-soname,$@ $(SDK_INCLUDES) plugin.cpp -lrt read_util: read_util.cpp $(SDK_HEADERS) - g++ -g -o $@ -Wall $(SDK_INCLUDES) read_util.cpp -lrt + g++ -O2 -o $@ -Wall $(SDK_INCLUDES) read_util.cpp -lrt .PHONY: clean clean: From baee534123929f5d333a8ca355c4dd24d377c24b Mon Sep 17 00:00:00 2001 From: James Thistlewood Date: Sat, 20 Mar 2021 17:00:17 +0000 Subject: [PATCH 06/14] improve docs --- linux/plugins/README.md | 12 ++++++++++-- linux/plugins/plugin.cpp | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/linux/plugins/README.md b/linux/plugins/README.md index b0c4c1789..540f9c6b3 100644 --- a/linux/plugins/README.md +++ b/linux/plugins/README.md @@ -1,14 +1,18 @@ -# ETS2 Local Radio unofficial Linux port +# ETS2 Local Radio Linux port ## Installation instructions +To build and install the plugin and utility script, you will need GCC and Make (these come standard with a lot of distributions). + In this directory, run the command: ```bash make all && ./install.sh ``` -You will need to copy the plugin `telemetry.so` manually to your plugins directory if ETS is not installed via Steam on your main partition, or if you are using ATS. +If you get errors, you probably need to install a dependency. + +You will need to copy the plugin `ets2-telemetry-lin.so` manually to your plugins directory if ETS is not installed via Steam on your main partition, or if you are using ATS. ## Running @@ -17,3 +21,7 @@ Go to the `server` directory and run: ```bash python3 main.py ``` + +This will by default run at `localhost:3141`, but feel free to change the port by adding the `--port` flag. + +Then just visit `localhost:3141` (or your specified) port to view the web interface! diff --git a/linux/plugins/plugin.cpp b/linux/plugins/plugin.cpp index 2372c50a1..69a303853 100644 --- a/linux/plugins/plugin.cpp +++ b/linux/plugins/plugin.cpp @@ -1,5 +1,5 @@ /** - * @brief TODO + * @brief This plugin hooks into the telemetry API for ETS2, and writes it to a shm file. */ #include From 78c1bbce251b492f769d7feb45bc5b7f2ab4e96c Mon Sep 17 00:00:00 2001 From: James Thistlewood Date: Sat, 20 Mar 2021 20:53:56 +0000 Subject: [PATCH 07/14] move shared definitions to defs.h --- linux/plugins/Makefile | 4 ++-- linux/plugins/defs.h | 20 ++++++++++++++++++++ linux/plugins/plugin.cpp | 18 +++--------------- linux/plugins/read_util.cpp | 12 +++--------- 4 files changed, 28 insertions(+), 26 deletions(-) create mode 100644 linux/plugins/defs.h diff --git a/linux/plugins/Makefile b/linux/plugins/Makefile index 9ee14bece..55aaa0ecd 100644 --- a/linux/plugins/Makefile +++ b/linux/plugins/Makefile @@ -12,10 +12,10 @@ SDK_INCLUDES=\ all: ets2-telemetry-lin.so read_util -ets2-telemetry-lin.so: plugin.cpp $(SDK_HEADERS) +ets2-telemetry-lin.so: plugin.cpp $(SDK_HEADERS) defs.h g++ -O2 -o $@ -fPIC -Wall --shared -Wl,-soname,$@ $(SDK_INCLUDES) plugin.cpp -lrt -read_util: read_util.cpp $(SDK_HEADERS) +read_util: read_util.cpp $(SDK_HEADERS) defs.h g++ -O2 -o $@ -Wall $(SDK_INCLUDES) read_util.cpp -lrt .PHONY: clean diff --git a/linux/plugins/defs.h b/linux/plugins/defs.h new file mode 100644 index 000000000..7fe0ecd41 --- /dev/null +++ b/linux/plugins/defs.h @@ -0,0 +1,20 @@ +#ifndef __DEFS__ +#define __DEFS__ + +// SDK +#include "scssdk_telemetry.h" +#include "eurotrucks2/scssdk_eut2.h" +#include "eurotrucks2/scssdk_telemetry_eut2.h" +#include "amtrucks/scssdk_ats.h" +#include "amtrucks/scssdk_telemetry_ats.h" + +const char shm_name[] = "/ets2radiolinux"; +const int NUM_PAGES = 1; + +typedef struct telemetry_state_t +{ + scs_value_dplacement_t world_position; + scs_value_bool_t electricity; +} telemetry_state_t; + +#endif /* __DEFS__ */ diff --git a/linux/plugins/plugin.cpp b/linux/plugins/plugin.cpp index 69a303853..dc1b2fb25 100644 --- a/linux/plugins/plugin.cpp +++ b/linux/plugins/plugin.cpp @@ -11,13 +11,7 @@ #include #include -// SDK - -#include "scssdk_telemetry.h" -#include "eurotrucks2/scssdk_eut2.h" -#include "eurotrucks2/scssdk_telemetry_eut2.h" -#include "amtrucks/scssdk_ats.h" -#include "amtrucks/scssdk_telemetry_ats.h" +#include "defs.h" #define UNUSED(x) @@ -29,11 +23,7 @@ bool output_paused = true; /** * @brief Useful telemetry data. */ -struct telemetry_state_t -{ - scs_value_dplacement_t world_position; - scs_value_bool_t electricity; -} telemetry; +telemetry_state_t telemetry; /** * @brief Time since last mmap @@ -48,8 +38,6 @@ static scs_timestamp_t update_interval = 5e5; // every 0.5s void* mapped_region = NULL; size_t MAP_SIZE = -1; -static char shm_name[] = "/ets2radiolinux"; - /** * @brief Function writing message to the game internal log. */ @@ -231,7 +219,7 @@ SCSAPI_RESULT scs_telemetry_init(const scs_u32_t version, const scs_telemetry_in memset(&telemetry, 0, sizeof(telemetry)); // We only need one page of memory - MAP_SIZE = getpagesize(); + MAP_SIZE = getpagesize() * NUM_PAGES; // Open shared memory int handle = shm_open(shm_name, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); diff --git a/linux/plugins/read_util.cpp b/linux/plugins/read_util.cpp index 110047656..d9f4135b1 100644 --- a/linux/plugins/read_util.cpp +++ b/linux/plugins/read_util.cpp @@ -9,6 +9,8 @@ #include #include +#include "defs.h" + // SDK #include "scssdk_telemetry.h" @@ -17,14 +19,6 @@ #include "amtrucks/scssdk_ats.h" #include "amtrucks/scssdk_telemetry_ats.h" -static char shm_name[] = "/ets2radiolinux"; - -typedef struct telemetry_state_t -{ - scs_value_dplacement_t world_position; - scs_value_bool_t electricity; -} telemetry_state_t; - void print_info(telemetry_state_t info) { scs_value_dvector_t pos = info.world_position.position; int elec = info.electricity.value > 0 ? 1 : 0; @@ -34,7 +28,7 @@ void print_info(telemetry_state_t info) { int main() { // We only need one page of memory - size_t MAP_SIZE = getpagesize(); + size_t MAP_SIZE = getpagesize() * NUM_PAGES; // Open shared memory int handle = shm_open(shm_name, O_RDONLY, S_IRUSR); From 3c3f407e7df67445e924ef04ed7e1d4a013d2b6f Mon Sep 17 00:00:00 2001 From: James Thistlewood Date: Sat, 20 Mar 2021 21:02:29 +0000 Subject: [PATCH 08/14] make install script safer --- linux/plugins/README.md | 2 +- linux/plugins/install.sh | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/linux/plugins/README.md b/linux/plugins/README.md index 540f9c6b3..a80a1ae0c 100644 --- a/linux/plugins/README.md +++ b/linux/plugins/README.md @@ -24,4 +24,4 @@ python3 main.py This will by default run at `localhost:3141`, but feel free to change the port by adding the `--port` flag. -Then just visit `localhost:3141` (or your specified) port to view the web interface! +Then just visit `localhost:3141` (or your specified port) to view the web interface! diff --git a/linux/plugins/install.sh b/linux/plugins/install.sh index eadbe64da..4730a8c28 100755 --- a/linux/plugins/install.sh +++ b/linux/plugins/install.sh @@ -1,4 +1,16 @@ #!/usr/bin/env bash cp read_util "../server" -cp ets2-telemetry-lin.so "$HOME/.local/share/Steam/steamapps/common/Euro Truck Simulator 2/bin/linux_x64/plugins" + +if [ ! -d "$HOME/.local/share/Steam/steamapps/common/Euro Truck Simulator 2/bin/linux_x64/" ]; then + echo "WARN: Could not find ETS2 installation! Please manually move ets2-telemetry-lin.so to your plugins directory." + echo "Installed with partial success" + exit 1 +else + mkdir -p "$HOME/.local/share/Steam/steamapps/common/Euro Truck Simulator 2/bin/linux_x64/plugins" + mkdir -p "$HOME/.local/share/Steam/steamapps/common/Euro Truck Simulator 2/bin/linux_x86/plugins" + cp ets2-telemetry-lin.so "$HOME/.local/share/Steam/steamapps/common/Euro Truck Simulator 2/bin/linux_x64/plugins" + cp ets2-telemetry-lin.so "$HOME/.local/share/Steam/steamapps/common/Euro Truck Simulator 2/bin/linux_x86/plugins" +fi + +echo "Installed successfully" From 12509786ce53601552dd84175899e5bbcee25ccd Mon Sep 17 00:00:00 2001 From: James Thistlewood Date: Wed, 24 Mar 2021 18:04:19 +0000 Subject: [PATCH 09/14] stability improvements --- linux/plugins/read_util.cpp | 16 ++++++-- linux/plugins/uninstall.sh | 14 +++++++ linux/server/main.py | 77 ++++++++++++++++++++++++++++--------- 3 files changed, 84 insertions(+), 23 deletions(-) create mode 100755 linux/plugins/uninstall.sh diff --git a/linux/plugins/read_util.cpp b/linux/plugins/read_util.cpp index d9f4135b1..ad7014371 100644 --- a/linux/plugins/read_util.cpp +++ b/linux/plugins/read_util.cpp @@ -1,5 +1,10 @@ /** * @brief Utility that will output the value of the position in the shared memory. + * + * Return codes: + * 0: ok - exited normally (currently not reachable) + * 1: error - could not open shared memory + * 2: error - failed to mmap shared memory */ #include @@ -19,11 +24,14 @@ #include "amtrucks/scssdk_ats.h" #include "amtrucks/scssdk_telemetry_ats.h" +#define ERR_COULD_NOT_OPEN_MEMORY 1 +#define ERR_MMAP_FAILED 2 + void print_info(telemetry_state_t info) { scs_value_dvector_t pos = info.world_position.position; int elec = info.electricity.value > 0 ? 1 : 0; - fprintf(stdout, "%f,%f,%f;%d\n", pos.x, pos.y, pos.z, elec); - fflush(stdout); + fprintf(stdout, "INFO %f,%f,%f;%d\n", pos.x, pos.y, pos.z, elec); + fflush(stdout); } int main() { @@ -34,7 +42,7 @@ int main() { int handle = shm_open(shm_name, O_RDONLY, S_IRUSR); if (handle < 0) { fprintf(stderr, "ERR Could not open shared memory\n"); - return 1; + return ERR_COULD_NOT_OPEN_MEMORY; } void* mapped_region = mmap( @@ -48,7 +56,7 @@ int main() { if (mapped_region == MAP_FAILED) { fprintf(stderr, "ERR Could not mmap shared memory\n"); - return 1; + return ERR_MMAP_FAILED; } telemetry_state_t telemetry; diff --git a/linux/plugins/uninstall.sh b/linux/plugins/uninstall.sh new file mode 100755 index 000000000..8cbbdf4d0 --- /dev/null +++ b/linux/plugins/uninstall.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +rm -f -- ../server/read_util + +if [ ! -d "$HOME/.local/share/Steam/steamapps/common/Euro Truck Simulator 2/bin/linux_x64/" ]; then + echo "WARN: Could not find ETS2 installation! Please manually move ets2-telemetry-lin.so to your plugins directory." + echo "Uninstalled with partial success" + exit 1 +else + rm -f -- "$HOME/.local/share/Steam/steamapps/common/Euro Truck Simulator 2/bin/linux_x64/plugins/ets2-telemetry-lin.so" + rm -f -- "$HOME/.local/share/Steam/steamapps/common/Euro Truck Simulator 2/bin/linux_x86/plugins/ets2-telemetry-lin.so" +fi + +echo "Uninstalled successfully" diff --git a/linux/server/main.py b/linux/server/main.py index 758f98e4e..fea2ccde0 100644 --- a/linux/server/main.py +++ b/linux/server/main.py @@ -16,18 +16,43 @@ def start_read(): global last_value, has_elec global alive - read_util = subprocess.Popen(["./read_util"], stdout=subprocess.PIPE, bufsize=1, universal_newlines=True) - while read_util.poll() is None and alive: - txt = read_util.stdout.readline().strip() - if not txt: - continue - - parts = txt.split(";") - last_value = tuple([float(x) for x in parts[0].split(",")]) - has_elec[0] = int(parts[1]) > 0 - - read_util.stdout.close() - read_util.send_signal(signal.SIGKILL) + timeout = 1 + + while alive: + try: + read_util = subprocess.Popen(["./read_util"], stdout=subprocess.PIPE, bufsize=1, universal_newlines=True) + except Exception as e: + print("Couldn't start read util:", e) + alive = False + break + + while read_util.poll() is None and alive: + txt = read_util.stdout.readline().strip() + if not txt: + continue + + if txt[:4] != "INFO": + continue + + # reset timeout + timeout = 1 + + txt = txt[5:] + parts = txt.split(";") + last_value = tuple([float(x) for x in parts[0].split(",")]) + has_elec[0] = int(parts[1]) > 0 + + if read_util.returncode == 1: + print(f"Your game doesn't seem to be running, trying again in 5 seconds...") + time.sleep(5) + elif read_util.returncode > 0: + # Generic error handling + print(f"Read util encountered an error, trying again in {timeout} seconds...") + time.sleep(timeout) + timeout *= 2 + + read_util.stdout.close() + read_util.send_signal(signal.SIGKILL) class CustomHandler(SimpleHTTPRequestHandler): def __init__(self, *args): @@ -76,24 +101,38 @@ def do_GET(self): else: super().do_GET() + # Comment this out if you want to debug issues - for now this just prevents the + # handler cluttering stdout with logs. + def log_message(self, format, *args): + pass + def run_server(): global last_value global alive global httpd global PORT - with socketserver.TCPServer(("", PORT), CustomHandler) as httpd: - print("Server open on port", PORT) - httpd.serve_forever() - -def signal_handler(sig, frame): + while alive: + try: + with socketserver.TCPServer(("", PORT), CustomHandler) as httpd: + print(f"Server open on port {PORT}, visit it at: http://localhost:{PORT}/") + httpd.serve_forever() + except OSError as e: + if e.errno == 98: # Address in use + print("That port is already in use (possibly from previous run of this script)") + print("Waiting 5 seconds before trying again...") + time.sleep(5) + else: + raise # reraise + +def shutdown(sig=None, frame=None): global alive global httpd if not alive: return - print("Interrupted, shutting down") + print("Interrupted, shutting down...") alive = False if httpd is not None: @@ -104,7 +143,7 @@ def signal_handler(sig, frame): print("Done.") -signal.signal(signal.SIGINT, signal_handler) +signal.signal(signal.SIGINT, shutdown) read_thread = threading.Thread(target=start_read) server_thread = threading.Thread(target=run_server) From 6f2aefa25daa76764973a5ff55d93e370533a7fa Mon Sep 17 00:00:00 2001 From: James Thistlewood Date: Wed, 24 Mar 2021 20:26:46 +0000 Subject: [PATCH 10/14] fix read util buffer overflow --- linux/plugins/read_util.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/plugins/read_util.cpp b/linux/plugins/read_util.cpp index ad7014371..ab4019f0a 100644 --- a/linux/plugins/read_util.cpp +++ b/linux/plugins/read_util.cpp @@ -62,7 +62,7 @@ int main() { telemetry_state_t telemetry; memset(&telemetry, 0, sizeof(telemetry_state_t)); while (1) { - memcpy(&telemetry, mapped_region, MAP_SIZE); + memcpy(&telemetry, mapped_region, sizeof(telemetry_state_t)); print_info(telemetry); usleep(500000); } From 2f0dcc534397f6bb1e9069a5ef16d42fe1d51fa5 Mon Sep 17 00:00:00 2001 From: James Thistlewood Date: Thu, 25 Mar 2021 18:00:52 +0000 Subject: [PATCH 11/14] add dummy shm writer for debug purposes --- linux/plugins/dummy | Bin 0 -> 17392 bytes linux/plugins/dummy.cpp | 111 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100755 linux/plugins/dummy create mode 100644 linux/plugins/dummy.cpp diff --git a/linux/plugins/dummy b/linux/plugins/dummy new file mode 100755 index 0000000000000000000000000000000000000000..108b06c12fda80b4b4c6560b7411d9e1928b9af4 GIT binary patch literal 17392 zcmeHPeQ;FO6~CKJAo8)hC}_YcuNa&}VM!oh{GjfJB)oJML`W2^_*iyd$d>(b_pO3j z8z$he6QZdzwm4I#ICZKWXEgqyqjuCqgo;irI31-rR@$j!LjAy~3`ODfoO|zidHa&B z9jAYErh7AQ&pp3;&bjxV`?2@#yEi{=3$<6cT#SO7{eU5N>^y-4WJ1F=G6E#Pn%Q*t zoXaj|lYp<}m`D!@0;wV#tth6|5_bbdy&8;6K))woMwCj36!l7l>T^U2QRWjo>Q!M> z@=n^t6B#2)=~jLUDnn5Cr$yJx`VqD3p>NhN6!Pnr7t;di$@-HX!l;*)dTFUglu13J z6=25RQDGQU#o@VN9RQJ2hMQ9QP3S<|9;bYVP}%xzt`wRzdXWljEU%D^3V`cVTlB5ToYA_)N)T`p>t$ZQRr_`$*k04_x|;yW!SXUVQh*WCQa=84RdTO-#HO zA}GV_?|Ovwf(T+%$0PciiSDkj@5<4B8ZMAp9Hs%Q6hoYQ7(UOP0KdKjes2l<(GvLG zCGevq@ZXle>i}2aafTEC6P5qw68KXk@K;LUR{*ZS;|zxYnCAq6{{=iV*c_$=L?Vr| z-$^{+z!~(jjDh{Ck!c;BtF)+|(KpAkhMwtMwIZHM>Yd?Dah++}=0qx~WsPvg&@>@B zpozjEWF%~$bub*+f~eLVj>TEl zh{nK#kug$HmPmxttb4O=WEp&`onJ^h}0z-;s%N6OpZ9tvi+s$1zdUFjHCH zL>P>%40W`u&=&g_`WR ze1?#(4oE5pBmU^M0>H5hMXXv1l)!<0QX9OpF(du=%8LE&*5ZlBkOY&iKEb)K`~Q^*L= zf(@T)!(X%EIQLOFY{Q`=ltbAAWe=1+Q1(FC1OJyE_`-YfX|?a;YPG-W&x;sScNUBa z^RU|YYW05Z4|C~SfJe>w>wtLYGL-MZl+lsDnx;9(d0fbhjvTRgTriA|?6Y`WD2$Fg zZSlB37#(@U;&EXxI`T7%#|6RY$aagzMabyLZ5EFUgVB+&#pA+gbmV&Cp|ltA(zY^Q zUf>`0W9i5E2Oa!?gWu`kf8yYC4nE=FbqBw}!FSntbzu2?FJmJGr~vvictf}MvqG>d z*x9vC-TpDI6QgQ><@`FRrkdYTdkSb{!CPPTGFqvxdQFfcK!$gNjTd@BQodxf%8jw)?mwEup|31HecowK3ewV;c!mkf)jfWb|VE*Xy0I?Ua z35P!bZLm}aT54ax<=wFb#q)oA9#>fEK)g2HSWxrtga)FuYrc5mU~6riOC4BxIh5dK zG}2gTd~f&-u<=qa%I808Yy3p*pY;%AHvA~SjfcF?wqC?`6pY6FyR8FRSN-d1Lb^F(7Ciez!*HN#pwYC9zaX(n=_NH#BRYtutDr-63 z?47Y5+1A=>&IdqX8YBVWgJ@^1&pV^qJ7bM+WEJ>9?Vo-r7VROZwYP)-n!pAH^3TChVmaBgd(%y+s>F~N8~`T z?|=)lezhUkcc#L7e+y*Un*V&HPGtSW0>~P&x3Yc!vpxX2|LB8~?Z#w#$$pK=zky^v z9)=e{tlRIMy8|qMBl!4i{G=m)cI3rf(TS@7wDL?-nyKz zF9(5jK^E#j)oI|>D^D4-z^PAS9HxeI%wyiUy=WOK$0^7k!Nh^3uOq2HtL9Ir`#!x^ z-FM2Zx(=#G&Kfhpz)oqP+C0|H+l%zEzxOVG5t3Q%@-DS+`6i62`45e0YX9W|z3iLyL={M;BCSbi6bmf#H?`L}~z9r>?oC`yYnf}S&voIn$<>39hB&@l9U3)F_-%o) z*2(-eN-ad2;5quKX$~Ro@wIzue(0TgM|Cf|cFt9oEvcP{FzVj|50=sX{snOZp7nt5 z1xf*as$jd}IRSVp{+`nA@!eMerU%?D9%XlB&{KDBRnXJWH)*A3sKWi2Yl^3#-BTCz zC@r3v7LTvRQ{6Td)X@H);MwEI7wvxx&ta&K4ixR*U(w;IxzF9^Q3fj8JaxOPT0IT- zPEtM1eUn#u0!dFZnnzu!TV`I( z=s+m#Ilek;rch)rzR-g}YxZ6lp|em0YkaD!~1fTU|A-6&~V(q2giCEY9Okfa4k9p=B4rSpm4A0Ae& zSaFq7*R?5^G;+$7{w4l~g^gF_INNy7;s$?1Q@wz{)iU-8mvzqHE{-QV>705o?q+lj zy%?`#cgucRj8|Fxxfq|ss2>;OlNt5*V!WDBKPbk}Vbp($@hOb@X)!*PQGYJRVRTQ> zK5n>8p-hN-*x?CrFFQ6N?qj|7L!2^mH=OLG`J=czaB^C)BO>l*=UM0S?Wt}Uy}s|; z5t5(9Xg;#1y4m^GJXDNdz$O|8ZkSbQ-YAxbSubEmMBHsVXKhb)Gny60P@Fz0+2`gM zB4Rk7x!?r(kRo_;E6y(y$zK9^1vH{h&g(*pvH21YIPhkP)As<$uZD;V%C80lg5|@o z0Up-`e%k>!=38*sALR1m{a>p5PjUH~taBbj9ATpRzQ|=}jcdm@0e7+S?f8zAclL+l z93S7W&Tu>9+r1qo=nDKcqz2*ojGYILIv2oz{NkbSuS)<|q>P<>!}0O`=UTugI;X!D z@QLmdbeF*I2HeMHvzmZCSH>ukxLLq4@o8Zo?OiFIjlDzBpal{HGpG#9hUu8mv~6xG+$9OAA)or zjEkXw6eJ?$>0a6*iQfn~$~*lZk@ACbKIFflf=&}y*Gu4kLHpomXZeo!(MtIKn@5a|Du83X0s#S1y;wAtNNn>*(rKpM z9BRf(A<1w;XOWDNHF9u}CBnvToM=Wui{PD;EZpCSrnJrR)TVG;iyEm+Rtx90vPdeC zj_ZaV^*1*xYnWIF?|{U#a3&Mprs+uo-WYUeAV-ZC1XOIC29idRsHU~A4X$d_+E%yX zEtg_}DAQVRS{+=~v7$JIUy1<;?oerMsPa5VRoVE^gpJ8H%qDY0+#->j@{Lc(thGI*@|XCv1qZCp0JyS%niZFecQYotYoTeFDa^ZNB-gQCyk9R}p--i%=ui^F(RCDwDY^ z#gXFr3%7=(Px}X=3Z&VCH4jQ~8Bn|adYM1bTDvmD>HCe~4u?MNM~EsUD5+EbM!-+P z9+S#X`*5Q4y-E9UXZgdRk84)ar~M7lA#^STtRqHApZ3c!z_7}sPx~OEw9f|-9;7*9 z*=-Pm4Wdf@Fy}^Zx@RVEswIpa@B#A;DUo&ivnl6uUm{hl#3~XbaST2>K8ra2}qI zLBwdEMEBF3^28JU9CW-keY%&raUoj6wQzADJ)*eZ!oEw*MEh|1e*@$g9Df){kL*4H z7*^5Jm%sC+<$`<=m283ZiB1A#yFLp@;RZozoRSIBBOd1`#3+9yAW~|izB7MCBwL>| zWd2gz!b$%!3lR}4D+W8Iz9Uk^rz#EZk&e3vU~~zUpX!CbUsBnHruaN|g`iE2M9P;Yl;E77}wD3J#Y23v&ID$N&HU literal 0 HcmV?d00001 diff --git a/linux/plugins/dummy.cpp b/linux/plugins/dummy.cpp new file mode 100644 index 000000000..653df25a2 --- /dev/null +++ b/linux/plugins/dummy.cpp @@ -0,0 +1,111 @@ +/** + * @brief This provides dummy data to the shared memory. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "defs.h" + +/** + * @brief Useful telemetry data. + */ +telemetry_state_t telemetry; + +/** + * @brief Opaque pointer to use for mmap + */ +void* mapped_region = NULL; +size_t MAP_SIZE = -1; + + +int main() +{ + memset(&telemetry, 0, sizeof(telemetry)); + MAP_SIZE = getpagesize(); + + // Open shared memory + int handle = shm_open(shm_name, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (handle < 0) { + fprintf(stderr, "Local radio: could not open shared memory"); + return 1; + } + + if (ftruncate(handle, MAP_SIZE) < 0) { + fprintf(stderr, "Local radio: could not truncate shared memory"); + return 1; + } + + mapped_region = mmap( + 0, + MAP_SIZE, + PROT_READ | PROT_WRITE, + MAP_SHARED, + handle, + 0 + ); + + if (mapped_region == MAP_FAILED) { + fprintf(stderr, "Local radio: could not mmap shared memory"); + return 1; + } + + telemetry_state_t telemetry; + memset(&telemetry, 0, sizeof(telemetry_state_t)); + telemetry.electricity = { 1 }; + + char buf[128]; + while (1) { + printf("Enter location (x,y,z): "); + fgets(buf, 128, stdin); + + char numBuf[32]; + int offset = 0; + double coords[3]; + int onCoord = 0; + char current; + for (int i = 0; i < 128; ++i) { + current = buf[i]; + if (current == '\x00') { + break; + } + + if (current == ',' || current == '\n') { + numBuf[i - offset] = '\x00'; + coords[onCoord] = strtod(numBuf, NULL); + + onCoord++; + offset = i + 1; + + if (onCoord > 2) { + break; + } + + continue; + } + + numBuf[i - offset] = current; + } + + telemetry.world_position.position = { + coords[0], + coords[1], + coords[2], + }; + + memcpy(mapped_region, &telemetry, sizeof(telemetry_state_t)); + } + + return 0; +} + +void __attribute__ ((destructor)) unload(void) +{ + shm_unlink(shm_name); +} From b405c6145295d669adec3126b6fa2d6740bcd985 Mon Sep 17 00:00:00 2001 From: James Thistlewood Date: Thu, 25 Mar 2021 18:01:19 +0000 Subject: [PATCH 12/14] try to catch request handling failures --- linux/plugins/Makefile | 9 +++++++-- linux/server/main.py | 7 +++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/linux/plugins/Makefile b/linux/plugins/Makefile index 55aaa0ecd..43b416280 100644 --- a/linux/plugins/Makefile +++ b/linux/plugins/Makefile @@ -10,7 +10,9 @@ SDK_INCLUDES=\ -I../sdk/include/amtrucks/ \ -I../sdk/include/eurotrucks2 -all: ets2-telemetry-lin.so read_util +CLEANABLE=*.so read_util dummy + +all: ets2-telemetry-lin.so read_util dummy ets2-telemetry-lin.so: plugin.cpp $(SDK_HEADERS) defs.h g++ -O2 -o $@ -fPIC -Wall --shared -Wl,-soname,$@ $(SDK_INCLUDES) plugin.cpp -lrt @@ -18,6 +20,9 @@ ets2-telemetry-lin.so: plugin.cpp $(SDK_HEADERS) defs.h read_util: read_util.cpp $(SDK_HEADERS) defs.h g++ -O2 -o $@ -Wall $(SDK_INCLUDES) read_util.cpp -lrt +dummy: dummy.cpp $(SDK_HEADERS) defs.h + g++ -O2 -o $@ -Wall $(SDK_INCLUDES) dummy.cpp -lrt + .PHONY: clean clean: - @rm -f -- *.so read_util + @rm -f -- $(CLEANABLE) diff --git a/linux/server/main.py b/linux/server/main.py index fea2ccde0..2eb9aeefd 100644 --- a/linux/server/main.py +++ b/linux/server/main.py @@ -94,6 +94,13 @@ def handle_cmd(self): self.wfile.write(json.dumps(payload).encode("utf-8")) def do_GET(self): + try: + self.GET_handler() + except Exception as e: + print("Server: encountered error while responding to request:", e) + print("Ignoring and continuing to run.") + + def GET_handler(self): if self.path[:4] == "/api": self.handle_api() elif self.path[:9] == "/commands": From 19c22e112143afff7d8d2d4df7e657ccb9c7a4aa Mon Sep 17 00:00:00 2001 From: James Thistlewood Date: Thu, 25 Mar 2021 19:11:41 +0000 Subject: [PATCH 13/14] add favourites functionality, fix station not changing when crossing borders --- .gitignore | 2 + linux/server/lib/favourites.py | 60 ++++++++++++++++++++++++++++++ linux/server/main.py | 67 +++++++++++++++++++++++++++------- 3 files changed, 115 insertions(+), 14 deletions(-) create mode 100644 linux/server/lib/favourites.py diff --git a/.gitignore b/.gitignore index fb373e9af..587f9914c 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,5 @@ server/packages/** linux/plugins/ets2-telemetry-lin.so linux/plugins/read_util linux/server/read_util +favourites.json* +__pycache__/ diff --git a/linux/server/lib/favourites.py b/linux/server/lib/favourites.py new file mode 100644 index 000000000..2909ddbbe --- /dev/null +++ b/linux/server/lib/favourites.py @@ -0,0 +1,60 @@ +import json +import os + +class Favourites: + def __init__(self, filename): + self.filename = filename + self.stored = {} + + self.load_from_file() + + def load_from_file(self): + try: + fd = open(self.filename, "r") + except FileNotFoundError: + # File doesn't exist, this is OK + return + except IOError: + raise Exception("Could not open specified favourites save file!") + + txt = fd.read().strip() + fd.close() + + if not txt: + return + + try: + self.stored = json.loads(txt) + except json.JSONDecodeError: + raise Exception("Corrupt favourites save file!") + + def save_to_file(self): + try: + os.rename(self.filename, self.filename + ".bak") + except FileNotFoundError: + pass + + try: + fd = open(self.filename, "w") + except IOError: + raise Exception("Could not open specified favourites save file!") + + fd.write(json.dumps(self.stored)) + fd.close() + + def get_favourite(self, country): + if country not in self.stored: + return "" + + return self.stored[country] + + def set_favourite(self, country, station): + self.stored[country] = station + self.save_to_file() + + def remove_favourite(self, country, station): + if self.stored[country] != station: + return + + self.stored[country] == "" + self.save_to_file() diff --git a/linux/server/main.py b/linux/server/main.py index 2eb9aeefd..a9b280a2f 100644 --- a/linux/server/main.py +++ b/linux/server/main.py @@ -6,11 +6,18 @@ import signal import time import argparse +from urllib.parse import unquote + +from lib import favourites as favourite_manager + +FAVOURITES_FILE = "favourites.json" last_value = (0, 0, 0) has_elec = [False, False] # current, last read by command handler alive = True httpd = None +favourites = None + def start_read(): global last_value, has_elec @@ -58,21 +65,22 @@ class CustomHandler(SimpleHTTPRequestHandler): def __init__(self, *args): super().__init__(*args, directory="../../web/") + def send_json(self, obj): + self.send_response(200) + self.send_header("Content-Type", "application/json") + self.send_header("Content-Encoding", "utf-8") + self.end_headers() + self.wfile.write(json.dumps(obj).encode("utf-8")) + def handle_api(self): global last_value - payload = { + self.send_json({ "Physics": { "CoordinateX": last_value[0], "CoordinateY": last_value[1], "CoordinateZ": last_value[2], } - } - - self.send_response(200) - self.send_header("Content-Type", "application/json") - self.send_header("Content-Encoding", "utf-8") - self.end_headers() - self.wfile.write(json.dumps(payload).encode("utf-8")) + }) def handle_cmd(self): global has_elec @@ -87,24 +95,54 @@ def handle_cmd(self): has_elec[1] = has_elec[0] payload["action"] = "engineOn" if has_elec[0] else "engineOff" - self.send_response(200) - self.send_header("Content-Type", "application/json") - self.send_header("Content-Encoding", "utf-8") + self.send_json(payload) + + def get_favourite(self, parts): + fav = favourites.get_favourite(parts[1]) + self.send_json({ + "Name": fav, + }) + + def set_favourite(self, parts): + favourites.set_favourite(parts[1], parts[2]) + self.send_response_only(200) self.end_headers() - self.wfile.write(json.dumps(payload).encode("utf-8")) + + def get_all_favourites(self): + self.send_json(favourites.stored) + + def handle_fav(self): + global favourites + + parts = [x.strip() for x in self.path.split("/")] + parts = [unquote(x) for x in parts if x] + + if len(parts) == 1: + self.get_all_favourites() + elif len(parts) == 2: + # We need to return the favourite for this country + self.get_favourite(parts) + elif len(parts) == 3: + # We need to set the favourite for this country + self.set_favourite(parts) + else: + self.send_response_only(400) + self.end_headers() def do_GET(self): try: self.GET_handler() except Exception as e: print("Server: encountered error while responding to request:", e) - print("Ignoring and continuing to run.") + print("Server: ignoring and continuing to run") def GET_handler(self): if self.path[:4] == "/api": self.handle_api() elif self.path[:9] == "/commands": self.handle_cmd() + elif self.path[:10] == "/favourite": + self.handle_fav() else: super().do_GET() @@ -117,7 +155,6 @@ def run_server(): global last_value global alive global httpd - global PORT while alive: try: @@ -161,6 +198,8 @@ def shutdown(sig=None, frame=None): args = parser.parse_args() PORT = args.port +favourites = favourite_manager.Favourites(FAVOURITES_FILE) + read_thread.start() server_thread.start() From bb04ee920c67190a848576fa5fc34ff522096f35 Mon Sep 17 00:00:00 2001 From: James Thistlewood Date: Thu, 25 Mar 2021 19:16:10 +0000 Subject: [PATCH 14/14] update README --- linux/plugins/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/linux/plugins/README.md b/linux/plugins/README.md index a80a1ae0c..0814ba899 100644 --- a/linux/plugins/README.md +++ b/linux/plugins/README.md @@ -25,3 +25,7 @@ python3 main.py This will by default run at `localhost:3141`, but feel free to change the port by adding the `--port` flag. Then just visit `localhost:3141` (or your specified port) to view the web interface! + +## Development + +The `dummy` allows you to send fake data to the shared memory, for testing purposes.