commit e3bfbba906f8c859cfb15248f94041298f6b6654 Author: YUCLing Date: Sat Apr 29 05:19:39 2023 +0800 Initial commit (SDK v1.14) diff --git a/README.md b/README.md new file mode 100644 index 0000000..a6201b3 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# SCS SDK +SCS Software Development Kit + +## License +See \ No newline at end of file diff --git a/examples/input/Makefile b/examples/input/Makefile new file mode 100644 index 0000000..aaf661b --- /dev/null +++ b/examples/input/Makefile @@ -0,0 +1,26 @@ +SDK_HEADERS=\ + ../../include/*.h \ + ../../include/common/*.h \ + ../../include/amtrucks/*.h \ + ../../include/eurotrucks2/*.h + +SDK_INCLUDES=\ + -I../../include \ + -I../../include/common/ \ + -I../../include/amtrucks/ \ + -I../../include/eurotrucks2 + +UNAME:= $(shell uname -s) + +ifeq ($(UNAME),Darwin) +LIB_NAME_OPTION=-install_name +else +LIB_NAME_OPTION=-soname +endif + +input.so: *.cpp $(SDK_HEADERS) + gcc -o $@ -fPIC -Wall --shared -Wl,$(LIB_NAME_OPTION),$@ $(SDK_INCLUDES) *.cpp + +.PHONY: clean +clean: + @rm -f -- *.so diff --git a/examples/input/input.cpp b/examples/input/input.cpp new file mode 100644 index 0000000..7187b10 --- /dev/null +++ b/examples/input/input.cpp @@ -0,0 +1,321 @@ +/** + * @brief Simple input device + * + * Generates device with a few axes and buttons which change with time + */ + +// Windows stuff. + +#ifdef _WIN32 +# define WINVER 0x0500 +# define _WIN32_WINNT 0x0500 +# include +#endif + +#include +#include +#include +#include +#include +#include + +// SDK + +#include "scssdk_input.h" +#include "eurotrucks2/scssdk_eut2.h" +#include "eurotrucks2/scssdk_input_eut2.h" +#include "amtrucks/scssdk_ats.h" +#include "amtrucks/scssdk_input_ats.h" + +#define UNUSED(x) + +/** + * @brief Logging support. + */ +FILE *log_file = NULL; + +const unsigned AXIS_COUNT = 2; +const unsigned BUTTON_COUNT = 3; + +// Simulation of the values. + +struct device_data_t +{ + // Current state of the dummy inputs. + + float axes[AXIS_COUNT]; + unsigned button_state; + + // Last time when we simulated movement. + + time_t last_movement_time; + + // Index of next input we will report. + + unsigned next_reported_input; +}; + +device_data_t dummy_device; + +// Management of the log file. + +bool init_log(void) +{ + if (log_file) { + return true; + } + log_file = fopen("input.log", "wt"); + if (! log_file) { + return false; + } + fprintf(log_file, "Log opened\n"); + return true; +} + +void finish_log(void) +{ + if (! log_file) { + return; + } + fprintf(log_file, "Log ended\n"); + fclose(log_file); + log_file = NULL; +} + +void log_print(const char *const text, ...) +{ + if (! log_file) { + return; + } + va_list args; + va_start(args, text); + vfprintf(log_file, text, args); + va_end(args); +} + +void log_line(const char *const text, ...) +{ + if (! log_file) { + return; + } + va_list args; + va_start(args, text); + vfprintf(log_file, text, args); + fprintf(log_file, "\n"); + va_end(args); +} + +void simulate_device(device_data_t &device) +{ + // Move once per second. + + const time_t now = time(NULL); + if (device.last_movement_time == now) { + return; + } + device.last_movement_time = now; + + // Move each axis to maximum before moving the next + // one reseting everything to zero if all of them + // reach maximum. + + if (device.axes[AXIS_COUNT - 1] >= 1.0f) { + memset(device.axes, 0, sizeof(device.axes)); + } + + for (unsigned i = 0; i < AXIS_COUNT; ++i) { + if (device.axes[i] < 1.0f) { + device.axes[i] += 0.125f; + break; + } + } + + device.button_state = (device.button_state + 1) % (BUTTON_COUNT * 2); +} + +SCSAPI_VOID input_active_callback(const scs_u8_t active, const scs_context_t UNUSED(context)) +{ + // This callback is optional. A real implementation could do some cleanup + // on deactivation or can leave it to event callback with + // SCS_INPUT_EVENT_CALLBACK_FLAG_first_after_activation flag. + + if (active > 0) { + log_line("Device activated"); + } + else { + log_line("Device deactivated"); + } +} + +SCSAPI_RESULT input_event_callback(scs_input_event_t *const event_info, const scs_u32_t flags, const scs_context_t context) +{ + device_data_t &device = *static_cast(context); + + if (flags & SCS_INPUT_EVENT_CALLBACK_FLAG_first_after_activation) { + log_line("First call after activation"); + } + + // Simulates retrieval of current data from some external device at start + // of the frame. + + if (flags & SCS_INPUT_EVENT_CALLBACK_FLAG_first_in_frame) { + simulate_device(device); + device.next_reported_input = 0; + } + + // Did we process all events for this frame? + + if (device.next_reported_input >= (AXIS_COUNT + BUTTON_COUNT)) { + return SCS_RESULT_not_found; + } + + // Generate events for individual inputs. If the number of inputs is small, + // it is fine to generate events even if there was no change. + + event_info->input_index = device.next_reported_input; + if (device.next_reported_input < AXIS_COUNT) { + event_info->value_float.value = device.axes[device.next_reported_input]; + } + else { + event_info->value_bool.value = (((device.next_reported_input - AXIS_COUNT) * 2) == device.button_state) ? 1 : 0; + } + ++device.next_reported_input; + return SCS_RESULT_ok; +} + +/** + * @brief Input API initialization function. + * + * See scssdk_input.h + */ +SCSAPI_RESULT scs_input_init(const scs_u32_t version, const scs_input_init_params_t *const params) +{ + // We currently support only one version. + + if (version != SCS_INPUT_VERSION_1_00) { + return SCS_RESULT_unsupported; + } + + const scs_input_init_params_v100_t *const version_params = static_cast(params); + if (! init_log()) { + version_params->common.log(SCS_LOG_TYPE_error, "Unable to initialize the log file"); + return SCS_RESULT_generic_error; + } + + // Check application version. Note that this example uses fairly basic features which are likely to be supported + // by any future SCS trucking game however more advanced applications might want to at least warn the user if there + // is game or version they do not support. + + log_line("Game '%s' %u.%u", version_params->common.game_id, SCS_GET_MAJOR_VERSION(version_params->common.game_version), SCS_GET_MINOR_VERSION(version_params->common.game_version)); + + 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_INPUT_EUT2_GAME_VERSION_1_00; + if (version_params->common.game_version < MINIMAL_VERSION) { + log_line("WARNING: Too old version of the game, some features might behave incorrectly"); + } + + // Future versions are fine as long the major version is not changed. + + const scs_u32_t IMPLEMENTED_VERSION = SCS_INPUT_EUT2_GAME_VERSION_CURRENT; + if (SCS_GET_MAJOR_VERSION(version_params->common.game_version) > SCS_GET_MAJOR_VERSION(IMPLEMENTED_VERSION)) { + log_line("WARNING: Too new major version of the game, some features 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_INPUT_ATS_GAME_VERSION_1_00; + if (version_params->common.game_version < MINIMAL_VERSION) { + log_line("WARNING: Too old version of the game, some features might behave incorrectly"); + } + + // Future versions are fine as long the major version is not changed. + + const scs_u32_t IMPLEMENTED_VERSION = SCS_INPUT_ATS_GAME_VERSION_CURRENT; + if (SCS_GET_MAJOR_VERSION(version_params->common.game_version) > SCS_GET_MAJOR_VERSION(IMPLEMENTED_VERSION)) { + log_line("WARNING: Too new major version of the game, some features might behave incorrectly"); + } + } + else { + log_line("WARNING: Unsupported game, some features or values might behave incorrectly"); + } + + // Setup the device information. + + const scs_input_device_input_t inputs[] = { + {"a1", "Analog axis 1", SCS_VALUE_TYPE_float}, + {"a2", "Analog axis 2", SCS_VALUE_TYPE_float}, + {"b1", "Button 1", SCS_VALUE_TYPE_bool}, + {"b2", "Button 2", SCS_VALUE_TYPE_bool}, + {"b3", "Button 3", SCS_VALUE_TYPE_bool}, + }; + assert(AXIS_COUNT == 2); + assert(BUTTON_COUNT == 3); + + scs_input_device_t device_info; + memset(&device_info, 0, sizeof(device_info)); + device_info.name = "example"; + device_info.display_name = "Example SDK Device"; + device_info.type = SCS_INPUT_DEVICE_TYPE_generic; + device_info.input_count = sizeof(inputs) / sizeof(inputs[0]); + device_info.inputs = inputs; + + device_info.input_active_callback = input_active_callback; + device_info.input_event_callback = input_event_callback; + device_info.callback_context = &dummy_device; + + memset(&dummy_device, 0, sizeof(dummy_device)); + dummy_device.last_movement_time = time(NULL); + + if (version_params->register_device(&device_info) != SCS_RESULT_ok) { + + // Registrations created by unsuccessfull initialization are + // cleared automatically so we can simply exit. + + version_params->common.log(SCS_LOG_TYPE_error, "Unable to register device"); + return SCS_RESULT_generic_error; + } + + return SCS_RESULT_ok; +} + +/** + * @brief Input API deinitialization function. + * + * See scssdk_input.h + */ +SCSAPI_VOID scs_input_shutdown(void) +{ + // Any cleanup needed. The registrations will be removed automatically. + + finish_log(); +} + +// Cleanup + +#ifdef _WIN32 +BOOL APIENTRY DllMain( + HMODULE module, + DWORD reason_for_call, + LPVOID reseved +) +{ + if (reason_for_call == DLL_PROCESS_DETACH) { + finish_log(); + } + return TRUE; +} +#endif + +#ifdef __linux__ +void __attribute__ ((destructor)) unload(void) +{ + finish_log(); +} +#endif diff --git a/examples/input/input.def b/examples/input/input.def new file mode 100644 index 0000000..92d1249 --- /dev/null +++ b/examples/input/input.def @@ -0,0 +1,4 @@ +LIBRARY input +EXPORTS +scs_input_init=scs_input_init +scs_input_shutdown=scs_input_shutdown diff --git a/examples/input/input.sln b/examples/input/input.sln new file mode 100644 index 0000000..1e26f84 --- /dev/null +++ b/examples/input/input.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{50A37733-D338-413F-8589-F7B943950E5C}") = "input", "input.vcproj", "{90C24814-396E-4E0D-8448-9EE2AB1EE096}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {90C24814-396E-4E0D-8448-9EE2AB1EE096}.Debug|Win32.ActiveCfg = Debug|Win32 + {90C24814-396E-4E0D-8448-9EE2AB1EE096}.Debug|Win32.Build.0 = Debug|Win32 + {90C24814-396E-4E0D-8448-9EE2AB1EE096}.Debug|x64.ActiveCfg = Debug|x64 + {90C24814-396E-4E0D-8448-9EE2AB1EE096}.Debug|x64.Build.0 = Debug|x64 + {90C24814-396E-4E0D-8448-9EE2AB1EE096}.Release|Win32.ActiveCfg = Release|Win32 + {90C24814-396E-4E0D-8448-9EE2AB1EE096}.Release|Win32.Build.0 = Release|Win32 + {90C24814-396E-4E0D-8448-9EE2AB1EE096}.Release|x64.ActiveCfg = Release|x64 + {90C24814-396E-4E0D-8448-9EE2AB1EE096}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/examples/input/input.vcproj b/examples/input/input.vcproj new file mode 100644 index 0000000..648152f --- /dev/null +++ b/examples/input/input.vcproj @@ -0,0 +1,333 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/input_semantical/Makefile b/examples/input_semantical/Makefile new file mode 100644 index 0000000..c5a66bd --- /dev/null +++ b/examples/input_semantical/Makefile @@ -0,0 +1,26 @@ +SDK_HEADERS=\ + ../../include/*.h \ + ../../include/common/*.h \ + ../../include/amtrucks/*.h \ + ../../include/eurotrucks2/*.h + +SDK_INCLUDES=\ + -I../../include \ + -I../../include/common/ \ + -I../../include/amtrucks/ \ + -I../../include/eurotrucks2 + +UNAME:= $(shell uname -s) + +ifeq ($(UNAME),Darwin) +LIB_NAME_OPTION=-install_name +else +LIB_NAME_OPTION=-soname +endif + +input_semantical.so: *.cpp $(SDK_HEADERS) + gcc -o $@ -fPIC -Wall --shared -Wl,$(LIB_NAME_OPTION),$@ $(SDK_INCLUDES) *.cpp + +.PHONY: clean +clean: + @rm -f -- *.so diff --git a/examples/input_semantical/input_semantical.cpp b/examples/input_semantical/input_semantical.cpp new file mode 100644 index 0000000..38ecdf2 --- /dev/null +++ b/examples/input_semantical/input_semantical.cpp @@ -0,0 +1,102 @@ +/** + * @brief Simple semantical input device + * + * Generates a simple device which toggles lights based on time + */ + +// Windows stuff. + +#ifdef _WIN32 +# define WINVER 0x0500 +# define _WIN32_WINNT 0x0500 +# include +#endif + +#include +#include +#include +#include +#include +#include + +// SDK + +#include "scssdk_input.h" +#include "eurotrucks2/scssdk_eut2.h" +#include "eurotrucks2/scssdk_input_eut2.h" +#include "amtrucks/scssdk_ats.h" +#include "amtrucks/scssdk_input_ats.h" + +#define UNUSED(x) + +SCSAPI_RESULT input_event_callback(scs_input_event_t *const event_info, const scs_u32_t flags, const scs_context_t UNUSED(context)) +{ + if ((flags & SCS_INPUT_EVENT_CALLBACK_FLAG_first_in_frame) == 0) { + return SCS_RESULT_not_found; + } + + event_info->input_index = 0; + event_info->value_bool.value = (time(NULL) & 1); + return SCS_RESULT_ok; +} + +/** + * @brief Input API initialization function. + * + * See scssdk_input.h + */ +SCSAPI_RESULT scs_input_init(const scs_u32_t version, const scs_input_init_params_t *const params) +{ + // We currently support only one version. + + if (version != SCS_INPUT_VERSION_1_00) { + return SCS_RESULT_unsupported; + } + + const scs_input_init_params_v100_t *const version_params = static_cast(params); + + // Setup the device information. The name of the input matches the name of the + // mix as seen in controls.sii. Note that only some inputs are supported this way. + // See documentation of SCS_INPUT_DEVICE_TYPE_semantical + + const scs_input_device_input_t input = {"light", "Lights", SCS_VALUE_TYPE_bool}; + + scs_input_device_t device_info; + memset(&device_info, 0, sizeof(device_info)); + device_info.name = "example_semantical"; + device_info.display_name = "Example Semantical SDK Device"; + device_info.type = SCS_INPUT_DEVICE_TYPE_semantical; + device_info.input_count = 1; + device_info.inputs = &input; + device_info.input_event_callback = input_event_callback; + device_info.callback_context = NULL; + + if (version_params->register_device(&device_info) != SCS_RESULT_ok) { + version_params->common.log(SCS_LOG_TYPE_error, "Unable to register device"); + return SCS_RESULT_generic_error; + } + + return SCS_RESULT_ok; +} + +/** + * @brief Input API deinitialization function. + * + * See scssdk_input.h + */ +SCSAPI_VOID scs_input_shutdown(void) +{ +} + +// Cleanup + +#ifdef _WIN32 +BOOL APIENTRY DllMain( + HMODULE module, + DWORD reason_for_call, + LPVOID reseved +) +{ + return TRUE; +} +#endif diff --git a/examples/input_semantical/input_semantical.def b/examples/input_semantical/input_semantical.def new file mode 100644 index 0000000..e236b50 --- /dev/null +++ b/examples/input_semantical/input_semantical.def @@ -0,0 +1,4 @@ +LIBRARY input_semantical +EXPORTS +scs_input_init=scs_input_init +scs_input_shutdown=scs_input_shutdown diff --git a/examples/input_semantical/input_semantical.sln b/examples/input_semantical/input_semantical.sln new file mode 100644 index 0000000..61eb3b8 --- /dev/null +++ b/examples/input_semantical/input_semantical.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{ED234DAB-6945-4EF7-A3AE-60071F851C82}") = "input_semantical", "input_semantical.vcproj", "{26C594C5-58C7-4F22-B594-DB4F1D62224E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {26C594C5-58C7-4F22-B594-DB4F1D62224E}.Debug|Win32.ActiveCfg = Debug|Win32 + {26C594C5-58C7-4F22-B594-DB4F1D62224E}.Debug|Win32.Build.0 = Debug|Win32 + {26C594C5-58C7-4F22-B594-DB4F1D62224E}.Debug|x64.ActiveCfg = Debug|x64 + {26C594C5-58C7-4F22-B594-DB4F1D62224E}.Debug|x64.Build.0 = Debug|x64 + {26C594C5-58C7-4F22-B594-DB4F1D62224E}.Release|Win32.ActiveCfg = Release|Win32 + {26C594C5-58C7-4F22-B594-DB4F1D62224E}.Release|Win32.Build.0 = Release|Win32 + {26C594C5-58C7-4F22-B594-DB4F1D62224E}.Release|x64.ActiveCfg = Release|x64 + {26C594C5-58C7-4F22-B594-DB4F1D62224E}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/examples/input_semantical/input_semantical.vcproj b/examples/input_semantical/input_semantical.vcproj new file mode 100644 index 0000000..000c9b1 --- /dev/null +++ b/examples/input_semantical/input_semantical.vcproj @@ -0,0 +1,333 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/telemetry/Makefile b/examples/telemetry/Makefile new file mode 100644 index 0000000..22911b1 --- /dev/null +++ b/examples/telemetry/Makefile @@ -0,0 +1,26 @@ +SDK_HEADERS=\ + ../../include/*.h \ + ../../include/common/*.h \ + ../../include/amtrucks/*.h \ + ../../include/eurotrucks2/*.h + +SDK_INCLUDES=\ + -I../../include \ + -I../../include/common/ \ + -I../../include/amtrucks/ \ + -I../../include/eurotrucks2 + +UNAME:= $(shell uname -s) + +ifeq ($(UNAME),Darwin) +LIB_NAME_OPTION=-install_name +else +LIB_NAME_OPTION=-soname +endif + +telemetry.so: *.cpp $(SDK_HEADERS) + gcc -o $@ -fPIC -Wall --shared -Wl,$(LIB_NAME_OPTION),$@ $(SDK_INCLUDES) *.cpp + +.PHONY: clean +clean: + @rm -f -- *.so diff --git a/examples/telemetry/telemetry.cpp b/examples/telemetry/telemetry.cpp new file mode 100644 index 0000000..c46b90f --- /dev/null +++ b/examples/telemetry/telemetry.cpp @@ -0,0 +1,529 @@ +/** + * @brief Simple logger. + * + * Writes the output into file inside the current directory. + */ + +// Windows stuff. + +#ifdef _WIN32 +# define WINVER 0x0500 +# define _WIN32_WINNT 0x0500 +# include +#endif + +#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 Logging support. + */ +FILE *log_file = NULL; + +/** + * @brief Tracking of paused state of the game. + */ +bool output_paused = true; + +/** + * @brief Should we print the data header next time + * we are printing the data? + */ +bool print_header = true; + +/** + * @brief Last timestamp we received. + */ +scs_timestamp_t last_timestamp = static_cast(-1); + +/** + * @brief Combined telemetry data. + */ +struct telemetry_state_t +{ + scs_timestamp_t timestamp; + scs_timestamp_t raw_rendering_timestamp; + scs_timestamp_t raw_simulation_timestamp; + scs_timestamp_t raw_paused_simulation_timestamp; + + bool orientation_available; + float heading; + float pitch; + float roll; + + float speed; + float rpm; + int gear; + +} telemetry; + +/** + * @brief Function writting message to the game internal log. + */ +scs_log_t game_log = NULL; + +// Management of the log file. + +bool init_log(void) +{ + if (log_file) { + return true; + } + log_file = fopen("telemetry.log", "wt"); + if (! log_file) { + return false; + } + fprintf(log_file, "Log opened\n"); + return true; +} + +void finish_log(void) +{ + if (! log_file) { + return; + } + fprintf(log_file, "Log ended\n"); + fclose(log_file); + log_file = NULL; +} + +void log_print(const char *const text, ...) +{ + if (! log_file) { + return; + } + va_list args; + va_start(args, text); + vfprintf(log_file, text, args); + va_end(args); +} + +void log_line(const char *const text, ...) +{ + if (! log_file) { + return; + } + va_list args; + va_start(args, text); + vfprintf(log_file, text, args); + fprintf(log_file, "\n"); + va_end(args); +} + +// 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); + + // The following processing of the timestamps is done so the output + // from this plugin has continuous time, it is not necessary otherwise. + + // When we just initialized itself, assume that the time started + // just now. + + if (last_timestamp == static_cast(-1)) { + last_timestamp = info->paused_simulation_time; + } + + // 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; + } + + // Advance the timestamp by delta since last frame. + + telemetry.timestamp += (info->paused_simulation_time - last_timestamp); + last_timestamp = info->paused_simulation_time; + + // The raw values. + + telemetry.raw_rendering_timestamp = info->render_time; + telemetry.raw_simulation_timestamp = info->simulation_time; + telemetry.raw_paused_simulation_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; + } + + // The header. + + if (print_header) { + print_header = false; + log_line("timestamp[us];raw rendering timestamp[us];raw simulation timestamp[us];raw paused simulation timestamp[us];heading[deg];pitch[deg];roll[deg];speed[m/s];rpm;gear"); + } + + // The data line. + + log_print("%" SCS_PF_U64 ";%" SCS_PF_U64 ";%" SCS_PF_U64 ";%" SCS_PF_U64, telemetry.timestamp, telemetry.raw_rendering_timestamp, telemetry.raw_simulation_timestamp, telemetry.raw_paused_simulation_timestamp); + if (telemetry.orientation_available) { + log_print(";%f;%f;%f", telemetry.heading, telemetry.pitch, telemetry.roll); + } + else { + log_print(";---;---;---"); + } + log_line( + ";%f;%f;%d", + telemetry.speed, + telemetry.rpm, + telemetry.gear + ); +} + +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); + if (output_paused) { + log_line("Telemetry paused"); + } + else { + log_line("Telemetry unpaused"); + } + print_header = true; +} + +void telemetry_print_attributes(const scs_named_value_t *const attributes) +{ + for (const scs_named_value_t *current = attributes; current->name; ++current) { + log_print(" %s", current->name); + if (current->index != SCS_U32_NIL) { + log_print("[%u]", static_cast(current->index)); + } + log_print(" : "); + switch (current->value.type) { + case SCS_VALUE_TYPE_INVALID: { + log_line("none"); + break; + } + case SCS_VALUE_TYPE_bool: { + log_line("bool = %s", current->value.value_bool.value ? "true" : "false"); + break; + } + case SCS_VALUE_TYPE_s32: { + log_line("s32 = %d", static_cast(current->value.value_s32.value)); + break; + } + case SCS_VALUE_TYPE_u32: { + log_line("u32 = %u", static_cast(current->value.value_u32.value)); + break; + } + case SCS_VALUE_TYPE_s64: { + log_line("s64 = %" SCS_PF_S64, current->value.value_s64.value); + break; + } + case SCS_VALUE_TYPE_u64: { + log_line("u64 = %" SCS_PF_U64, current->value.value_u64.value); + break; + } + case SCS_VALUE_TYPE_float: { + log_line("float = %f", current->value.value_float.value); + break; + } + case SCS_VALUE_TYPE_double: { + log_line("double = %f", current->value.value_double.value); + break; + } + case SCS_VALUE_TYPE_fvector: { + log_line( + "fvector = (%f,%f,%f)", + current->value.value_fvector.x, + current->value.value_fvector.y, + current->value.value_fvector.z + ); + break; + } + case SCS_VALUE_TYPE_dvector: { + log_line( + "dvector = (%f,%f,%f)", + current->value.value_dvector.x, + current->value.value_dvector.y, + current->value.value_dvector.z + ); + break; + } + case SCS_VALUE_TYPE_euler: { + log_line( + "euler = h:%f p:%f r:%f", + current->value.value_euler.heading * 360.0f, + current->value.value_euler.pitch * 360.0f, + current->value.value_euler.roll * 360.0f + ); + break; + } + case SCS_VALUE_TYPE_fplacement: { + log_line( + "fplacement = (%f,%f,%f) h:%f p:%f r:%f", + current->value.value_fplacement.position.x, + current->value.value_fplacement.position.y, + current->value.value_fplacement.position.z, + current->value.value_fplacement.orientation.heading * 360.0f, + current->value.value_fplacement.orientation.pitch * 360.0f, + current->value.value_fplacement.orientation.roll * 360.0f + ); + break; + } + case SCS_VALUE_TYPE_dplacement: { + log_line( + "dplacement = (%f,%f,%f) h:%f p:%f r:%f", + current->value.value_dplacement.position.x, + current->value.value_dplacement.position.y, + current->value.value_dplacement.position.z, + current->value.value_dplacement.orientation.heading * 360.0f, + current->value.value_dplacement.orientation.pitch * 360.0f, + current->value.value_dplacement.orientation.roll * 360.0f + ); + break; + } + case SCS_VALUE_TYPE_string: { + log_line("string = %s", current->value.value_string.value); + break; + } + default: { + log_line("unknown"); + break; + } + } + } +} + +SCSAPI_VOID telemetry_configuration(const scs_event_t event, const void *const event_info, const scs_context_t UNUSED(context)) +{ + // Here we just print the configuration info. + + const struct scs_telemetry_configuration_t *const info = static_cast(event_info); + log_line("Configuration: %s", info->id); + + telemetry_print_attributes(info->attributes); + + print_header = true; +} + +SCSAPI_VOID telemetry_gameplay_event(const scs_event_t event, const void *const event_info, const scs_context_t UNUSED(context)) +{ + // Here we just print the event info. + + const struct scs_telemetry_gameplay_event_t *const info = static_cast(event_info); + log_line("Gameplay event: %s", info->id); + + telemetry_print_attributes(info->attributes); + + print_header = true; +} + +// Handling of individual channels. + +SCSAPI_VOID telemetry_store_orientation(const scs_string_t name, const scs_u32_t index, const scs_value_t *const value, const scs_context_t context) +{ + assert(context); + telemetry_state_t *const state = static_cast(context); + + // This callback was registered with the SCS_TELEMETRY_CHANNEL_FLAG_no_value flag + // so it is called even when the value is not available. + + if (! value) { + state->orientation_available = false; + return; + } + + assert(value); + assert(value->type == SCS_VALUE_TYPE_euler); + state->orientation_available = true; + state->heading = value->value_euler.heading * 360.0f; + state->pitch = value->value_euler.pitch * 360.0f; + state->roll = value->value_euler.roll * 360.0f; +} + +SCSAPI_VOID telemetry_store_float(const scs_string_t name, const scs_u32_t index, const scs_value_t *const value, const scs_context_t context) +{ + // The SCS_TELEMETRY_CHANNEL_FLAG_no_value flag was not provided during registration + // so this callback is only called when a valid value is available. + + assert(value); + assert(value->type == SCS_VALUE_TYPE_float); + assert(context); + *static_cast(context) = value->value_float.value; +} + +SCSAPI_VOID telemetry_store_s32(const scs_string_t name, const scs_u32_t index, const scs_value_t *const value, const scs_context_t context) +{ + // The SCS_TELEMETRY_CHANNEL_FLAG_no_value flag was not provided during registration + // so this callback is only called when a valid value is available. + + assert(value); + assert(value->type == SCS_VALUE_TYPE_s32); + assert(context); + *static_cast(context) = value->value_s32.value; +} + +/** + * @brief Telemetry API initialization function. + * + * 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_1_01) { + return SCS_RESULT_unsupported; + } + + const scs_telemetry_init_params_v101_t *const version_params = static_cast(params); + if (! init_log()) { + version_params->common.log(SCS_LOG_TYPE_error, "Unable to initialize the log file"); + return SCS_RESULT_generic_error; + } + + // 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. + + log_line("Game '%s' %u.%u", version_params->common.game_id, SCS_GET_MAJOR_VERSION(version_params->common.game_version), SCS_GET_MINOR_VERSION(version_params->common.game_version)); + + 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) { + log_line("WARNING: Too old version of the game, some features 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)) { + log_line("WARNING: Too new major version of the game, some features 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) { + log_line("WARNING: Too old version of the game, some features 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)) { + log_line("WARNING: Too new major version of the game, some features might behave incorrectly"); + } + } + else { + log_line("WARNING: Unsupported game, some features or values might behave incorrectly"); + } + + // 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, "Unable to register event callbacks"); + return SCS_RESULT_generic_error; + } + + // Register for the configuration info. As this example only prints the retrieved + // data, it can operate even if that fails. + + version_params->register_for_event(SCS_TELEMETRY_EVENT_configuration, telemetry_configuration, NULL); + + // Register for gameplay events. + + version_params->register_for_event(SCS_TELEMETRY_EVENT_gameplay, telemetry_gameplay_event, NULL); + + // 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_euler, SCS_TELEMETRY_CHANNEL_FLAG_no_value, telemetry_store_orientation, &telemetry); + version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_speed, SCS_U32_NIL, SCS_VALUE_TYPE_float, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_float, &telemetry.speed); + version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_engine_rpm, SCS_U32_NIL, SCS_VALUE_TYPE_float, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_float, &telemetry.rpm); + version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_engine_gear, SCS_U32_NIL, SCS_VALUE_TYPE_s32, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_s32, &telemetry.gear); + + // Remember the function we will use for logging. + + game_log = version_params->common.log; + game_log(SCS_LOG_TYPE_message, "Initializing telemetry log example"); + + // Set the structure with defaults. + + memset(&telemetry, 0, sizeof(telemetry)); + print_header = true; + last_timestamp = static_cast(-1); + + // 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 = NULL; + finish_log(); +} + +// Cleanup + +#ifdef _WIN32 +BOOL APIENTRY DllMain( + HMODULE module, + DWORD reason_for_call, + LPVOID reseved +) +{ + if (reason_for_call == DLL_PROCESS_DETACH) { + finish_log(); + } + return TRUE; +} +#endif + +#ifdef __linux__ +void __attribute__ ((destructor)) unload(void) +{ + finish_log(); +} +#endif diff --git a/examples/telemetry/telemetry.def b/examples/telemetry/telemetry.def new file mode 100644 index 0000000..db28f3c --- /dev/null +++ b/examples/telemetry/telemetry.def @@ -0,0 +1,4 @@ +LIBRARY telemetry +EXPORTS +scs_telemetry_init=scs_telemetry_init +scs_telemetry_shutdown=scs_telemetry_shutdown diff --git a/examples/telemetry/telemetry.sln b/examples/telemetry/telemetry.sln new file mode 100644 index 0000000..a7001af --- /dev/null +++ b/examples/telemetry/telemetry.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "telemetry", "telemetry.vcproj", "{A682E7F2-5D47-4223-9605-CC75FFCB813C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A682E7F2-5D47-4223-9605-CC75FFCB813C}.Debug|Win32.ActiveCfg = Debug|Win32 + {A682E7F2-5D47-4223-9605-CC75FFCB813C}.Debug|Win32.Build.0 = Debug|Win32 + {A682E7F2-5D47-4223-9605-CC75FFCB813C}.Debug|x64.ActiveCfg = Debug|x64 + {A682E7F2-5D47-4223-9605-CC75FFCB813C}.Debug|x64.Build.0 = Debug|x64 + {A682E7F2-5D47-4223-9605-CC75FFCB813C}.Release|Win32.ActiveCfg = Release|Win32 + {A682E7F2-5D47-4223-9605-CC75FFCB813C}.Release|Win32.Build.0 = Release|Win32 + {A682E7F2-5D47-4223-9605-CC75FFCB813C}.Release|x64.ActiveCfg = Release|x64 + {A682E7F2-5D47-4223-9605-CC75FFCB813C}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/examples/telemetry/telemetry.vcproj b/examples/telemetry/telemetry.vcproj new file mode 100644 index 0000000..df50de3 --- /dev/null +++ b/examples/telemetry/telemetry.vcproj @@ -0,0 +1,333 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/telemetry_mem/telemetry_mem.cpp b/examples/telemetry_mem/telemetry_mem.cpp new file mode 100644 index 0000000..d61223d --- /dev/null +++ b/examples/telemetry_mem/telemetry_mem.cpp @@ -0,0 +1,485 @@ +/** + * @brief Example of using a shared memory to pass a mostly fixed set of telemetry + * data to another process. + * + * Note that use of the shared memory reduces precision of the output and + * increases latency between event and possible reaction. + */ + +// Windows stuff. + +#define WINVER 0x0500 +#define _WIN32_WINNT 0x0500 +#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) + +/** + * @name Callbacks remembered from the initialization info. + */ +//@{ +scs_telemetry_register_for_channel_t register_for_channel = NULL; +scs_telemetry_unregister_from_channel_t unregister_from_channel = NULL; +scs_log_t game_log = NULL; +//@} + +/** + * @brief Prints message to game log. + */ +void log_line(const scs_log_type_t type, const char *const text, ...) +{ + if (! game_log) { + return; + } + char formated[1000]; + + va_list args; + va_start(args, text); + vsnprintf_s(formated, sizeof(formated), _TRUNCATE, text, args); + formated[sizeof(formated) - 1] = 0; + va_end(args); + + game_log(type, formated); +} + +const size_t MAX_SUPPORTED_WHEEL_COUNT = 8; + +#pragma pack(push) +#pragma pack(1) + +/** + * @brief The layout of the shared memory. +*/ +struct telemetry_state_t +{ + scs_u8_t running; // Is the telemetry running or it is paused? + + scs_value_dplacement_t ws_truck_placement; // SCS_TELEMETRY_TRUCK_CHANNEL_world_placement + + scs_float_t speedometer_speed; // SCS_TELEMETRY_TRUCK_CHANNEL_speed + scs_float_t rpm; // SCS_TELEMETRY_TRUCK_CHANNEL_engine_rpm + scs_s32_t gear; // SCS_TELEMETRY_TRUCK_CHANNEL_engine_gear + + scs_float_t steering; // SCS_TELEMETRY_TRUCK_CHANNEL_effective_steering + scs_float_t throttle; // SCS_TELEMETRY_TRUCK_CHANNEL_effective_throttle + scs_float_t brake; // SCS_TELEMETRY_TRUCK_CHANNEL_effective_brake + scs_float_t clutch; // SCS_TELEMETRY_TRUCK_CHANNEL_effective_clutch + + scs_value_fvector_t linear_valocity; // SCS_TELEMETRY_TRUCK_CHANNEL_local_linear_velocity + scs_value_fvector_t angular_velocity; // SCS_TELEMETRY_TRUCK_CHANNEL_local_angular_velocity + scs_value_fvector_t linear_acceleration; // SCS_TELEMETRY_TRUCK_CHANNEL_local_linear_acceleration + scs_value_fvector_t angular_acceleration; // SCS_TELEMETRY_TRUCK_CHANNEL_local_angular_acceleration + scs_value_fvector_t cabin_angular_velocity; // SCS_TELEMETRY_TRUCK_CHANNEL_cabin_angular_velocity + scs_value_fvector_t cabin_angular_acceleration; // SCS_TELEMETRY_TRUCK_CHANNEL_cabin_angular_acceleration + + scs_u32_t wheel_count; // SCS_TELEMETRY_CONFIG_ATTRIBUTE_wheel_count + scs_float_t wheel_deflections[MAX_SUPPORTED_WHEEL_COUNT]; // SCS_TELEMETRY_TRUCK_CHANNEL_wheel_susp_deflection +}; + +#pragma pack(pop) + +/** + * @brief Handle of the memory mapping. + */ +HANDLE memory_mapping = NULL; + +/** + * @brief Block inside the shared memory. + */ +telemetry_state_t *shared_memory = NULL; + +/** + * @brief Deinitialize the shared memory objects. + */ +void deinitialize_shared_memory(void) +{ + if (shared_memory) { + UnmapViewOfFile(shared_memory); + shared_memory = NULL; + } + + if (memory_mapping) { + CloseHandle(memory_mapping); + memory_mapping = NULL; + } +} + +/** + * @brief Initialize the shared memory objects. + */ +bool initialize_shared_memory(void) +{ + // Setup the mapping. + + const DWORD memory_size = sizeof(telemetry_state_t); + memory_mapping = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE | SEC_COMMIT, 0, memory_size, "SCSTelemetryExample"); + if (! memory_mapping) { + log_line(SCS_LOG_TYPE_error, "Unable to create shared memory %08X", GetLastError()); + deinitialize_shared_memory(); + return false; + } + if (GetLastError() == ERROR_ALREADY_EXISTS) { + log_line(SCS_LOG_TYPE_error, "Shared memory is already in use."); + deinitialize_shared_memory(); + return false; + } + + shared_memory = static_cast(MapViewOfFile(memory_mapping, FILE_MAP_ALL_ACCESS, 0, 0, 0)); + if (! shared_memory) { + log_line(SCS_LOG_TYPE_error, "Unable to map the view %08X", GetLastError()); + deinitialize_shared_memory(); + return false; + } + + // Defaults in the structure. + + memset(shared_memory, 0, memory_size); + + // We are always initialized in the paused state. + + shared_memory->running = 0; + + // No wheels until we get corresponding configuration message. + + shared_memory->wheel_count = 0; + return true; +} + +/** + * @brief Float storage callback. + * + * Can be used together with SCS_TELEMETRY_CHANNEL_FLAG_no_value in which case it + * will store zero if the value is not available. + */ +SCSAPI_VOID telemetry_store_float(const scs_string_t name, const scs_u32_t index, const scs_value_t *const value, const scs_context_t context) +{ + assert(context); + scs_float_t *const storage = static_cast(context); + + if (value) { + assert(value->type == SCS_VALUE_TYPE_float); + *storage = value->value_float.value; + } + else { + *storage = 0.0f; + } +} + +/** + * @brief s32 storage callback. + * + * Can be used together with SCS_TELEMETRY_CHANNEL_FLAG_no_value in which case it + * will store zero if the value is not available. + */ +SCSAPI_VOID telemetry_store_s32(const scs_string_t name, const scs_u32_t index, const scs_value_t *const value, const scs_context_t context) +{ + assert(context); + scs_s32_t *const storage = static_cast(context); + + if (value) { + assert(value->type == SCS_VALUE_TYPE_s32); + *storage = value->value_s32.value; + } + else { + *storage = 0; + } +} + +/** + * @brief Orientation storage callback. + * + * Can be used together with SCS_TELEMETRY_CHANNEL_FLAG_no_value in which case it + * will store zero if the value is not available. + */ +SCSAPI_VOID telemetry_store_orientation(const scs_string_t name, const scs_u32_t index, const scs_value_t *const value, const scs_context_t context) +{ + assert(context); + scs_value_euler_t *const storage = static_cast(context); + + if (value) { + assert(value->type == SCS_VALUE_TYPE_euler); + *storage = value->value_euler; + } + else { + storage->heading = 0.0f; + storage->pitch = 0.0f; + storage->roll = 0.0f; + } +} + +/** + * @brief Vector storage callback. + * + * Can be used together with SCS_TELEMETRY_CHANNEL_FLAG_no_value in which case it + * will store zero if the value is not available. + */ +SCSAPI_VOID telemetry_store_fvector(const scs_string_t name, const scs_u32_t index, const scs_value_t *const value, const scs_context_t context) +{ + assert(context); + scs_value_fvector_t *const storage = static_cast(context); + + if (value) { + assert(value->type == SCS_VALUE_TYPE_fvector); + *storage = value->value_fvector; + } + else { + storage->x = 0.0f; + storage->y = 0.0f; + storage->z = 0.0f; + } +} + +/** + * @brief Placement storage callback. + * + * Can be used together with SCS_TELEMETRY_CHANNEL_FLAG_no_value in which case it + * will store zeros if the value is not available. + */ +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) +{ + assert(context); + scs_value_dplacement_t *const storage = static_cast(context); + + if (value) { + assert(value->type == SCS_VALUE_TYPE_dplacement); + *storage = value->value_dplacement; + } + else { + storage->position.x = 0.0; + storage->position.y = 0.0; + storage->position.z = 0.0; + storage->orientation.heading = 0.0f; + storage->orientation.pitch = 0.0f; + storage->orientation.roll = 0.0f; + } +} + +/** + * @brief Finds attribute with specified name in the configuration structure. + * + * Returns NULL if the attribute was not found or if it is not of the expected type. + */ +const scs_named_value_t *find_attribute(const scs_telemetry_configuration_t &configuration, const char *const name, const scs_u32_t index, const scs_value_type_t expected_type) +{ + for (const scs_named_value_t *current = configuration.attributes; current->name; ++current) { + if ((current->index != index) || (strcmp(current->name, name) != 0)) { + continue; + } + if (current->value.type == expected_type) { + return current; + } + log_line(SCS_LOG_TYPE_error, "Attribute %s has unexpected type %u", name, static_cast(current->value.type)); + break; + } + return NULL; +} + +/** + * @brief Called whenever the game pauses or unpauses its telemetry output. + */ +SCSAPI_VOID telemetry_pause(const scs_event_t event, const void *const UNUSED(event_info), const scs_context_t UNUSED(context)) +{ + shared_memory->running = (event == SCS_TELEMETRY_EVENT_started) ? 1 : 0; +} + +/** + * @brief Called whenever configuration changes. + */ +SCSAPI_VOID telemetry_configuration(const scs_event_t event, const void *const event_info, const scs_context_t UNUSED(context)) +{ + // We currently only care for the truck telemetry info. + + const struct scs_telemetry_configuration_t *const info = static_cast(event_info); + if (strcmp(info->id, SCS_TELEMETRY_CONFIG_truck) != 0) { + return; + } + + // Determine number of wheels up to the number supported by our memory structure. + + const scs_named_value_t *const wheel_count_attr = find_attribute(*info, SCS_TELEMETRY_CONFIG_ATTRIBUTE_wheel_count, SCS_U32_NIL, SCS_VALUE_TYPE_u32); + size_t wheel_count = wheel_count_attr ? wheel_count_attr->value.value_u32.value : 0; + if (wheel_count > MAX_SUPPORTED_WHEEL_COUNT) { + wheel_count = MAX_SUPPORTED_WHEEL_COUNT; + } + + // Update the wheel-related channel registrations to match. + + while (shared_memory->wheel_count > wheel_count) { + --shared_memory->wheel_count; + shared_memory->wheel_deflections[shared_memory->wheel_count] = 0.0f; + unregister_from_channel(SCS_TELEMETRY_TRUCK_CHANNEL_wheel_susp_deflection, shared_memory->wheel_count, SCS_VALUE_TYPE_float); + } + while (shared_memory->wheel_count < wheel_count) { + register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_wheel_susp_deflection, shared_memory->wheel_count, SCS_VALUE_TYPE_float, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_float, (shared_memory->wheel_deflections + shared_memory->wheel_count)); + ++shared_memory->wheel_count; + } +} + +/** + * @brief Telemetry API initialization function. + * + * 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 of the API. + + if (version != SCS_TELEMETRY_VERSION_1_00) { + return SCS_RESULT_unsupported; + } + const scs_telemetry_init_params_v100_t *const version_params = static_cast(params); + game_log = version_params->common.log; + + // Check application version. + + log_line(SCS_LOG_TYPE_message, "Game '%s' %u.%u", version_params->common.game_id, SCS_GET_MAJOR_VERSION(version_params->common.game_version), SCS_GET_MINOR_VERSION(version_params->common.game_version)); + + 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). + + if (version_params->common.game_version < SCS_TELEMETRY_EUT2_GAME_VERSION_1_03) { // Fixed the wheels.count attribute + log_line(SCS_LOG_TYPE_error, "Too old version of the game"); + game_log = NULL; + return SCS_RESULT_unsupported; + } + + if (version_params->common.game_version < SCS_TELEMETRY_EUT2_GAME_VERSION_1_07) { // Fixed the angular acceleration calculation + log_line(SCS_LOG_TYPE_warning, "This version of the game has less precise output of angular acceleration of the cabin"); + } + + // 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)) { + log_line(SCS_LOG_TYPE_warning, "Too new major version of the game, some features 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) { + log_line(SCS_LOG_TYPE_warning, "WARNING: Too old version of the game, some features 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)) { + log_line(SCS_LOG_TYPE_warning, "WARNING: Too new major version of the game, some features might behave incorrectly"); + } + } + else { + log_line(SCS_LOG_TYPE_warning, "Unsupported game, some features or values might behave incorrectly"); + } + + // 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_paused, telemetry_pause, NULL) == SCS_RESULT_ok) && + (version_params->register_for_event(SCS_TELEMETRY_EVENT_started, telemetry_pause, NULL) == SCS_RESULT_ok) && + (version_params->register_for_event(SCS_TELEMETRY_EVENT_configuration, telemetry_configuration, NULL) == SCS_RESULT_ok) + ; + if (! events_registered) { + + // Registrations created by unsuccessful initialization are + // cleared automatically so we can simply exit. + + log_line(SCS_LOG_TYPE_error, "Unable to register event callbacks"); + game_log = NULL; + return SCS_RESULT_generic_error; + } + + // Initialize the shared memory. + + if (! initialize_shared_memory()) { + log_line(SCS_LOG_TYPE_error, "Unable to initialize shared memory"); + game_log = NULL; + return SCS_RESULT_generic_error; + } + + // Register all changes we are interested in. Note that some wheel-related channels will be initialized when we + // receive a configuration event. 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 failures so the unsupported channels will remain at theirs default value. + +#define register_channel(name, index, type, field) version_params->register_for_channel(SCS_TELEMETRY_##name, index, SCS_VALUE_TYPE_##type, SCS_TELEMETRY_CHANNEL_FLAG_no_value, telemetry_store_##type, &shared_memory->field); + + register_channel(TRUCK_CHANNEL_world_placement, SCS_U32_NIL, dplacement, ws_truck_placement); + + register_channel(TRUCK_CHANNEL_speed, SCS_U32_NIL, float, speedometer_speed); + register_channel(TRUCK_CHANNEL_engine_rpm, SCS_U32_NIL, float, rpm); + register_channel(TRUCK_CHANNEL_engine_gear, SCS_U32_NIL, s32, gear); + + register_channel(TRUCK_CHANNEL_effective_steering, SCS_U32_NIL, float, steering); + register_channel(TRUCK_CHANNEL_effective_throttle, SCS_U32_NIL, float, throttle); + register_channel(TRUCK_CHANNEL_effective_brake, SCS_U32_NIL, float, brake); + register_channel(TRUCK_CHANNEL_effective_clutch, SCS_U32_NIL, float, clutch); + + register_channel(TRUCK_CHANNEL_local_linear_velocity, SCS_U32_NIL, fvector, linear_valocity); + register_channel(TRUCK_CHANNEL_local_angular_velocity, SCS_U32_NIL, fvector, angular_velocity); + register_channel(TRUCK_CHANNEL_local_linear_acceleration, SCS_U32_NIL, fvector, linear_acceleration); + register_channel(TRUCK_CHANNEL_local_angular_acceleration, SCS_U32_NIL, fvector, angular_acceleration); + register_channel(TRUCK_CHANNEL_cabin_angular_velocity, SCS_U32_NIL, fvector, cabin_angular_velocity); + register_channel(TRUCK_CHANNEL_cabin_angular_acceleration, SCS_U32_NIL, fvector, cabin_angular_acceleration); + +#undef register_channel + + // Remember other the functions we will use in the future. + + register_for_channel = version_params->register_for_channel; + unregister_from_channel = version_params->unregister_from_channel; + + // We are done. + + log_line(SCS_LOG_TYPE_message, "Memory telemetry example initialized"); + 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. + + deinitialize_shared_memory(); + + unregister_from_channel = NULL; + register_for_channel = NULL; + game_log = NULL; +} + +// Cleanup + +BOOL APIENTRY DllMain( + HMODULE module, + DWORD reason_for_call, + LPVOID reseved +) +{ + return TRUE; +} + +// EOF // diff --git a/examples/telemetry_mem/telemetry_mem.def b/examples/telemetry_mem/telemetry_mem.def new file mode 100644 index 0000000..0d3fc42 --- /dev/null +++ b/examples/telemetry_mem/telemetry_mem.def @@ -0,0 +1,4 @@ +LIBRARY telemetry_mem +EXPORTS +scs_telemetry_init=scs_telemetry_init +scs_telemetry_shutdown=scs_telemetry_shutdown diff --git a/examples/telemetry_mem/telemetry_mem.sln b/examples/telemetry_mem/telemetry_mem.sln new file mode 100644 index 0000000..b188777 --- /dev/null +++ b/examples/telemetry_mem/telemetry_mem.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "telemetry_mem", "telemetry_mem.vcproj", "{9BAFDA36-E52E-41FC-A143-C94B8FB0294B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9BAFDA36-E52E-41FC-A143-C94B8FB0294B}.Debug|Win32.ActiveCfg = Debug|Win32 + {9BAFDA36-E52E-41FC-A143-C94B8FB0294B}.Debug|Win32.Build.0 = Debug|Win32 + {9BAFDA36-E52E-41FC-A143-C94B8FB0294B}.Debug|x64.ActiveCfg = Debug|x64 + {9BAFDA36-E52E-41FC-A143-C94B8FB0294B}.Debug|x64.Build.0 = Debug|x64 + {9BAFDA36-E52E-41FC-A143-C94B8FB0294B}.Release|Win32.ActiveCfg = Release|Win32 + {9BAFDA36-E52E-41FC-A143-C94B8FB0294B}.Release|Win32.Build.0 = Release|Win32 + {9BAFDA36-E52E-41FC-A143-C94B8FB0294B}.Release|x64.ActiveCfg = Release|x64 + {9BAFDA36-E52E-41FC-A143-C94B8FB0294B}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/examples/telemetry_mem/telemetry_mem.vcproj b/examples/telemetry_mem/telemetry_mem.vcproj new file mode 100644 index 0000000..9210cd6 --- /dev/null +++ b/examples/telemetry_mem/telemetry_mem.vcproj @@ -0,0 +1,333 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/telemetry_position/Makefile b/examples/telemetry_position/Makefile new file mode 100644 index 0000000..1c33add --- /dev/null +++ b/examples/telemetry_position/Makefile @@ -0,0 +1,26 @@ +SDK_HEADERS=\ + ../../include/*.h \ + ../../include/common/*.h \ + ../../include/amtrucks/*.h \ + ../../include/eurotrucks2/*.h + +SDK_INCLUDES=\ + -I../../include \ + -I../../include/common/ \ + -I../../include/amtrucks/ \ + -I../../include/eurotrucks2 + +UNAME:= $(shell uname -s) + +ifeq ($(UNAME),Darwin) +LIB_NAME_OPTION=-install_name +else +LIB_NAME_OPTION=-soname +endif + +telemetry_position.so: *.cpp $(SDK_HEADERS) + gcc -o $@ -fPIC -Wall --shared -Wl,$(LIB_NAME_OPTION),$@ $(SDK_INCLUDES) *.cpp + +.PHONY: clean +clean: + @rm -f -- *.so diff --git a/examples/telemetry_position/telemetry_position.cpp b/examples/telemetry_position/telemetry_position.cpp new file mode 100644 index 0000000..30eb95d --- /dev/null +++ b/examples/telemetry_position/telemetry_position.cpp @@ -0,0 +1,429 @@ +/** + * @brief Logs calculated world space position of the head to demonstrate + * combination of telemetry channels and configuration data. + */ + +// Windows stuff. + +#ifdef _WIN32 +# define WINVER 0x0500 +# define _WIN32_WINNT 0x0500 +# include +#endif + +#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 Logging support. + */ +FILE *log_file = NULL; + +/** + * @brief Tracking of paused state of the game. + */ +bool output_paused = true; + +/** + * @brief Combined telemetry data. + */ +struct telemetry_state_t +{ + // Configuration. + + /** + * @brief Position of the cabin joint in vehicle space. + */ + scs_value_fvector_t cabin_position; + + /** + * @brief Base position of the head in the cabin. + */ + scs_value_fvector_t head_position; + + // Channels. + + /** + * @brief World space position & orientation of the truck. + */ + scs_value_dplacement_t truck_placement; + + /** + * @brief Offset of the cabin from the default position. + */ + scs_value_fplacement_t cabin_offset; + + /** + * @brief Offset of the head from its default position. + */ + scs_value_fplacement_t head_offset; + +} telemetry; + +/** + * @brief Function writting message to the game internal log. + */ +scs_log_t game_log = NULL; + +// Management of the log file. + +bool init_log(void) +{ + if (log_file) { + return true; + } + log_file = fopen("telemetry_position.log", "wt"); + if (! log_file) { + return false; + } + fprintf(log_file, "Log opened\n"); + return true; +} + +void finish_log(void) +{ + if (! log_file) { + return; + } + fprintf(log_file, "Log ended\n"); + fclose(log_file); + log_file = NULL; +} + +void log_print(const char *const text, ...) +{ + if (! log_file) { + return; + } + va_list args; + va_start(args, text); + vfprintf(log_file, text, args); + va_end(args); +} + +void log_line(const char *const text, ...) +{ + if (! log_file) { + return; + } + va_list args; + va_start(args, text); + vfprintf(log_file, text, args); + fprintf(log_file, "\n"); + va_end(args); +} + +/** + * @brief Adds two float vectors. + */ +scs_value_fvector_t add(const scs_value_fvector_t &first, const scs_value_fvector_t &second) +{ + scs_value_fvector_t result; + result.x = first.x + second.x; + result.y = first.y + second.y; + result.z = first.z + second.z; + return result; +} + +/** + * @brief Adds float vector to double vector. + */ +scs_value_dvector_t add(const scs_value_dvector_t &first, const scs_value_fvector_t &second) +{ + scs_value_dvector_t result; + result.x = first.x + second.x; + result.y = first.y + second.y; + result.z = first.z + second.z; + return result; +} + +/** + * @brief Rotates specified vector by specified orientation. + */ +scs_value_fvector_t rotate(const scs_value_euler_t &orientation, const scs_value_fvector_t &vector) +{ + const float heading_radians = orientation.heading * 6.2831853071795864769252867665590058f; + const float pitch_radians = orientation.pitch * 6.2831853071795864769252867665590058f; + const float roll_radians = orientation.roll * 6.2831853071795864769252867665590058f; + + const float cos_heading = cosf(heading_radians); + const float sin_heading = sinf(heading_radians); + const float cos_pitch = cosf(pitch_radians); + const float sin_pitch = sinf(pitch_radians); + const float cos_roll = cosf(roll_radians); + const float sin_roll = sinf(roll_radians); + + // Roll around Z axis. + + const float post_roll_x = vector.x * cos_roll - vector.y * sin_roll; + const float post_roll_y = vector.x * sin_roll + vector.y * cos_roll; + const float post_roll_z = vector.z; + + // Pitch around X axis. + + const float post_pitch_x = post_roll_x; + const float post_pitch_y = post_roll_y * cos_pitch - post_roll_z * sin_pitch; + const float post_pitch_z = post_roll_y * sin_pitch + post_roll_z * cos_pitch; + + // Heading around Y axis. + + scs_value_fvector_t result; + result.x = post_pitch_x * cos_heading + post_pitch_z * sin_heading; + result.y = post_pitch_y; + result.z = -post_pitch_x * sin_heading + post_pitch_z * cos_heading; + return result; +} + +// Handling of individual events. + +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; + } + + // Calculate the position. Note that the value differs slightly from the values used by the + // game for rendering. This code evaluates the value using data directly corresponding to + // simulation steps instead of interpolating between two neighbour simulated positions like + // the game does. It also calculates the value for each simulation frame instead of once per + // rendering frame like the game does. + + const scs_value_fvector_t head_position_in_cabin_space = add(telemetry.head_position, telemetry.head_offset.position); + const scs_value_fvector_t head_position_in_vehicle_space = add(add(telemetry.cabin_position, telemetry.cabin_offset.position), rotate(telemetry.cabin_offset.orientation, head_position_in_cabin_space)); + const scs_value_dvector_t head_position_in_world_space = add(telemetry.truck_placement.position, rotate(telemetry.truck_placement.orientation, head_position_in_vehicle_space)); + + log_line("%f;%f;%f", head_position_in_world_space.x, head_position_in_world_space.y, head_position_in_world_space.z); +} + +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); +} + + +/** + * @brief Finds attribute with specified name in the configuration structure. + * + * Returns NULL if the attribute was not found or if it is not of the expected type. + */ +const scs_named_value_t *find_attribute(const scs_telemetry_configuration_t &configuration, const char *const name, const scs_u32_t index, const scs_value_type_t expected_type) +{ + for (const scs_named_value_t *current = configuration.attributes; current->name; ++current) { + if ((current->index != index) || (strcmp(current->name, name) != 0)) { + continue; + } + if (current->value.type == expected_type) { + return current; + } + log_line("ERROR: Attribute %s has unexpected type %u", name, static_cast(current->value.type)); + break; + } + return NULL; +} + + +SCSAPI_VOID telemetry_configuration(const scs_event_t event, const void *const event_info, const scs_context_t UNUSED(context)) +{ + const struct scs_telemetry_configuration_t *const info = static_cast(event_info); + + // We currently only care for the truck telemetry info. + + if (strcmp(info->id, SCS_TELEMETRY_CONFIG_truck) != 0) { + return; + } + + // Extract the value we are interested in. + + const scs_named_value_t *const cabin_position = find_attribute(*info, SCS_TELEMETRY_CONFIG_ATTRIBUTE_cabin_position, SCS_U32_NIL, SCS_VALUE_TYPE_fvector); + if (cabin_position) { + telemetry.cabin_position = cabin_position->value.value_fvector; + } + else { + // Vehicle without separate cabin. + + telemetry.cabin_position.x = telemetry.cabin_position.y = telemetry.cabin_position.z = 0.0f; + } + + const scs_named_value_t *const head_position = find_attribute(*info, SCS_TELEMETRY_CONFIG_ATTRIBUTE_head_position, SCS_U32_NIL, SCS_VALUE_TYPE_fvector); + if (head_position) { + telemetry.head_position = head_position->value.value_fvector; + } + else { + log_line("WARNING: Head position unavailable"); + telemetry.head_position.x = telemetry.head_position.y = telemetry.head_position.z = 0.0f; + } +} + +// Handling of individual channels. + +SCSAPI_VOID telemetry_store_fplacement(const scs_string_t name, const scs_u32_t index, const scs_value_t *const value, const scs_context_t context) +{ + assert(context); + assert(value); + assert(value->type == SCS_VALUE_TYPE_fplacement); + scs_value_fplacement_t *const placement = static_cast(context); + *placement = value->value_fplacement; +} + +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) +{ + assert(context); + assert(value); + assert(value->type == SCS_VALUE_TYPE_dplacement); + scs_value_dplacement_t *const placement = static_cast(context); + *placement = value->value_dplacement; +} + +/** + * @brief Telemetry API initialization function. + * + * 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_1_00) { + return SCS_RESULT_unsupported; + } + + const scs_telemetry_init_params_v100_t *const version_params = static_cast(params); + if (! init_log()) { + version_params->common.log(SCS_LOG_TYPE_error, "Unable to initialize the log file"); + return SCS_RESULT_generic_error; + } + + // 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. + + log_line("Game '%s' %u.%u", version_params->common.game_id, SCS_GET_MAJOR_VERSION(version_params->common.game_version), SCS_GET_MINOR_VERSION(version_params->common.game_version)); + + 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) { + log_line("WARNING: Too old version of the game, some features 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)) { + log_line("WARNING: Too new major version of the game, some features 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) { + log_line("WARNING: Too old version of the game, some features 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)) { + log_line("WARNING: Too new major version of the game, some features might behave incorrectly"); + } + } + else { + log_line("WARNING: Unsupported game, some features or values might behave incorrectly"); + } + + // 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_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) && + (version_params->register_for_event(SCS_TELEMETRY_EVENT_configuration, telemetry_configuration, 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, "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.truck_placement); + version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_cabin_offset, SCS_U32_NIL, SCS_VALUE_TYPE_fplacement, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_fplacement, &telemetry.cabin_offset); + version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_head_offset , SCS_U32_NIL, SCS_VALUE_TYPE_fplacement, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_fplacement, &telemetry.head_offset); + + game_log = version_params->common.log; + + // Set the structure with defaults. + + memset(&telemetry, 0, sizeof(telemetry)); + + // 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 = NULL; + finish_log(); +} + +// Cleanup + +#ifdef _WIN32 +BOOL APIENTRY DllMain( + HMODULE module, + DWORD reason_for_call, + LPVOID reseved +) +{ + if (reason_for_call == DLL_PROCESS_DETACH) { + finish_log(); + } + return TRUE; +} +#endif + +#ifdef __linux__ +void __attribute__ ((destructor)) unload(void) +{ + finish_log(); +} +#endif diff --git a/examples/telemetry_position/telemetry_position.def b/examples/telemetry_position/telemetry_position.def new file mode 100644 index 0000000..ec1cc38 --- /dev/null +++ b/examples/telemetry_position/telemetry_position.def @@ -0,0 +1,4 @@ +LIBRARY telemetry_position +EXPORTS +scs_telemetry_init=scs_telemetry_init +scs_telemetry_shutdown=scs_telemetry_shutdown diff --git a/examples/telemetry_position/telemetry_position.sln b/examples/telemetry_position/telemetry_position.sln new file mode 100644 index 0000000..f5ab18d --- /dev/null +++ b/examples/telemetry_position/telemetry_position.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "telemetry_position", "telemetry_position.vcproj", "{C5A4192E-FD7E-45F4-8DDE-999C89A882D3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C5A4192E-FD7E-45F4-8DDE-999C89A882D3}.Debug|Win32.ActiveCfg = Debug|Win32 + {C5A4192E-FD7E-45F4-8DDE-999C89A882D3}.Debug|Win32.Build.0 = Debug|Win32 + {C5A4192E-FD7E-45F4-8DDE-999C89A882D3}.Debug|x64.ActiveCfg = Debug|x64 + {C5A4192E-FD7E-45F4-8DDE-999C89A882D3}.Debug|x64.Build.0 = Debug|x64 + {C5A4192E-FD7E-45F4-8DDE-999C89A882D3}.Release|Win32.ActiveCfg = Release|Win32 + {C5A4192E-FD7E-45F4-8DDE-999C89A882D3}.Release|Win32.Build.0 = Release|Win32 + {C5A4192E-FD7E-45F4-8DDE-999C89A882D3}.Release|x64.ActiveCfg = Release|x64 + {C5A4192E-FD7E-45F4-8DDE-999C89A882D3}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/examples/telemetry_position/telemetry_position.vcproj b/examples/telemetry_position/telemetry_position.vcproj new file mode 100644 index 0000000..8ede541 --- /dev/null +++ b/examples/telemetry_position/telemetry_position.vcproj @@ -0,0 +1,333 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/include/amtrucks/scssdk_ats.h b/include/amtrucks/scssdk_ats.h new file mode 100644 index 0000000..e6461ed --- /dev/null +++ b/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/include/amtrucks/scssdk_input_ats.h b/include/amtrucks/scssdk_input_ats.h new file mode 100644 index 0000000..4d82181 --- /dev/null +++ b/include/amtrucks/scssdk_input_ats.h @@ -0,0 +1,31 @@ +/** + * @file scssdk_input_ats.h + * + * @brief ATS input specific constants. + */ +#ifndef SCSSDK_INPUT_ATS_H +#define SCSSDK_INPUT_ATS_H + +#include "../scssdk.h" + +SCSSDK_HEADER + +/** + * @name Value used in the scs_sdk_init_params_t::game_version + * + * Changes in the major version indicate incompatible changes. + * Changes in the minor version indicate compatible changes (e.g. added more types) + * + * Changes: + * 1.00 - initial version + */ +//@{ +#define SCS_INPUT_ATS_GAME_VERSION_1_00 SCS_MAKE_VERSION(1, 0) +#define SCS_INPUT_ATS_GAME_VERSION_CURRENT SCS_INPUT_ATS_GAME_VERSION_1_00 +//@} + +SCSSDK_FOOTER + +#endif // SCSSDK_INPUT_ATS_H + +/* eof */ diff --git a/include/amtrucks/scssdk_telemetry_ats.h b/include/amtrucks/scssdk_telemetry_ats.h new file mode 100644 index 0000000..cf41e7b --- /dev/null +++ b/include/amtrucks/scssdk_telemetry_ats.h @@ -0,0 +1,77 @@ +/** + * @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 + * 1.04 - added differential lock, lift axle and hazard warning channels + * 1.05 - added multiplayer time offset and trailer body wear channel, fixed trailer chassis wear channel + */ +//@{ +#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_1_04 SCS_MAKE_VERSION(1, 4) // Patch 1.41 +#define SCS_TELEMETRY_ATS_GAME_VERSION_1_05 SCS_MAKE_VERSION(1, 5) // Patch 1.45 +#define SCS_TELEMETRY_ATS_GAME_VERSION_CURRENT SCS_TELEMETRY_ATS_GAME_VERSION_1_05 +//@} + +// 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/include/common/scssdk_telemetry_common_channels.h b/include/common/scssdk_telemetry_common_channels.h new file mode 100644 index 0000000..c4f3f69 --- /dev/null +++ b/include/common/scssdk_telemetry_common_channels.h @@ -0,0 +1,67 @@ +/** + * @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 Offset from the game_time simulated in the local economy to the + * game time of the Convoy multiplayer server. + * + * The value of this channel can change frequently during the Convoy + * session. For example when the user enters the desktop, the local + * economy time stops however the multiplayer time continues to run + * so the value will start to change. + * + * Represented in in-game minutes. Set to 0 when multiplayer is not active. + * + * Type: s32 + */ +#define SCS_TELEMETRY_CHANNEL_multiplayer_time_offset "multiplayer.time.offset" + +/** + * @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/include/common/scssdk_telemetry_common_configs.h b/include/common/scssdk_telemetry_common_configs.h new file mode 100644 index 0000000..8965752 --- /dev/null +++ b/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/include/common/scssdk_telemetry_common_gameplay_events.h b/include/common/scssdk_telemetry_common_gameplay_events.h new file mode 100644 index 0000000..9d6f018 --- /dev/null +++ b/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/include/common/scssdk_telemetry_job_common_channels.h b/include/common/scssdk_telemetry_job_common_channels.h new file mode 100644 index 0000000..018b6f4 --- /dev/null +++ b/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/include/common/scssdk_telemetry_trailer_common_channels.h b/include/common/scssdk_telemetry_trailer_common_channels.h new file mode 100644 index 0000000..50ec9df --- /dev/null +++ b/include/common/scssdk_telemetry_trailer_common_channels.h @@ -0,0 +1,80 @@ +/** + * @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_body "trailer.wear.body" +#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/include/common/scssdk_telemetry_truck_common_channels.h b/include/common/scssdk_telemetry_truck_common_channels.h new file mode 100644 index 0000000..d703a6c --- /dev/null +++ b/include/common/scssdk_telemetry_truck_common_channels.h @@ -0,0 +1,766 @@ +/** + * @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 Are the hazard warning light enabled? + * + * This represents the logical enable state of the hazard warning. + * It it is true as long it is enabled regardless of the physical + * enabled state of the light (i.e. it does not blink). + * + * Type: bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_hazard_warning "truck.hazard.warning" + +/** + * @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" + +/** + * @brief Is the differential lock enabled? + * + * Type: bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_differential_lock "truck.differential_lock" + +/** + * @brief Is the lift axle control set to lifted state? + * + * Type: bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_lift_axle "truck.lift_axle" + +/** + * @brief Is the lift axle indicator lit? + * + * Type: bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_lift_axle_indicator "truck.lift_axle.indicator" + +/** + * @brief Is the trailer lift axle control set to lifted state? + * + * Type: bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_trailer_lift_axle "truck.trailer.lift_axle" + +/** + * @brief Is the trailer lift axle indicator lit? + * + * Type: bool + */ +#define SCS_TELEMETRY_TRUCK_CHANNEL_trailer_lift_axle_indicator "truck.trailer.lift_axle.indicator" + +// 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/include/eurotrucks2/scssdk_eut2.h b/include/eurotrucks2/scssdk_eut2.h new file mode 100644 index 0000000..473ce76 --- /dev/null +++ b/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/include/eurotrucks2/scssdk_input_eut2.h b/include/eurotrucks2/scssdk_input_eut2.h new file mode 100644 index 0000000..ac0a3cc --- /dev/null +++ b/include/eurotrucks2/scssdk_input_eut2.h @@ -0,0 +1,31 @@ +/** + * @file scssdk_input_eut2.h + * + * @brief ETS2 input specific constants. + */ +#ifndef SCSSDK_INPUT_EUT2_H +#define SCSSDK_INPUT_EUT2_H + +#include "../scssdk.h" + +SCSSDK_HEADER + +/** + * @name Value used in the scs_sdk_init_params_t::game_version + * + * Changes in the major version indicate incompatible changes. + * Changes in the minor version indicate compatible changes (e.g. added more types) + * + * Changes: + * 1.00 - initial version + */ +//@{ +#define SCS_INPUT_EUT2_GAME_VERSION_1_00 SCS_MAKE_VERSION(1, 0) +#define SCS_INPUT_EUT2_GAME_VERSION_CURRENT SCS_INPUT_EUT2_GAME_VERSION_1_00 +//@} + +SCSSDK_FOOTER + +#endif // SCSSDK_INPUT_EUT2_H + +/* eof */ diff --git a/include/eurotrucks2/scssdk_telemetry_eut2.h b/include/eurotrucks2/scssdk_telemetry_eut2.h new file mode 100644 index 0000000..11d9430 --- /dev/null +++ b/include/eurotrucks2/scssdk_telemetry_eut2.h @@ -0,0 +1,107 @@ +/** + * @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 + * 1.17 - added differential lock, lift axle and hazard warning channels + * 1.18 - added multiplayer time offset and trailer body wear channel, fixed trailer chassis wear channel + */ +//@{ +#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_1_17 SCS_MAKE_VERSION(1, 17) // Patch 1.41 +#define SCS_TELEMETRY_EUT2_GAME_VERSION_1_18 SCS_MAKE_VERSION(1, 18) // Patch 1.45 +#define SCS_TELEMETRY_EUT2_GAME_VERSION_CURRENT SCS_TELEMETRY_EUT2_GAME_VERSION_1_18 +//@} + +// 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/include/scssdk.h b/include/scssdk.h new file mode 100644 index 0000000..e77865f --- /dev/null +++ b/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 conflicting object (e.g. callback for the specified event/channel, input device with the same name). +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/include/scssdk_input.h b/include/scssdk_input.h new file mode 100644 index 0000000..97b2ce8 --- /dev/null +++ b/include/scssdk_input.h @@ -0,0 +1,84 @@ +/** + * @file scssdk_input.h + * + * @brief Input SDK. + */ +#ifndef SCSSDK_INPUT_H +#define SCSSDK_INPUT_H + +#include "scssdk.h" +#include "scssdk_value.h" +#include "scssdk_input_device.h" + +SCSSDK_HEADER + +/** + * @name Versions of the input SDK + * + * Changes in the major version indicate incompatible changes in the API. + * Changes in the minor version indicate additions. + */ +//@{ +#define SCS_INPUT_VERSION_1_00 SCS_MAKE_VERSION(1, 0) +#define SCS_INPUT_VERSION_CURRENT SCS_INPUT_VERSION_1_00 +//@} + +// Structures used to pass additional data to the initialization function. + +/** + * @brief Common ancestor to all structures providing parameters to the input + * initialization. + */ +struct scs_input_init_params_t +{ + void method_indicating_this_is_not_a_c_struct(void); +}; + +/** + * @brief Initialization parameters for the 1.00 version of the input API. + */ +struct scs_input_init_params_v100_t : public scs_input_init_params_t +{ + /** + * @brief Common initialization parameters. + */ + scs_sdk_init_params_v100_t common; + + /** + * @name Function used to register input device. + */ + scs_input_register_device_t register_device; +}; +scs_check_size(scs_input_init_params_v100_t, 20, 40); + +// Functions which should be exported by the dynamic library serving as +// recipient of the input. + +/** + * @brief Initializes input support. + * + * This function must be provided by the library if it wants to support input 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. + * + * @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_input_init (const scs_u32_t version, const scs_input_init_params_t *const params); + +/** + * @brief Shuts down the input support. + * + * The engine will call this function if available and if the scs_input_init indicated + * success. + */ +SCSAPI_VOID scs_input_shutdown (void); + +SCSSDK_FOOTER + +#endif // SCSSDK_INPUT_H + +/* eof */ diff --git a/include/scssdk_input_device.h b/include/scssdk_input_device.h new file mode 100644 index 0000000..5dce5a2 --- /dev/null +++ b/include/scssdk_input_device.h @@ -0,0 +1,188 @@ +/** + * @file scssdk_input_device.h + * + * @brief Input SDK - devices. + */ +#ifndef SCSSDK_INPUT_DEVICE_H +#define SCSSDK_INPUT_DEVICE_H + +#include "scssdk.h" +#include "scssdk_value.h" +#include "scssdk_input_event.h" + +SCSSDK_HEADER + +/** + * @name Types of input devices. + */ +//@{ + +typedef scs_u32_t scs_input_device_type_t; +const scs_input_device_type_t SCS_INPUT_DEVICE_TYPE_INVALID = 0; + +/** + * @brief Generic device bindable in the game UI. + */ +const scs_input_device_type_t SCS_INPUT_DEVICE_TYPE_generic = 1; + +/** + * @brief Semantical device. + * + * The inputs of this device map directly to mixes with the same + * name the same way the Steam Input works. No binding UI is + * supported. This allows the device to work without user having + * to do any configuration however it also means that if the + * game mixes change, the user will be unable to adjust the binding + * and a plugin update will be required. + * + * Note that only subset of mixes are supported. If mix expression + * in a fresh controls.sii references something like "semantical.?0", + * then semantical input is likely supported for that mix. + */ +const scs_input_device_type_t SCS_INPUT_DEVICE_TYPE_semantical = 2; + +//@} + +/** + * @brief Maximal number of inputs allowed on a single device. + */ +const scs_u32_t SCS_INPUT_MAX_INPUT_COUNT = 400; + +/** + * @brief Information about a single input of the input device. + */ +struct scs_input_device_input_t +{ + /** + * @brief Name of this input used in the configuration file + * + * This string can contain only the following characters: + * @li lower-cased english letters + * @li digits + * @li underscore + */ + scs_string_t name; + + /** + * @brief Name of the input shown to the user. + * + * Currently only the following characters are allowed: + * @li English letters + * @li digits + * @li underscore + * @li space + * @li dot + */ + scs_string_t display_name; + + /** + * @brief Type of the value provided by this input. + * + * Only the following value types are supported: + * @li SCS_VALUE_TYPE_bool + * @li SCS_VALUE_TYPE_float + */ + scs_value_type_t value_type; + +#ifdef SCS_ARCHITECTURE_x64 + /** + * @brief Explicit 8-byte alignment for structure size. + */ + scs_u32_t _padding; +#endif +}; +scs_check_size(scs_input_device_input_t, 12, 24); + +/** + * @brief Type of function called to notify about changes in device activity state + * + * @param active Nonzero if the device is active and processing events. + * @param context Context information passed during device registration. + */ +typedef SCSAPI_VOID_FPTR(scs_input_active_callback_t)(const scs_u8_t active, const scs_context_t context); + +/** + * @brief Input device. + */ +struct scs_input_device_t +{ + /** + * @brief Name of this device used in the configuration file + * + * Must be unique among all input plugins. + * + * This string can contain only the following characters: + * @li lower-cased English letters + * @li digits + * @li underscore + */ + scs_string_t name; + + /** + * @brief Name of the device shown to the user. + * + * Currently only the following characters are allowed: + * @li English letters + * @li digits + * @li underscore + * @li space + * @li dot + */ + scs_string_t display_name; + + /** + * @brief Type of this device. + */ + scs_input_device_type_t type; + + /** + * @brief Number of inputs in the inputs array. + * + * There must be at least one input. + */ + scs_u32_t input_count; + + /** + * @brief Individual inputs. + */ + const scs_input_device_input_t *inputs; + + /** + * @brief Context value to provide to the callbacks. + */ + scs_context_t callback_context; + + /** + * @brief Callback called when device activity state changes + * + * Optional + */ + scs_input_active_callback_t input_active_callback; + + /** + * @brief Callback to call to retrieve input events. + * + * Only called when the device is active. + * + * Required + */ + scs_input_event_callback_t input_event_callback; +}; +scs_check_size(scs_input_device_t, 32, 56); + +/** + * @brief Registers a input device + * + * This function can be only called from scs_input_init. Devices are automatically unregistered before + * calling scs_input_shutdown. + * + * @param device_info Information about the device. The structure is fully processed during the call. + * @return SCS_RESULT_ok on successful registration. Error code otherwise. + */ +typedef SCSAPI_RESULT_FPTR(scs_input_register_device_t)(const scs_input_device_t *const device_info); + +SCSSDK_FOOTER + +#endif // SCSSDK_INPUT_DEVICE_H + +/* eof */ diff --git a/include/scssdk_input_event.h b/include/scssdk_input_event.h new file mode 100644 index 0000000..8a70e21 --- /dev/null +++ b/include/scssdk_input_event.h @@ -0,0 +1,68 @@ +/** + * @file scssdk_input_event.h + * + * @brief Input SDK - events. + */ +#ifndef SCSSDK_INPUT_EVENT_H +#define SCSSDK_INPUT_EVENT_H + +#include "scssdk.h" +#include "scssdk_value.h" +#include "scssdk_input_event.h" + +SCSSDK_HEADER + +/** + * @brief Information about a input event. + */ +struct scs_input_event_t +{ + /** + * @brief Zero-based index of input this event is for. + */ + scs_u32_t input_index; + + /** + * @brief The event value. + * + * Must use the value type corresponding to the value_type the input was registered with. + */ + union { + scs_value_bool_t value_bool; + scs_value_float_t value_float; + float _sizing_for_future_extensions[6]; + }; +}; +scs_check_size(scs_input_event_t, 28, 28); + +/** + * @brief Indicates that this is the first call of this callback for the current device in the current frame. + */ +const scs_u32_t SCS_INPUT_EVENT_CALLBACK_FLAG_first_in_frame = 0x00000001; + +/** + * @brief Indicates that this is the first call of this callback after device became active. + * + * When this flag is set, the first_in_frame flag will be set as well. + */ +const scs_u32_t SCS_INPUT_EVENT_CALLBACK_FLAG_first_after_activation = 0x00000002; + +/** + * @brief Type of function called to retrieve next event from the input device + * + * This function is called on the main thread and must be quick. In each frame where the + * device is active, this function will be called repeatedly until it returns SCS_RESULT_not_found. + * + * @param[out] event_info Store the event info here. Ignored if the function returns anything other than SCS_RESULT_ok. + * @param flags Combination of relevant SCS_INPUT_EVENT_CALLBACK_FLAG_* flags. + * @param context Context information passed during device registration. + * @return SCS_RESULT_ok when event was retrieved, SCS_RESULT_not_found when there was no event. Any other value + * will disconnect the device and prevent future related callbacks. + */ +typedef SCSAPI_RESULT_FPTR(scs_input_event_callback_t)(scs_input_event_t *const event_info, const scs_u32_t flags, const scs_context_t context); + +SCSSDK_FOOTER + +#endif // SCSSDK_INPUT_EVENT_H + +/* eof */ diff --git a/include/scssdk_telemetry.h b/include/scssdk_telemetry.h new file mode 100644 index 0000000..626d421 --- /dev/null +++ b/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/include/scssdk_telemetry_channel.h b/include/scssdk_telemetry_channel.h new file mode 100644 index 0000000..019fe4f --- /dev/null +++ b/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/include/scssdk_telemetry_event.h b/include/scssdk_telemetry_event.h new file mode 100644 index 0000000..5864f4c --- /dev/null +++ b/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/include/scssdk_value.h b/include/scssdk_value.h new file mode 100644 index 0000000..364aebb --- /dev/null +++ b/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/readme.txt b/readme.txt new file mode 100644 index 0000000..2fa59d6 --- /dev/null +++ b/readme.txt @@ -0,0 +1,63 @@ +========== 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. + +If more than one API is provided by a plugin, the order in which theirs initialization, shutdown or other callbacks happen is undefined unless explicitly stated otherwise. + +========== 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. + +========== Input API ========== + +Input API provides basic support for adding custom simple input devices to the game without having to use vJoy or similar joystick emulators. It is meant for use cases like additional button boxes and currently does not support dynamic addition and removal of input devices. + +===== 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/sdk_license.txt b/sdk_license.txt new file mode 100644 index 0000000..ef8bc63 --- /dev/null +++ b/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.