Git Product home page Git Product logo

rust-server's Introduction

DCS gRPC Server

DCS gRPC is an RPC (Remote Procedure Call) server that allows network clients to interact with a currently running mission on a DCS server.

Installation

Download

Download the latest version of the server from the Releases and extract the zip file into your DCS Server directory.

This is typically found in C:\Users\USERNAME\Saved Games\DCS.openbeta_server. Once extracted you will have a Scripts\DCS-gRPC folder, a Mods\Tech\DCS-gRPC folder, and a Scripts\Hooks\DCS-gRPC.lua file in your server folder. As well as these scripts there will be a Docs/DCS-gRPC folder containing documentation and a Tools/DCS-gRPC folder containing client tools.

Prepare DCS

To make the gRPC server available in the mission scripting environment, add the following line to your MissionScripting.lua file that is found by default in the DCS World install folder at C:\Program Files\Eagle Dynamics\DCS World\Scripts\MissionScripting.lua. If you installed the server in another location then look for the Scripts\MissionScripting.lua file in there.

Note: The following file is in diff format to highlight the line that has been added. Do note include the + symbol when you paste into the MissionScripting.lua file.

  --Initialization script for the Mission lua Environment (SSE)

  dofile('Scripts/ScriptingSystem.lua')
+ dofile(lfs.writedir()..[[Scripts\DCS-gRPC\grpc-mission.lua]])

  --Sanitize Mission Scripting environment
  --This makes unavailable some unsecure functions.
  --Mission downloaded from server to client may contain potentialy harmful lua code that may use these functions.
  --You can remove the code below and make availble these functions at your own risk.

  local function sanitizeModule(name)
    _G[name] = nil
    package.loaded[name] = nil
  end

  do
    sanitizeModule('os')
    sanitizeModule('io')
    sanitizeModule('lfs')
    _G['require'] = nil
    _G['loadlib'] = nil
    _G['package'] = nil
  end

Running DCS-gRPC

There are two ways of running DCS-gRPC. One way allows it to run regardless of what mission is running and the other means that DCS-gRPC will only run if the mission scripting itself enables it.

Running regardless of mission

Create the file Saved Games\DCS\Config\dcs-grpc.lua and add the line below

autostart = true

As well as this you can set other options in this file. All options and the config file itself are optional โ€“ you only have to create the config file and set an option if you want to change any of the default values (the list below shows the default values). The available options are:

-- Whether the `Eval` method is enabled or not.
evalEnabled = false

-- The host the gRPC listens on (use "0.0.0.0" to listen on all IP addresses of the host).
host = "127.0.0.1"

-- The port to listen on.
port = 50051

-- Whether debug logging is enabled or not.
debug = false

-- Limit of calls per second that are executed inside of the mission scripting environment.
throughputLimit = 600

-- Whether the integrity check, meant to spot installation issues, is disabled.
integrityCheckDisabled = false

-- The default TTS provider to use if a TTS request does not explicitly specify another one.
tts.defaultProvider = "win"

-- Your Azure access key.
tts.provider.azure.key = "..."

-- The Azure region to use (see https://learn.microsoft.com/en-us/azure/cognitive-services/speech-service/regions).
tts.provider.azure.region = "westeurope"

-- The default Azure voice to use (see https://learn.microsoft.com/azure/cognitive-services/speech-service/language-support).
tts.provider.azure.defaultVoice = "en-US-AriaNeural"

-- Your AWS key.
tts.provider.aws.key = "..."

-- Your AWS secret key.
tts.provider.aws.secret = "..."

-- Your AWS region (see https://docs.aws.amazon.com/general/latest/gr/pol.html).
tts.provider.aws.region = "eu-central-1"

-- The default AWS voice to use (see https://docs.aws.amazon.com/polly/latest/dg/voicelist.html).
tts.provider.aws.defaultVoice = "Brian"

-- Your Google Cloud access key.
tts.provider.gcloud.key = "..."

-- The default Google Cloud voice to use (see https://cloud.google.com/text-to-speech/docs/voices).
tts.provider.gcloud.defaultVoice = "en-GB-Neural2-A"

-- The default Windows voice to use (see https://support.microsoft.com/en-us/windows/appendix-a-supported-languages-and-voices-4486e345-7730-53da-fcfe-55cc64300f01).
-- Requires at least Windows Server 2019 to work properly.
tts.provider.win.defaultVoice = "David"

-- Your SRS server's address.
srs.addr = "127.0.0.1:5002"

Once you have done this start the DCS server and skip to the "Confirming that DCS-gRPC is running" section of this README.

Running only if the mission scripting enables it

Make sure that the file Saved Games\DCS\Config\dcs-grpc.lua does not exist (Delete if it does).

Add the following code to your mission. This will start the DCS-gRPC server. You can add this code to a DO SCRIPT trigger in your .miz file or you can add this code to an existing lua file that your mission may be running.

-- Load the gRPC server into the mission
GRPC.load()

As well as this you can set other options in the script before GRPC.Load() . These are listed below:

-- Whether the `Eval` method is enabled or not.
GRPC.evalEnabled = false

-- The host the gRPC listens on (use "0.0.0.0" to listen on all IP addresses of the host).
GRPC.host = '127.0.0.1'

-- The port to listen on.
GRPC.port = 50051

-- Whether debug logging is enabled or not.
GRPC.debug = false

-- Limit of calls per second that are executed inside of the mission scripting environment.
GRPC.throughputLimit = 600

For example:

GRPC.host = '0.0.0.0'
GRPC.load()

Confirming that DCS-gRPC is running

To confirm that the server is running check the \Logs\dcs.log file and look for entries prefixed with GRPC. You can also check for the present of a \Logs\grpc.log file.

The server will be running on port 50051 by default.

Lua API

DCS-gRPC provides the following Lua APIs to interact with the server from within Lua.

  • GRPC.tts(ssml, frequency[, options]) - Synthesize text (ssml; SSML tags supported) to speech and transmit it over SRS on the frequency with the following optional options (and their defaults):
    {
        -- The plain text without any transformations made to it for the purpose of getting it spoken out
        -- as desired (no SSML tags, no FOUR NINER instead of 49, ...). Even though this field is
        -- optional, please consider providing it as it can be used to display the spoken text to players
        -- with hearing impairments.
        plaintext = null, -- e.g. `= "Hello Pilot"`
    
        -- Name of the SRS client.
        srsClientName = "DCS-gRPC",
    
        -- The origin of the transmission. Relevant if the SRS server has "Line of
        -- Sight" and/or "Distance Limit" enabled.
        position = {
            lat = 0.0,
            lon = 0.0,
            alt = 0.0, -- in meters
        },
    
        -- The coalition of the transmission. Relevant if the SRS server has "Secure
        -- Coalition Radios" enabled. Supported values are: `blue` and `red`. Defaults
        -- to being spectator if not specified.
        coalition = null,
    
        -- TTS provider to be use. Defaults to the one configured in your config or to Windows'
        -- built-in TTS. Examples:
        -- `= { aws = {} }` / `= { aws = { voice = "..." } }` enable AWS TTS
        -- `= { azure = {} }` / `= { azure = { voice = "..." } }` enable Azure TTS
        -- `= { gcloud = {} }` / `= { gcloud = { voice = "..." } }` enable Google Cloud TTS
        -- `= { win = {} }` / `= { win = { voice = "..." } }` enable Windows TTS
        provider = null,
    }

Client Development

DCS-gRPC, as the name implies, uses the gRPC framework to handle communication between clients and the server. gRPC supports a wide variety of languages which allows you to develop clients in the languages of your choice.

In order to develop clients for DCS-gRPC you must be familiar with gRPC concepts so we recommend reading the gRPC documentation for your language.

The gRPC .proto files are available in the Docs/DCS-gRPC folder and also available in the Github repo

Server Development

The following section is only applicable to people who want to developer the DCS-gRPC server itself.

Build Dependencies

  • Rust >=1.64
  • rustfmt (rustup component add rustfmt)

Build

make build

You may need to use the following in powershell

cargo build

Or if you want to use the hot reloading DLL (this is the same as make build):

cargo build --features hot-reload
copy target/debug/dcs_grpc.dll target/debug/dcs_grpc_hot_reload.dll

Prepare DCS

For development:

  • update your MissionScripting.lua to load grpc-mission.lua from your local clone, e.g.:

    - dofile(lfs.writedir()..[[Scripts\DCS-gRPC\grpc-mission.lua]])
    + dofile([[C:\Development\DCS-gRPC\rust-server\lua\DCS-gRPC\grpc-mission.lua]])
  • add custom dllPath and luaPath settings to your Saved Games\DCS\Config\dcs-grpc.lua:

    dllPath = [[C:\Development\DCS-gRPC\rust-server\target\debug\]]
    luaPath = [[C:\Development\DCS-gRPC\rust-server\lua\DCS-gRPC\]]
  • copy the hook script from lua\Hooks\DCS-gRPC.lua to Scripts\Hooks\DCS-gRPC.lua

  • set integrityCheckDisabled = true in your Saved Games\DCS\Config\dcs-grpc.lua to allow changes to Lua without having to re-compile the dll

Debugging

  • Search for [GRPC] in the DCS logs
  • Consult the gRPC Server logs at Saved Games\DCS.openbeta\Logs\gRPC.log

Test the running server via grpcurl: (Remove the .exe when running on Linux)

grpcurl.exe -plaintext -import-path ./protos -proto ./protos/dcs/dcs.proto -d '{\"text\": \"Works!\", \"display_time\": 10, \"clear_view\": false}' 127.0.0.1:50051 dcs.trigger.v0.TriggerService/OutText

or watch the mission event stream via:

grpcurl.exe -plaintext -import-path ./protos -proto ./protos/dcs/dcs.proto -d '{}' 127.0.0.1:50051 dcs.mission.v0.MissionService/StreamEvents

REPL

DCS-gRPC provides the facility to directly run lua code inside the mission scripting environment. This feature is mainly intended for development and is disabled by default. You can enable it via the GRPC settings (See Settings section above)

To build and run the repl run the following commands

cargo build -p dcs-grpc-repl
# Make sure your DCS mission is running
cargo run -p dcs-grpc-repl

Note that the REPL is hardcoded to connect to localhost on the default port

Once connected you can enter lua code to execute. Prefix the lua with return to have the lua code return a value to the client. For example:

return Group.getByName('Aerial-1')
= {
    "id_": 1
}

return Group.getByName('Aerial-1'):getName()
= Aerial-1

The REPL is also available in the release and can be run by running Tools/DCS-gRPC/repl.exe

Contributions

This repository is powered by GitHub Actions for the Continuous Integration (CI) services. The same CI checks would be triggered and executed as you push code to your forked repository, and providing early feedback before a maintainer executes a manual execution on the pull request.

Troublshooting

Linker Error 1104

If you see LINK : fatal error LNK1104: cannot open file when running cargo build make sure that there is no running DCS mission as that locks the DLL files. Exit the mission (You do not have to exit DCS) then re-run the command before restarting the mission.

rust-server's People

Contributors

applevangelist avatar b1naryth1ef avatar dependabot[bot] avatar justin-lovell avatar martinco avatar mobot-gh avatar morpheusxaut avatar penecruz avatar rkusa avatar rurounijones avatar soerenmeier avatar vivanov avatar yolowingpixie avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rust-server's Issues

Possible block on shutdown if a connected client never reads form an opened stream

When the gRPC server is shutdown, it closes all opened streams (events, units, ...) as follows:

  1. The stream waits for
    a) an item (event / unit update) to forward to the client
    b) a shutdown signal
  2. Whatever it receives first, is handled first
  3. If it receives an item, it is send to the client

It is possible that (1) is not run again until the client read the previous item from the stream from (3). If this theory holds true, it would be possible to block the server shutdown by establishing a connection but never reading items from it.

Move DCT functions into separate namespace

Move the DCT functions out of the custom namespace and into a dct namespace. All future Custom, framework specific, work will be done in an isolated namespace.

As part of this we will rename the lua methods with a prefix as suggested by @justin-lovell to help with isolation and reduce chances of conflict. (i.e. dct.joinMission). We may eventually rename all existing core methods as well but that is out of scope for this.

Errors when getting group name of a unit, leading to no stream kill events

The following is in our log:

2023-01-02 19:34:05.364 INFO Scripting (Main): event:type=kill,target_unit_type=BRDM-2,target_coalition=1,weapon=AGM-65L,t=5703.05,initiator_ws_type1=1,targetMissionID=1002613,initiator_unit_type=AV8BNA,target_ws_type1=2,initiator_object_id=16812805,initiator_coalition=2,target=BRDM-2,initiatorPilotName=TDCS | Pilot Pyles,initiatorMissionID=2166,target_object_id=17092097,
2023-01-02 19:34:05.364 ERROR SCRIPTING (Main): [GRPC] Error in event handler: [string "C:\Users\TacticalDCS\Saved Games\instance2\Scripts\DCS-gRPC\exporters\object.lua"]:45: attempt to index a nil value
stack traceback:
[string "C:\Users\TacticalDCS\Saved Games\instance2\Scripts\DCS-gRPC\exporters\object.lua"]:45: in function <[string "C:\Users\TacticalDCS\Saved Games\instance2\Scripts\DCS-gRPC\exporters\object.lua"]:27>
(tail call): ?
[string "C:\Users\TacticalDCS\Saved Games\instance2\Scripts\DCS-gRPC\methods\mission.lua"]:43: in function 'typed_exporter'
[string "C:\Users\TacticalDCS\Saved Games\instance2\Scripts\DCS-gRPC\methods\mission.lua"]:341: in function <[string "C:\Users\TacticalDCS\Saved Games\instance2\Scripts\DCS-gRPC\methods\mission.lua"]:63>
(tail call): ?
[C]: in function 'xpcall'
[string "C:\Users\TacticalDCS\Saved Games\instance2\Scripts\DCS-gRPC\grpc.lua"]:226: in function 'onEvent'
[string "Scripts/World/EventHandlers.lua"]:13: in function <[string "Scripts/World/EventHandlers.lua"]:11>

This links to this line in object.lua:

groupName = Unit.getGroup(unit):getName(),

DCS-gRPC server never starts listening

Hi ya'll, using the latest version it looks like the server never starts. The only mention in the log is:

2022-10-03 09:56:22.708 INFO [GRPC-Hook]: Initializing ...
2022-10-03 09:56:22.708 INFO [GRPC-Hook]: Checking optional config at Config/dcs-grpc.lua ...
2022-10-03 09:56:22.708 INFO [GRPC-Hook]: Config/dcs-grpc.lua successfully read
2022-10-03 09:56:22.710 INFO [GRPC-Hook]: Initialized ...

There is no gRPC.log being generated. Our dcs_grpc.lua is as follows:

autostart = true
-- Whether the Eval method is enabled or not.
evalEnabled = false

-- The host the gRPC listens on (use "0.0.0.0" to listen on all IP addresses of the host).
host = '0.0.0.0'

-- The port to listen on.
port = 50052

-- Whether debug logging is enabled or not.
debug = true

-- Limit of calls per second that are executed inside of the mission scripting environment.
throughputLimit = 600

New "ME draw panel" shapes, line types and categories missing

Over the last months, ED added additional shapes and line types to the ME draw panel, including additional parameters (depending on the shape), like:

  • thickness
  • angle

New shape:

  • oval

New category:

  • text box
  • icon

Line type examples:
image

Oval, text box, icon example:
image

In multiplayer `WorldService.GetMarkPanels` errors in lua when Game Master role is in use

steps to reproduce:

  • start a blank miz file with dcs-grpc, include at least one game master slot
  • connect to the multiplayer, join the game master slot
  • place a marker using the game master role
  • make an rpc call for WorldService.GetMarkPanels
  • expect lua error below

error from the logs:

stack traceback:
	[string "<redacted>\Saved Games\DCS\Scripts\DCS-gRPC\exporters\object.lua"]:28: in function 'unit'
	[string "<redacted>\Saved Games\DCS\Scripts\DCS-gRPC\exporters\object.lua"]:122: in function 'markPanel'
	[string "<redacted>\Saved Games\DCS\Scripts\DCS-gRPC\methods\world.lua"]:36: in function <[string "<redacted>\Saved Games\DCS\Scripts\DCS-gRPC\methods\world.lua"]:31>
	(tail call): ?
	[C]: in function 'xpcall'
	[string "<redacted>\Saved Games\DCS\Scripts\DCS-gRPC\grpc.lua"]:167: in function <[string "<redacted>\Saved Games\DCS\Scripts\DCS-gRPC\grpc.lua"]:163>
	[C]: in function 'next'
	[string "<redacted>\Saved Games\DCS\Scripts\DCS-gRPC\grpc.lua"]:200: in function <[string "<redacted>\Saved Games\DCS\Scripts\DCS-gRPC\grpc.lua"]:198>
	[C]: in function 'pcall'
	[string "<redacted>\Saved Games\DCS\Scripts\DCS-gRPC\grpc.lua"]:211: in function <[string "<redacted>\Saved Games\DCS\Scripts\DCS-gRPC\grpc.lua"]:209>
2022-06-04 22:42:46.069 ERROR   dcs_grpc: Error retrieving next command: attempt to concatenate a userdata value```

Errors returned to lua

When deserialization fails in a next call this error gets returned from rust:

rust-server/src/lib.rs

Lines 151 to 158 in 4a04655

next.success(lua, &res).map_err(|err| {
mlua::Error::ExternalError(Arc::new(Error::DeserializeResult {
err,
method,
result: pretty_print_value(res, 0)
.unwrap_or_else(|err| format!("failed to pretty print result: {}", err)),
}))
})?;

But in lua only the following line get's outputed: Error retrieving next command: attempt to concatenate a userdata value. This is very misleading.
I tried to debug this but I haven't found anything. Something might be wrong with the lua version of dcs.
Did this works once and is this a known issue?

mlua:
function that passes the error to lua: callback_error
function that converts the mlua error into a string: init_error_registry

Add a TTS API to indicate channel in use

Follow-up from the main TTS Work.

Add an API that, when called, returns a boolean that indicates if DCS-gRPC is currently transmitting on a certain frequency. This would be callable over network and from MSE in lua.

Optionally, add an API that, when called, blocks if DCS-gRPC is currently transmitting on a certain frequency and doesn't return until the transmission has finished. This would be network only.

These two APIs can be ersatz mutexes to help stop clients stepping over each other on the radios.

Eval failure

Noting here for reference.

Running the following via the gRPC hook eval API terrain.getRadio()[1] returns a gRPC error response with the following in the dcs.log.

2022-04-20 11:45:00.730 ERROR   [GRPC-Hook]: Error retrieving next command: callback error
stack traceback:
	[C]: in function 'next'
	[string "C:\Users\jeff\source\repos\DCS-gRPC\rust-server\lua\DCS-gRPC\grpc.lua"]:244: in function <[string "C:\Users\jeff\source\repos\DCS-gRPC\rust-server\lua\DCS-gRPC\grpc.lua"]:242>
	[C]: in function 'pcall'
	[string "C:\Users\jeff\source\repos\DCS-gRPC\rust-server\lua\DCS-gRPC\grpc.lua"]:259: in function 'onSimulationFrame'
	[string "C:\Users\jeff\source\repos\DCS-gRPC\rust-server\lua\DCS-gRPC\grpc-hook.lua"]:34: in function <[string "C:\Users\jeff\source\repos\DCS-gRPC\rust-server\lua\DCS-gRPC\grpc-hook.lua"]:32>
	[C]: in function 'pcall'
	[string "MissionEditor/loadUserScripts.lua"]:131: in function <[string "MissionEditor/loadUserScripts.lua"]:126>
	(tail call): in function <(tail call):-1>
caused by: Failed to deserialize result for method hookEval: deserialize error: invalid type: integer `2`, expected a string key
{
  "role" = {
    1 = "ground",
    2 = "tower",
    3 = "approach",
  },
  "callsign" = {
    1 = {
      "common" = {
        1 = "OMAA",
        2 = "OMAA",
      },
    },
  },
  "sceneObjects" = {
    1 = "t:60392560",
  },
  "radioId" = "airfield22_0",
  "frequency" = {
    2 = {
      1 = 0,
      2 = 119200000,
    },
  },
}

What do to about versioning

This issue is all about versioning and how we want to handle it.

Background

gRPC is designed with the philosophy that you maintain backwards compatibility as much as possible and that, when you cannot, you provide a new API while maintaining the old API (at least until you are sure everyone has migrated).

Therefore we need to think about how we will handle this in DCS-gRPC

Options

Embrace versioning throughout the stack

To this end the recommended (and kind of required by Java and Go clients apparently) namespace format is. package.version so, for example. Our atmosphere package should actually be atmosphere.v1 and if we ever add a breaking change to any of the APIs then we add an atmosphere.v2 package and clone the .proto and make our changes in the new proto. (This is also reflected in the folder structure).

However if we completely embraces this concept then we need to do two things.

  1. Update the rust to become version aware (I am not sure if this requires extra rust code or if we just need to add the versioned services for example).
  2. Make all of our Lua code follow this same versioning pattern which would result in something like GRPC["methods"]["atmosphere"]["v1"]["getWind"] = function ... and mirroring the proto folder structure in that respect.

The issue with this is that we would end up loading all of the different versions of lua into the scripting environment which kind of goes against our concept of "more performance" but I am not sure if loaded but unexecuted lua is actually that much of a problem. There might be issues with streaming if there are streams coming from two different versions and we are trying to optimised but that scenario might not happen (Or we can make streams the exception in that we will only support 1 version at a time).

Only update version if the interface changes

We only create a new version if we change the public interface. Changes to the lua implementation will not prompt a version upgrade requirement. This allows us to always have just one implementation of lua instead of multiple versions.

No versioning

We do not do any gRPC / code versioning and rely on semantic versioning to allow clients to decide if they want to upgrade their servers and clients. I did this with the demo apps and, with the help of an IDE admittedly, it wasn't that painful.

Final thoughts

I am not actually against any of the above options. I think the full versioning option is the proper option if we think we will have many different clients (which is actually a hope of mine so that adds weight to this argument).

Otherwise I think we should just go with no versioning. I think the "interface change only" option brings extra complexity without any appropriate benefit.

No matter what options we choose I think we need to do the v1 package naming to make things easier for Go and Java clients anyway and I think we should do it now since we are making big refactoring changes anyway.

LotATC support

DArt, the creator of LotATC is interested in potentially using DCS-gRPC as an alternative back-end to get data out of, and commands into, DCS.

He did a bit of searching in his codebase and determined he uses the following APIs. If we want to enable DCS-gRPC as a back-end for LotATC then we would need to provide these APIs or an equivalent of. Some of them we already provide or have alternatives to; and some of them I have never heard of.

  • DCS.getATCradiosData - TODO
  • DCS.getAvailableCoalitions - TODO (?). Always seems to return red & blue.
  • DCS.getAvailableSlots - TODO
  • DCS.getCurrentMission - Returns a huge table, most of the info is available via other APIs. Need to know what is needed
  • DCS.getMissionDescription
  • DCS.getMissionFilename
  • DCS.getMissionName
  • DCS.getMissionOptions - Not sure what used for
  • DCS.getModelTime - Handled by Timer.GetTime DCS-gRPC API
  • DCS.getPause
  • DCS.getRealTime - No idea what this returns
  • DCS.isServer
  • DCS.reloadOptions - Not sure if applicable
  • DCS.setPause
  • DCS.stopMission
  • DCS.getUnitProperty -- Should be covered by the Unit Stream
  • DCS.getUnitType -- Should be covered by the Unit Stream
  • Export.GetDevice -- Not sure what used for and not accessible from DCS-gRPC accessible environments
  • Export.LoGeoCoordinatesToLoCoordinates - Not sure needed with unit stream.
  • Export.LoGetModelTime - Not sure needed with unit stream.
  • Export.LoGetObjectById - Not sure needed with unit stream.
  • Export.LoGetSelfData - Not sure needed with unit stream.
  • Export.LoGetWorldObjects - Not sure needed with unit stream.
  • Export.LoLoCoordinatesToGeoCoordinates - Not sure needed with unit stream.
  • Export.LoLoCoordinatesroGeoCoordinates - Not sure needed with unit stream.
  • net.get_my_player_id - Always returns 0
  • net.get_player_info - Returned as a detailed response with compounded get_player_list
  • net.get_player_list - Returned as a detailed response with compounded get_player_list
  • net.get_server_id - Always returns 0
  • net.missionlist_get - TODO
  • net.missionlist_run - TODO
  • net.recv_chat - Using the PlayerSendChatEvent message in the event stream
  • net.send_chat
  • net.send_chat_to

The following are temporarily assessed as probably not needed if using DCS-gRPC:

  • net.dostring_in
  • DCS.setUserCallbacks
  • net.log
  • net.lua2json

GetSessionId API

Since DCS-gRPC clients have a lifecycle outside of the DCS server we need a way for them to know if a mission has restarted or a new one loaded. At the moment the only thing a client will know is that it got disconnected and then reconnected.

After bouncing some ideas around with @rkusa I think what we want is:

  1. Implement a Mission.GetSessionId (Namespace up for debate) DCS-gRPC method.
  2. When called it checks for the existance of a DCS userFlag dcs_grpc_session_id
  3. If no value exists then set the value to the current unixtime (since flags can only contain bool or numeric) using something like os.time(os.date("!*t")) and return the value.
  4. If a value exists then return the existing value.

This will generate a new session unique ID each time a mission is loaded and return the same value until another one is loaded. Clients can then use this ID to determine if a session has changed after getting disconnected and perform appropriate processing.

Documenting this here in case anyone wants to give it a go, otherwise I will get around to it at some point. This would be a good, simple, API to implement for a newcomer.

Connection refused after a couple of hours

Hello!

I am having the following problem after the server was running for a couple of hours:

Copied the log out of the Air defense logging, but my own application gets the same error.
Could it be that I did not properly close the connection i.e when forcefully shutting down my debugging session in Visual Studio?
I have a session pool that only allows one active session at a time so it cannot be that too many sessions are created.

I will have a look if there is a difference between me shutting it down using the ShutdownAsync function and forcefully closing and will list the results here.

Many thanks in advance!

IP replaced with ***

Es konnte keine Verbindung hergestellt werden, da der Zielcomputer die Verbindung verweigerte. = Connection refused by the remote host.

[WRN] gRPC Exception
Grpc.Core.RpcException: Status(StatusCode="Unavailable", Detail="Error starting gRPC call. HttpRequestException: Es konnte keine Verbindung hergestellt werden, da der Zielcomputer die Verbindung verweigerte. () SocketException: Es konnte keine Verbindung hergestellt werden, da der Zielcomputer die Verbindung verweigerte.", DebugException="System.Net.Http.HttpRequestException: Es konnte keine Verbindung hergestellt werden, da der Zielcomputer die Verbindung verweigerte. ()
---> System.Net.Sockets.SocketException (10061): Es konnte keine Verbindung hergestellt werden, da der Zielcomputer die Verbindung verweigerte.

Don't return errors from Rust to Lua

It looks like errors returned from Rust to Lua might lead to DCS crashing once a mission is stopped. So either figure out what is wrong with those errors, or don't return errors at all and simply log them with log::error! instead.

Return parent objects

Return parent objects intead of references.

For example, when returning a unit we don't return a group_name or group_id field. We return the parent group object. We also return the player object (If one exists).

This will flesh out the information and allow for more advanced processing by event consumers and reduce the chance of race-conditions.

Note that we will only ever return the parent object. We wil not return children of the parent object since that will result in, at worst, an infinite loop (e.g. we will not return a unit with a group with a list of units, each with a group etc.).

Race condition during initialization

It appears that in some situations (especially on very fast hardware) DCS races the DCS-gRPC initialization and causes an unrecoverable scripting error trying to call world.addEventHandler.

An example of the log output for this situation:

2022-01-09 06:01:17.040 ERROR   SCRIPTING: [GRPC] Checking optional config at `Config/dcs-grpc.lua` ...
2022-01-09 06:01:17.040 INFO    SCRIPTING: [GRPC] Optional config at `Config/dcs-grpc.lua` not found (can't open "C:\Users\andrei\Saved Games\DCS.openbeta_server\Config\dcs-grpc.lua" for 'r')
2022-01-09 06:01:17.040 INFO    SCRIPTING: [GRPC] Writing C:\Users\andrei\Saved Games\DCS.openbeta_server\Data\dcs-grpc.lua
2022-01-09 06:01:17.041 INFO    SCRIPTING: [GRPC] auto starting
2022-01-09 06:01:17.047 INFO    SCRIPTING: [GRPC] mission loading ...
2022-01-09 06:01:17.064 INFO    SCRIPTING: [GRPC] Limit request execution at max. 18 calls every 0.03s (โ‰™ throughput of 600)
2022-01-09 06:01:17.064 ERROR   Lua::Config: load error Scripts/MissionScripting.lua:[string "C:\Users\andrei\Documents\GitHub\rust-server\lua\DCS-gRPC\grpc.lua"]:239: attempt to call field 'addEventHandler' (a nil value).

A simple workaround is to delay calling this function for a little bit (1 second worked for me, but we could experiment further).

  timer.scheduleFunction(function ()
    GRPC.logInfo("Adding world event handler")
    world.addEventHandler(eventHandler)
  end, {}, timer.getTime() + 1)

I've only encountered this on my desktop PC (i9-11900k), other instances of DCS running in VMs / on slower hardware do not encounter this problem.

Get Map/Mission name

  • A lot of client logic is tied up in what map is currently loaded.
  • More advanced usage may also have specific logic running depending on the mission name (e.g. if someone writes a client that basically runs a mission).

Therefore we should make it easy to retrieve the map and mission name via DCS-gRPC.

Log performance information

Log performance information to allow for server owners to have the information needed to see how DCS-gRPC is impacting their server.

Every minute write to the dcs.log file with the following information calculated from metrics of the last minute.

  • Total numer of calls into the MSE
  • Highest TPS count of calls into the MSE
  • Average TPS count of calls into the MSE
  • Average time spent waiting for MSE calls to complete
  • Total time spent waiting for MSE calls to complete
  • Average pending gRPC requests
  • Highest pending gRPC requests

Allow specifying IP address / Port number in lua

Allow users to (optionally) specify the IP and port to bind to. This will allow users to run multiple instances on one server long with the DCS instance.

The changes should enabled something like the following (Using the README.md init example)

package.cpath = package.cpath..[[M:\Development\DCS-gRPC\rust-server\target\debug\?.dll;]]
GRPC = { basePath = [[M:\Development\DCS-gRPC\rust-server\lua\]] }
+GRPC.host = '192.168.1.10' -- '*' for everything, '127.0.0.1' default
+GRPC.port = 54763 -- 50051 default.
dofile(GRPC.basePath .. [[grpc.lua]])

Ballistics objects tracking.

Background, some servers are suffering issues where ballistics objects appear to not be cleaned up by DCS. Being able to track this count of objects would be very handy.

Add the ability to send ballistics object information to clients.

This would have to be done using LoGetWorldObjects("ballistic") in the export environment and, given the number of objects that can exist at a time and the potential performance impact I am not sure if we would want to export them as part of the getUnitStream or some synchronous GetBallistics API that is regularly called by the client?

This ticket is mainly for record-keeping and pondering.

inject gRPC into all missions

Here is a hook that can be used to inject the gRPC server into all missions of the DCS server:

Saved Games\DCS.openbeta\Scripts\Hooks\gRPC.lua:

local function inject()
  log.write("[gRPC]", log.INFO, "Injecting gRPC into mission")

  local result = net.dostring_in("server", [[
    if GRPC == nil then
      package.cpath = package.cpath.."M:\\Development\\DCS-gRPC\\rust-server\\target\\debug\\?.dll;"
      GRPC = { basePath = "M:\\Development\\DCS-gRPC\\rust-server\\lua\\" }
      dofile(GRPC.basePath .. "grpc.lua")
    end
  ]])
end

local function init()
  local handler = {}

  function handler.onSimulationResume()
    local ok, err = pcall(inject)
    if not ok then
      log.write("[gRPC]", log.ERROR, "Failed to inject: "..tostring(err))
    end
  end

  DCS.setUserCallbacks(handler)
end

local ok, err = pcall(init)
if not ok then
  log.write("[gRPC]", log.ERROR, "Failed to init: "..tostring(err))
end

Adding it as an issue here to keep track of it. We can think of somehow adding it to the repo in the future.

Weapon & Ballistics stream

Some users have asked us to include a way to stream weapons and ballistics.

Opening this issue for discussion. Two options I see.

  1. Add weapons to the unit stream.
  2. Have a separate weapon stream.

Advantage of having separate streams is no unecessary extra work that might not be used by clients (e.g. OverlordBot does not care about weapons).

Speed and heading not updated in unit stream

The speed and heading of units is not being updated in the units stream.

  • Observed by watching LotATC and seeing a plane reverse course while the output stayed the same.
  • Observed by comparing DCS-gRPC output to Tacview output and observing only the tacview headings change.

Proving it will require a huge dump of stats so to illustate the point I am going to put just a few entries demonstrating it.

(Altitude doesn't change in these three samples but I did observer changes in other units so altitude is fine, this is an AI unit in a racetrack which is why it is so steady)

{
  "unit": {
    "id": 7013,
    "name": "RUSSIA air 13 unit1",
    "callsign": "106",
    "coalition": "COALITION_RED",
    "type": "C-101CC",
    "position": {
      "lat": 43.89192219804639,
      "lon": 40.93609502901023,
      "alt": 5486.39990234375
    },
    "groupName": "RUSSIA air 13",
    "numberInGroup": 1,
    "speed": 119.97181015273709,
    "heading": 126.50408556324253
  }
},
{
  "unit": {
    "id": 7013,
    "name": "RUSSIA air 13 unit1",
    "callsign": "106",
    "coalition": "COALITION_RED",
    "type": "C-101CC",
    "position": {
      "lat": 43.88827413932173,
      "lon": 40.94169167458405,
      "alt": 5486.39990234375
    },
    "groupName": "RUSSIA air 13",
    "numberInGroup": 1,
    "speed": 119.97181015273709,
    "heading": 126.50408556324253
  }
},
{
  "unit": {
    "id": 7013,
    "name": "RUSSIA air 13 unit1",
    "callsign": "106",
    "coalition": "COALITION_RED",
    "type": "C-101CC",
    "position": {
      "lat": 43.8702459604217,
      "lon": 40.969337211586804,
      "alt": 5486.39990234375
    },
    "groupName": "RUSSIA air 13",
    "numberInGroup": 1,
    "speed": 119.97181015273709,
    "heading": 126.50408556324253
  }
}

Group Spawning

This issue is the master issue for getting group spawning working and will probably have more things added to it over time as we realise what needs to be done:

The API for spawning addGroup in lua is basically "Pass in a huge lua table and hope you get it right" which is not the most user-friendly experience. Therefore we should try and add validation to our gRPC API (still in lua) where possible to remove some of the uncertainty around calling it.

Ground Groups

  • Group
  • Units
  • Waypoints
  • Tasks
  • Validation

Helicopter Groups

  • Group
  • Units
  • Waypoints
  • Tasks
  • Fuel/Guns/countermeasures etc.
  • Pylon setup
  • Validation

Plane Groups

  • Group
  • Units
  • Waypoints
  • Tasks
  • Fuel/Guns/countermeasures etc.
  • Pylon setup
  • Validation

Sea Groups

  • Group
  • Units
  • Waypoints
  • Tasks
  • Validation

Stream basic unit information (name, type, position)

EDIT

I think the easiest thing to do so far is to just use the MIST code where the lua will literally just be reading the mist.dbs.aliveUnits table, maybe converting them using the existing exporters and returning the table with the exported versions.

The question is how to have the rust code call this method on a timer and stream the objects to all clients. Since this is rust area I have very little idea of what needs to be done


Putting this in a Github Issue for discussion and brain storming.

We would like to export unit position information as a gRPC stream to clients. We want to do this in a way that does not block the lua thread in large missions. We want to do it at a use specified refresh rate which would be coarse to the level of something like every 1 second at highest refresh rate but could be as slow as every 10 seconds.

Looking for ideas since some of this will require more rust coding than just acting as an RPC interface I think (The perform in chunks option especially).

The basic process is:

  1. Client calls the RPC with desired refresh rate
  2. Call coalition.getGroups for coalition 1.
  3. For each group, call getUnits and add to a lua table
  4. Return the lua table to the rust code that streams each entry after serializing it
  5. IF coalition == 1 then GOTO 2 for coalition 2
  6. Sleep until refresh time and GOTO 2

This will work but it will get all units for each coalition in one go which may end up being many units and that could block the lua thread.

Possible ways around this....

Perform in chunks

  1. Client calls the RPC with desired refresh rate
  2. Call coalition.getGroups for coalitions 0, 1, 2 and add to a united groups table which is returned to rust
  3. For each group call Group.getUnits and return a lua table that contains the units to the rust code that iterates and serializes and streams each unit.
  4. Sleep TIME_REMAINING_UNTIL_REFRESH / GROUPS_REMAINING in rust.
  5. IF GROUPS_REMAINING > 1 GOTO 2 ELSE SLEEP TIME_REMAINING_UNTIL_REFRESH THEN GOTO 2

Use lua coroutines

This is the same as the basic process but we use lua coroutines and add some yields in the code when it iterates groups (or even units). I have no idea if coroutines will work as expected when the method is being called from rust or even if they will work properly.

We could look at how MIST does it ( https://github.com/dcs-liberation/dcs_liberation/blob/b8e6c2fe78ca3a3d49d302239403777e46bd12ad/resources/plugins/base/mist_4_4_90.lua ) or even just use MIST for the implementation by default.

Other things to think about

  • We could split by category as well and only do aircraft every X time while ground units would be every 5*X since they do not move as fast. This is what Tacview does (Just at a much higher rate for both).
  • We could ignore weapon objects entirely unless the client specifies weapons are in scope.
  • What if multiple clients call this API. We want to fanout the data to all of them, maybe that means no per-client configurable refresh rate.

New Events

Comparing recent DCS version, I've noticed the following new events that we could add:

  • S_EVENT_BDA = 37
  • S_EVENT_AI_ABORT_MISSION = 38
  • S_EVENT_DAYNIGHT = 39
  • S_EVENT_FLIGHT_TIME = 40
  • S_EVENT_PLAYER_SELF_KILL_PILOT = 41
  • S_EVENT_PLAYER_CAPTURE_AIRFIELD = 42
  • S_EVENT_EMERGENCY_LANDING = 43

    when AI aircraft lands on belly or ditch on water. Support for player will be added later

  • S_EVENT_UNIT_CREATE_TASK = 44
  • S_EVENT_UNIT_DELETE_TASK = 45

Pre-built proto files

Using dcs-grpc-stubs as a dependency in other Rust-based projects is a bit inconvenient as it requires this workaround to get it to build using its bundled protoc.

An alternative might be to pre-built the porto files instead of having them as part of the normal build process. Here is an example of another project doing something like this: open-telemetry/opentelemetry-rust#881

Add server FPS to the event stream

Since DCS-gRPC is hooked into the hook environment and gets called on each simulation frame; we can calculate the FPS of the server and emit that value as an event every second. This will allow clients to be able to see the server FPS which is useful for diagnostic purposes.

One solid use-case is to use Telemachus to record FPS metrics for display and alarming.

Note: Need to verify that a paused server still calls OnSimulationFrame or not. If it still calls the hook when the server is paused then we could use this as a way to see if the server has frozen or not.

Split API services into separate grpc packages

Split the API services into separate grpc pacakges so that we can rationalise the APIs and remove conflicts due to duplication, it also encourages proper separation of concerns.

  • Atmosphere
  • Coalition
  • Common (Do last)
  • Controller
  • Custom
  • Group
  • Hook
  • Mission
  • Timer
  • Trigger
  • Unit
  • World

Out of scope:

  • Splitting the rpc.rs file into package specific rust files. (Done)
  • Reorganising any APIs into new packages
  • Renaming packages.

Allow for tuning of DCS-gRPC TPS limits

Allow for clients to tune the number of times DCS-gRPC can call into the DCS Mission Scripting Environment from the initialization lua. Optionally also set the TPS for which the gRPC server is polled for incoming requests (This must be equal or lower to the MSE TPS limit).

Something along the lines of

package.cpath = package.cpath..[[M:\Development\DCS-gRPC\rust-server\target\debug\?.dll;]]
GRPC = { basePath = [[M:\Development\DCS-gRPC\rust-server\lua\]] }
+GRPC.setMaxMseTps(10)
+GRPC.setMaxGrpcTps(10)
dofile(GRPC.basePath .. [[grpc.lua]])

Specify IP and port bound to

In order to improve debuggability output the ip and port that the DCS-gRPC is binding to in the dcs.log / grpc.log

DCS Server crashes on player connection

The DCS dedicated server will crash with the current DCS-gRPC master branch code on player connection:

Steps to replicate.

  1. Run a mission, with DCS-gRPC enabled, on a DCS dedicated server.
  2. Connect to the running server in DCS.
  3. Server will instantly crash with the below exception.

Note that this does not happen:

  • when running the mission in singleplayer so it must be something to do with a multiplayer client connecting.
  • when no one connects, the dedicated server will happily run, even with complicated AI actions going on, as long as no one connects.

I am not sure when this started happening, there is at least one version of the server running on the Hoggit Syria At War server with no issues so we know of so this commit is working correctly.

If you cannot think of anything obvious then I wil do a git bisect although that will take a bit of time.

Stack trace below:

2021-08-21 02:27:34.651 INFO    EDCORE: signal caught: 'Abnormal termination'
2021-08-21 02:27:34.651 INFO    EDCORE: try to write dump information
2021-08-21 02:27:34.653 INFO    EDCORE: # -------------- 20210821-022734 --------------
2021-08-21 02:27:34.654 INFO    EDCORE: DCS/2.7.5.10869 (x86_64; Windows NT 10.0.19042)
2021-08-21 02:27:34.656 INFO    EDCORE: C:\Windows\System32\KERNELBASE.dll
2021-08-21 02:27:34.657 INFO    EDCORE: # 00000000 STATUS_SUCCESS at 73F44ED9 00:00000000
2021-08-21 02:27:34.663 INFO    EDCORE: SymInit: Symbol-SearchPath: '.;C:\Program Files\Eagle Dynamics\DCS World OpenBeta Server;C:\Program Files\Eagle Dynamics\DCS World OpenBeta Server\bin;C:\Windows;C:\Windows\system32;SRV*C:\websymbols*https://msdl.microsoft.com/download/symbols;', symOptions: 528, UserName: 'CowServer'
2021-08-21 02:27:34.665 INFO    EDCORE: OS-Version: 10.0.19042 () 0x100-0x1
2021-08-21 02:27:35.025 INFO    EDCORE: 0x0000000000034ED9 (KERNELBASE): RaiseException + 0x69
2021-08-21 02:27:35.025 INFO    EDCORE: 0x00000000000BB677 (edCore): ed::core::InitVFS::~InitVFS + 0x6B7
2021-08-21 02:27:35.025 INFO    EDCORE: 0x0000000000071881 (ucrtbase): raise + 0x1E1
2021-08-21 02:27:35.026 INFO    EDCORE: 0x0000000000072851 (ucrtbase): abort + 0x31
2021-08-21 02:27:35.026 INFO    EDCORE: 0x000000000000360D (VCRUNTIME140): _is_exception_typeof + 0x136D
2021-08-21 02:27:35.026 INFO    EDCORE: 0x000000000000BB64 (VCRUNTIME140): __C_specific_handler + 0x334
2021-08-21 02:27:35.026 INFO    EDCORE: 0x0000000000003029 (VCRUNTIME140): _is_exception_typeof + 0xD89
2021-08-21 02:27:35.026 INFO    EDCORE: 0x000000000000C021 (VCRUNTIME140): __CxxFrameHandler3 + 0x71
2021-08-21 02:27:35.027 INFO    EDCORE: 0x00000000000A21FF (ntdll): __chkstk + 0x19F
2021-08-21 02:27:35.027 INFO    EDCORE: 0x0000000000030939 (ntdll): RtlUnwindEx + 0x339
2021-08-21 02:27:35.027 INFO    EDCORE: 0x000000000000C80A (VCRUNTIME140): __report_gsfailure + 0x26A
2021-08-21 02:27:35.027 INFO    EDCORE: 0x0000000000006ED1 (lua): lua_getinfo + 0x1291
2021-08-21 02:27:35.028 INFO    EDCORE: 0x0000000000006CE2 (lua): lua_getinfo + 0x10A2
2021-08-21 02:27:35.028 INFO    EDCORE: 0x0000000000006950 (lua): lua_getinfo + 0xD10
2021-08-21 02:27:35.028 INFO    EDCORE: 0x0000000000017BB5 (lua): luaS_newlstr + 0x4075
2021-08-21 02:27:35.028 INFO    EDCORE: 0x0000000000002AA0 (lua): lua_concat + 0x40
2021-08-21 02:27:35.028 INFO    EDCORE: 0x000000000035A82A (dcs_grpc_server): luaopen_dcs_grpc_server + 0x1FCFBA
2021-08-21 02:27:35.028 INFO    EDCORE: 0x0000000000357DD3 (dcs_grpc_server): luaopen_dcs_grpc_server + 0x1FA563
2021-08-21 02:27:35.029 INFO    EDCORE: 0x0000000000007AC5 (lua): luaD_growstack + 0x845
2021-08-21 02:27:35.029 INFO    EDCORE: 0x0000000000018D94 (lua): luaS_newlstr + 0x5254
2021-08-21 02:27:35.029 INFO    EDCORE: 0x0000000000007E04 (lua): luaD_growstack + 0xB84
2021-08-21 02:27:35.029 INFO    EDCORE: 0x0000000000006F7F (lua): lua_getinfo + 0x133F
2021-08-21 02:27:35.029 INFO    EDCORE: 0x000000000000812E (lua): lua_yield + 0x9E
2021-08-21 02:27:35.030 INFO    EDCORE: 0x000000000001EEAC (lua): luaL_newstate + 0x21FC
2021-08-21 02:27:35.030 INFO    EDCORE: 0x0000000000007AC5 (lua): luaD_growstack + 0x845
2021-08-21 02:27:35.030 INFO    EDCORE: 0x0000000000018D94 (lua): luaS_newlstr + 0x5254
2021-08-21 02:27:35.030 INFO    EDCORE: 0x0000000000007E04 (lua): luaD_growstack + 0xB84
2021-08-21 02:27:35.030 INFO    EDCORE: 0x0000000000006F7F (lua): lua_getinfo + 0x133F
2021-08-21 02:27:35.030 INFO    EDCORE: 0x000000000000812E (lua): lua_yield + 0x9E
2021-08-21 02:27:35.031 INFO    EDCORE: 0x0000000000002576 (lua): lua_pcall + 0x66
2021-08-21 02:27:35.031 INFO    EDCORE: 0x00000000000F2DF9 (edCore): ED_lua_pcall + 0x59
2021-08-21 02:27:35.031 INFO    EDCORE: 0x00000000000EE8F4 (edCore): Lua::Config::call_func + 0xF4
2021-08-21 02:27:35.031 INFO    EDCORE: 0x00000000007D3C37 (DCS): SW + 0x4E3107
2021-08-21 02:27:35.031 INFO    EDCORE: 0x00000000008886C3 (DCS): SW + 0x597B93
2021-08-21 02:27:35.032 INFO    EDCORE: 0x000000000088125D (DCS): SW + 0x59072D
2021-08-21 02:27:35.032 INFO    EDCORE: 0x000000000087E612 (DCS): SW + 0x58DAE2
2021-08-21 02:27:35.032 INFO    EDCORE: 0x00000000000C6A57 (Flight): woATC::updateCoalition + 0x687
2021-08-21 02:27:35.032 INFO    EDCORE: 0x000000000009D2DA (Flight): woATC::Control + 0x51A
2021-08-21 02:27:35.032 INFO    EDCORE: 0x00000000000576CC (Flight): wAirdrome::Control + 0xCC
2021-08-21 02:27:35.032 INFO    EDCORE: 0x000000000005B3E3 (Flight): wAirdrome::Init + 0xF33
2021-08-21 02:27:35.033 INFO    EDCORE: 0x0000000000003AD6 (World): wSimTrace::CommandsTraceDiscreteIsOn + 0x466
2021-08-21 02:27:35.033 INFO    EDCORE: 0x0000000000003F3D (World): wSimCalendar::DoActionsUntil + 0x1FD
2021-08-21 02:27:35.033 INFO    EDCORE: 0x00000000007ECA22 (DCS): SW + 0x4FBEF2
2021-08-21 02:27:35.033 INFO    EDCORE: 0x00000000007EC78E (DCS): SW + 0x4FBC5E
2021-08-21 02:27:35.033 INFO    EDCORE: 0x00000000008022EB (DCS): SW + 0x5117BB
2021-08-21 02:27:35.033 INFO    EDCORE: 0x00000000007D3194 (DCS): SW + 0x4E2664
2021-08-21 02:27:35.034 INFO    EDCORE: 0x00000000007D356D (DCS): SW + 0x4E2A3D
2021-08-21 02:27:35.034 INFO    EDCORE: 0x0000000001C7332F (DCS): AmdPowerXpressRequestHighPerformance + 0xE7D32B
2021-08-21 02:27:35.034 INFO    EDCORE: 0x0000000000A0B23E (DCS): SW + 0x71A70E
2021-08-21 02:27:35.034 INFO    EDCORE: 0x0000000000017034 (KERNEL32): BaseThreadInitThunk + 0x14
2021-08-21 02:27:35.034 INFO    EDCORE: 0x0000000000052651 (ntdll): RtlUserThreadStart + 0x21
2021-08-21 02:27:35.243 INFO    EDCORE: Minidump created.
2021-08-21 02:27:35.243 INFO    Lua::Config: stack traceback:

Unit exporter `heading` is not actually units heading, it's the units 2d velocity vector

https://github.com/DCS-gRPC/rust-server/blob/main/lua/DCS-gRPC/exporters/object.lua#L28-L30

The heading returned for a unit is only correct if the unit is moving exactly forward. This is because getVelocity() is being used to calculate the "heading". The result is actually a 2d "velocity vector", which is the correct velocity vector. If the unit is moving forward it should match or be very close to the units heading.

However, it is not the units heading. A unit moving backwards such as a helicopter will result in "heading" from the unit exporter equal to the direction the unit is moving.

The correct way to get the units heading appears to be with the unit:getPosition() function (hoggit wiki) using the same algo in use.

This is supported by this forum thread: https://forum.dcs.world/topic/148896-converting-a-units-orientation-to-a-quotheadingquot-script-help/

I'm able to confirm this with CustomService.Eval
(I'm working in typescript, you can do the same in any language. Below code is a snippet)

const lua = `return Unit.getByName("${name}"):getPosition()`

custom.eval({ lua }, (error, result) => {
  if (error) {
    console.log('failed to get unit position', error)
    return
  }
  const pos = JSON.parse(result!.json!)

  let heading = deg(Math.atan2(pos.x.z, pos.x.x))
  if (heading < 0) {
    heading = heading + 360
  }

  // here, heading value is always correct, even when moving backwards
})

function deg(radians: number): number {
  return radians * (180 / Math.PI)
}

warning: opinions below:

As a consumer of dcs-grpc, I want the units actual heading, In addition to the units velocity. (speed, velocity vector, etc)

I propose that instead of (or in addition to) extracting the heading and speed, expose the units raw velocity vec3. In addition to the units position vector

There seems to be some attempt to expose the data available in lua outside of lua, but for units it doesn't align with the lua stuff very well. I had to figure out what heading was here. And currently have no access to raw vecs to do my own math

Proposal: Namespace methods according to respective Proto file

In browsing the source code, I may have generated a conflict without realizing it at the time. Here are two functions which both define getPlayers:

GRPC.methods.getPlayers = function(params)
local units = coalition.getPlayers(params.coalition)
local result = {}
for i, unit in ipairs(units) do
result[i] = GRPC.exporters.unit(unit)
end
return GRPC.success({units = result})
end

GRPC.methods.getPlayers = function()
local players = {};
for _,v in pairs(net.get_player_list()) do
local playerInfo = net.get_player_info(v);
table.insert(players, {
id = playerInfo.id,
name = playerInfo.name,
coalition = playerInfo.side + 1, -- common.Coalition enum offset
slot = playerInfo.slot,
ping = playerInfo.ping,
remoteAddress = playerInfo.ipaddr,
ucid = playerInfo.ucid,
locale = playerInfo.lang
})
end
return GRPC.success({players = players})
end

I would like to propose to avoid naming conflicting, let's prefix the method name with some annotation of the proto file as a namespace.

E.g.

- GRPC.methods.getPlayers = function(params)
+ GRPC.methods.net_getPlayers = function(params)

and

- GRPC.methods.getPlayers = function(params)
+ GRPC.methods.coalition_getPlayers = function(params)

An alternative is setup a localized Lua table with the following pseudo:

local coalition = {}
GRPC.methods.coalition = coalition

coalition.getPlayers = function() {
-- ...
}

and the Rust code would perform a function call similar to:

- self.request("getPlayers", request)
+self.request("coalition", "getPlayers", request)

Implement a basic spawn API

Implement a basic spawn api that will let a client spawn a group with at least one unit.

Required

  • Able to spawn a new ground group with at least one unit in it.

Optional

  • Able to spawn ship, helicopter, plane groups
  • Able to spawn multiple units
  • Able to set group waypoints, tasks and all the other stuff that is possible via the mission editor.

Notes

The spawn API can range from simple to very complex so I am happy to break this down into multiple stages across multiple merge requests, potentially across multiple milestones.

Some API cleanup

There are two areas where we can possibly clean up APIs we are exporting.

  1. There is inconsistency in the the use of weaponName string fields and weapon tables in the lua events. We could get rid of the weaponName field and move everything into the weapon table for events that have both (hit/kill events being good examples).

  2. For the APIs that take mission filenames (LoadMission, GetMissionFileName) and which take file paths (For some reason) we could do things such as splitting the paths and filenames into two separate fields.

Better performing Tacview exporter?

I know there are certain DCS servers like Enigmas that don't use LotATC or Tacview server side for performance reasons, and I know that DCS-gRPC can help server performance by offloading expensive calculations. My initial thought is DCS-gRPC might not help improve server performance when used as the basis for unit position streaming/recording applications.

Presumably though, there are some expectations of improved performance with #131, so are there similar expectations if DCS-gRPC were used to create an alternate Tacview exporter?

Add SRS client connected/disconnected/freq-changed events

Since we have an SRS integration now, we could establish an SRS Connection that listens for SRS events and broadcasts them on the gRPC event stream.

Possible events might be:

  • Client connected
  • Client disconnnected
  • Client frequency changed

I am not sure if the mapping from an SRS client to a unit works well enough. I know that we are getting the unit id, but finding a unit just by its id is a mess (there is no getById).

Use DCS' built-in magnetic declination API

Before 2.8, DCS did not have an API to retrieve the magnetic variation at a coordinate. Since 2.8 (might have also been available before, just unknown), there is finally one.
Our current implementation calculates the magvar outside of DCS based on the International Geomagnetic Reference Field (IGRF). The result is very close to what DCS calculates, but can now be replaced by the new API provided with DCS 2.8:

local magvar = require('magvar')
magvar.get_mag_decl(lat, lon)

For some more details around the API, see:
https://discord.com/channels/696721363276267590/911042786122399824/1041889659174793256

Necessary changes:

  • Move the API out of the custom service into another more fitting one (hook maybe? ๐Ÿคทโ€โ™‚๏ธ)
  • Replace the implementation with a call into Lua that executes the snippet mentioned above
  • Remove the igrf dependency

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.