-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
24 changed files
with
3,398 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
SDK_HEADERS=\ | ||
../sdk/include/*.h \ | ||
../sdk/include/common/*.h \ | ||
../sdk/include/amtrucks/*.h \ | ||
../sdk/include/eurotrucks2/*.h | ||
|
||
SDK_INCLUDES=\ | ||
-I../sdk/include \ | ||
-I../sdk/include/common/ \ | ||
-I../sdk/include/amtrucks/ \ | ||
-I../sdk/include/eurotrucks2 | ||
|
||
all: telemetry.so read_util | ||
|
||
telemetry.so: telemetry_mem.cpp $(SDK_HEADERS) | ||
g++ -o $@ -fPIC -Wall --shared -Wl,-soname,$@ $(SDK_INCLUDES) telemetry_mem.cpp -lrt | ||
|
||
read_util: read_util.cpp $(SDK_HEADERS) | ||
g++ -o $@ -Wall $(SDK_INCLUDES) read_util.cpp -lrt | ||
|
||
.PHONY: clean | ||
clean: | ||
@rm -f -- *.so read_util |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# ETS2 Local Radio unofficial Linux port | ||
|
||
## Installation instructions | ||
|
||
In this directory, run the command: | ||
|
||
```bash | ||
make all && ./install.sh | ||
``` | ||
|
||
You will need to copy the plugin `telemetry.so` manually to your plugins directory if ETS is not installed via Steam on your main partition, or if you are using ATS. | ||
|
||
## Running | ||
|
||
Go to the `server` directory and run: | ||
|
||
```bash | ||
python3 main.py | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
#!/usr/bin/env bash | ||
|
||
cp read_util "../server" | ||
cp telemetry.so "$HOME/.local/share/Steam/steamapps/common/Euro Truck Simulator 2/bin/linux_x64/plugins" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
/** | ||
* @brief Utility that will output the value of the position in the shared memory. | ||
*/ | ||
|
||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include <sys/mman.h> | ||
#include <unistd.h> | ||
#include <fcntl.h> | ||
|
||
// SDK | ||
|
||
#include "scssdk_telemetry.h" | ||
#include "eurotrucks2/scssdk_eut2.h" | ||
#include "eurotrucks2/scssdk_telemetry_eut2.h" | ||
#include "amtrucks/scssdk_ats.h" | ||
#include "amtrucks/scssdk_telemetry_ats.h" | ||
|
||
|
||
static char shm_name[] = "/ets2radiolinux"; | ||
|
||
void print_position(scs_value_dplacement_t placement) { | ||
scs_value_dvector_t pos = placement.position; | ||
fprintf(stdout, "%f,%f,%f\n", pos.x, pos.y, pos.z); | ||
fflush(stdout); | ||
} | ||
|
||
int main() { | ||
// We only need one page of memory | ||
size_t MAP_SIZE = getpagesize(); | ||
|
||
// Open shared memory | ||
int handle = shm_open(shm_name, O_RDONLY, S_IRUSR); | ||
if (handle < 0) { | ||
fprintf(stderr, "ERR Could not open shared memory\n"); | ||
return 1; | ||
} | ||
|
||
void* mapped_region = mmap( | ||
0, | ||
MAP_SIZE, | ||
PROT_READ, | ||
MAP_SHARED, | ||
handle, | ||
0 | ||
); | ||
|
||
if (mapped_region == MAP_FAILED) { | ||
fprintf(stderr, "ERR Could not mmap shared memory\n"); | ||
return 1; | ||
} | ||
|
||
scs_value_dplacement_t current; | ||
memset(¤t, 0, sizeof(scs_value_dplacement_t)); | ||
while (1) { | ||
memcpy(¤t, mapped_region, MAP_SIZE); | ||
print_position(current); | ||
usleep(500000); | ||
} | ||
|
||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,268 @@ | ||
/** | ||
* @brief TODO | ||
*/ | ||
|
||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <assert.h> | ||
#include <stdarg.h> | ||
#include <string.h> | ||
#include <sys/mman.h> | ||
#include <unistd.h> | ||
#include <fcntl.h> | ||
|
||
// SDK | ||
|
||
#include "scssdk_telemetry.h" | ||
#include "eurotrucks2/scssdk_eut2.h" | ||
#include "eurotrucks2/scssdk_telemetry_eut2.h" | ||
#include "amtrucks/scssdk_ats.h" | ||
#include "amtrucks/scssdk_telemetry_ats.h" | ||
|
||
#define UNUSED(x) | ||
|
||
/** | ||
* @brief Tracking of paused state of the game. | ||
*/ | ||
bool output_paused = true; | ||
|
||
/** | ||
* @brief Useful telemetry data. | ||
*/ | ||
struct telemetry_state_t | ||
{ | ||
scs_value_dplacement_t world_position; | ||
} telemetry; | ||
|
||
/** | ||
* @brief Time since last mmap | ||
*/ | ||
scs_timestamp_t elapsed = 0; | ||
scs_timestamp_t last_timestamp = -1; | ||
static scs_timestamp_t update_interval = 5e5; // every 0.5s | ||
|
||
/** | ||
* @brief Opaque pointer to use for mmap | ||
*/ | ||
void* mapped_region = NULL; | ||
size_t MAP_SIZE = -1; | ||
|
||
static char shm_name[] = "/ets2radiolinux"; | ||
|
||
/** | ||
* @brief Function writing message to the game internal log. | ||
*/ | ||
scs_log_t game_log = NULL; | ||
|
||
// Handling of individual events. | ||
|
||
SCSAPI_VOID telemetry_frame_start(const scs_event_t UNUSED(event), const void *const event_info, const scs_context_t UNUSED(context)) | ||
{ | ||
const struct scs_telemetry_frame_start_t* const info = static_cast<const scs_telemetry_frame_start_t *>(event_info); | ||
|
||
if (last_timestamp == static_cast<scs_timestamp_t>(-1)) { | ||
last_timestamp = 0; | ||
} | ||
|
||
// The timer might be sometimes restarted (e.g. after load) while | ||
// we want to provide continuous time on our output. | ||
if (info->flags & SCS_TELEMETRY_FRAME_START_FLAG_timer_restart) { | ||
last_timestamp = 0; | ||
} | ||
|
||
elapsed += info->paused_simulation_time - last_timestamp; | ||
last_timestamp = info->paused_simulation_time; | ||
} | ||
|
||
SCSAPI_VOID telemetry_frame_end(const scs_event_t UNUSED(event), const void *const UNUSED(event_info), const scs_context_t UNUSED(context)) | ||
{ | ||
if (output_paused) { | ||
return; | ||
} | ||
|
||
// We only copy once per interval, to prevent loads of unneccessary writes | ||
if (elapsed < update_interval) { | ||
return; | ||
} | ||
|
||
elapsed -= update_interval; | ||
|
||
// Copy telemetry data, after zeroing dest | ||
memset(mapped_region, 0, MAP_SIZE); | ||
memcpy(mapped_region, &telemetry, sizeof(telemetry)); | ||
} | ||
|
||
SCSAPI_VOID telemetry_pause(const scs_event_t event, const void *const UNUSED(event_info), const scs_context_t UNUSED(context)) | ||
{ | ||
output_paused = (event == SCS_TELEMETRY_EVENT_paused); | ||
} | ||
|
||
// Handling of individual channels. | ||
|
||
SCSAPI_VOID telemetry_store_dplacement(const scs_string_t name, const scs_u32_t index, const scs_value_t *const value, const scs_context_t context) | ||
{ | ||
if (!context) { | ||
game_log(SCS_LOG_TYPE_error, "Local radio: no context!"); | ||
return; | ||
} | ||
if (!value) { | ||
game_log(SCS_LOG_TYPE_error, "Local radio: no value!"); | ||
return; | ||
} | ||
if (value->type != SCS_VALUE_TYPE_dplacement) { | ||
game_log(SCS_LOG_TYPE_error, "Local radio: value wrong type!"); | ||
return; | ||
} | ||
|
||
scs_value_dplacement_t *const placement = static_cast<scs_value_dplacement_t *>(context); | ||
*placement = value->value_dplacement; | ||
} | ||
|
||
/** | ||
* @brief Telemetry API initialization function. | ||
* | ||
* ENTRY POINT. | ||
* | ||
* See scssdk_telemetry.h | ||
*/ | ||
SCSAPI_RESULT scs_telemetry_init(const scs_u32_t version, const scs_telemetry_init_params_t *const params) | ||
{ | ||
// We currently support only one version. | ||
|
||
if (version != SCS_TELEMETRY_VERSION_CURRENT) { | ||
return SCS_RESULT_unsupported; | ||
} | ||
|
||
const scs_telemetry_init_params_v101_t *const version_params = static_cast<const scs_telemetry_init_params_v101_t *>(params); | ||
|
||
// Remember the function we will use for logging. | ||
game_log = version_params->common.log; | ||
game_log(SCS_LOG_TYPE_message, "Local radio: begin initialization"); | ||
|
||
// Check application version. Note that this example uses fairly basic channels which are likely to be supported | ||
// by any future SCS trucking game however more advanced application might want to at least warn the user if there | ||
// is game or version they do not support. | ||
if (strcmp(version_params->common.game_id, SCS_GAME_ID_EUT2) == 0) { | ||
|
||
// Below the minimum version there might be some missing features (only minor change) or | ||
// incompatible values (major change). | ||
|
||
const scs_u32_t MINIMAL_VERSION = SCS_TELEMETRY_EUT2_GAME_VERSION_1_00; | ||
if (version_params->common.game_version < MINIMAL_VERSION) { | ||
// Might behave incorrectly | ||
} | ||
|
||
// Future versions are fine as long the major version is not changed. | ||
|
||
const scs_u32_t IMPLEMENTED_VERSION = SCS_TELEMETRY_EUT2_GAME_VERSION_CURRENT; | ||
if (SCS_GET_MAJOR_VERSION(version_params->common.game_version) > SCS_GET_MAJOR_VERSION(IMPLEMENTED_VERSION)) { | ||
// Might behave incorrectly | ||
} | ||
} | ||
else if (strcmp(version_params->common.game_id, SCS_GAME_ID_ATS) == 0) { | ||
|
||
// Below the minimum version there might be some missing features (only minor change) or | ||
// incompatible values (major change). | ||
|
||
const scs_u32_t MINIMAL_VERSION = SCS_TELEMETRY_ATS_GAME_VERSION_1_00; | ||
if (version_params->common.game_version < MINIMAL_VERSION) { | ||
// Might behave incorrectly | ||
} | ||
|
||
// Future versions are fine as long the major version is not changed. | ||
|
||
const scs_u32_t IMPLEMENTED_VERSION = SCS_TELEMETRY_ATS_GAME_VERSION_CURRENT; | ||
if (SCS_GET_MAJOR_VERSION(version_params->common.game_version) > SCS_GET_MAJOR_VERSION(IMPLEMENTED_VERSION)) { | ||
// Might behave incorrectly | ||
} | ||
} | ||
else { | ||
// Unsupported game | ||
} | ||
|
||
// Register for events. Note that failure to register those basic events | ||
// likely indicates invalid usage of the api or some critical problem. As the | ||
// example requires all of them, we can not continue if the registration fails. | ||
|
||
const bool events_registered = | ||
(version_params->register_for_event(SCS_TELEMETRY_EVENT_frame_start, telemetry_frame_start, NULL) == SCS_RESULT_ok) && | ||
(version_params->register_for_event(SCS_TELEMETRY_EVENT_frame_end, telemetry_frame_end, NULL) == SCS_RESULT_ok) && | ||
(version_params->register_for_event(SCS_TELEMETRY_EVENT_paused, telemetry_pause, NULL) == SCS_RESULT_ok) && | ||
(version_params->register_for_event(SCS_TELEMETRY_EVENT_started, telemetry_pause, NULL) == SCS_RESULT_ok) | ||
; | ||
if (! events_registered) { | ||
|
||
// Registrations created by unsuccessfull initialization are | ||
// cleared automatically so we can simply exit. | ||
|
||
version_params->common.log(SCS_LOG_TYPE_error, "Local radio: unable to register event callbacks"); | ||
return SCS_RESULT_generic_error; | ||
} | ||
|
||
// Register for channels. The channel might be missing if the game does not support | ||
// it (SCS_RESULT_not_found) or if does not support the requested type | ||
// (SCS_RESULT_unsupported_type). For purpose of this example we ignore the failues | ||
// so the unsupported channels will remain at theirs default value. | ||
version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_world_placement, SCS_U32_NIL, SCS_VALUE_TYPE_dplacement, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_dplacement, &telemetry.world_position); | ||
|
||
// Set the structure with defaults. | ||
memset(&telemetry, 0, sizeof(telemetry)); | ||
|
||
// We only need one page of memory | ||
MAP_SIZE = getpagesize(); | ||
|
||
// Open shared memory | ||
int handle = shm_open(shm_name, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); | ||
if (handle < 0) { | ||
game_log(SCS_LOG_TYPE_error, "Local radio: could not open shared memory"); | ||
return SCS_RESULT_generic_error; | ||
} | ||
|
||
if (ftruncate(handle, MAP_SIZE) < 0) { | ||
game_log(SCS_LOG_TYPE_error, "Local radio: could not truncate shared memery"); | ||
return SCS_RESULT_generic_error; | ||
} | ||
|
||
mapped_region = mmap( | ||
0, | ||
MAP_SIZE, | ||
PROT_READ | PROT_WRITE, | ||
MAP_SHARED, | ||
handle, | ||
0 | ||
); | ||
|
||
if (mapped_region == MAP_FAILED) { | ||
game_log(SCS_LOG_TYPE_error, "Local radio: could not mmap shared memory"); | ||
return SCS_RESULT_generic_error; | ||
} | ||
|
||
game_log(SCS_LOG_TYPE_message, "Local radio: initialized successfully"); | ||
|
||
// Initially the game is paused. | ||
output_paused = true; | ||
return SCS_RESULT_ok; | ||
} | ||
|
||
/** | ||
* @brief Telemetry API deinitialization function. | ||
* | ||
* See scssdk_telemetry.h | ||
*/ | ||
SCSAPI_VOID scs_telemetry_shutdown(void) | ||
{ | ||
// Any cleanup needed. The registrations will be removed automatically | ||
// so there is no need to do that manually. | ||
game_log(SCS_LOG_TYPE_message, "Local radio: shutdown"); | ||
|
||
game_log = NULL; | ||
shm_unlink(shm_name); | ||
mapped_region = NULL; | ||
} | ||
|
||
// Cleanup | ||
|
||
void __attribute__ ((destructor)) unload(void) | ||
{ | ||
scs_telemetry_shutdown(); | ||
} |
Oops, something went wrong.