1. Can someone help me determine the Steam build id version of a running Rust dedicated server from C#? I suppose this can be done by checking the local `steamapps\appmanifest_xxxxxx` file, but I like to see if that number can be determined through some kind of code without file-level parsing.

    I know the AppId and BuildId exist somewhere when the server is running because I can find them through WinDbg. Using JustDecompile on Rust.Global.dll shows BuildId declared in Rust.Global.SteamClient as a Client type.
    Code:
    Rust.Global.dllnamespace Rust
    {
        public static class Global
        {
            ...        public static Client SteamClient
            {
                get;
                set;
            }
        }
    }
    Code:
    Facepunch.Steamworks.dllnamespace Facepunch.Steamworks
    {
        public class Client : BaseSteamworks, IDisposable
        {
            ...
       
            public int BuildId
            {
                get;
                private set;
            }
        }
    }
    There appears to be a SingletonComponent defined in Assembly-CSharp.dll for SteamClient
    Code:
    Assembly-CSharp.dllpublic class SteamClient : SingletonComponent<SteamClient>
    {
        public SteamClient()
        {
        }
    }
    But I can't figure out how to get the value of SteamClient.BuildId . I'm working with an OxideMod extension so I'm outside of the sandboxing. I've used non-OxideMod objects and methods from other core areas of Rust dlls elsewhere, so I'm sure this must be possible. When I try code like below, I get an error "object reference not set to an instance of an object"
    Code:
    int x1 = (int) SteamClient.Instance.GetInstanceField("BuildId");
    I've got to be close, because I can do this and it compiles/runs fine (of course without being able to get BuildId)
    Code:
    SteamClient b1 = SteamClient.Instance;
    TIA
     
    Last edited by a moderator: Oct 2, 2017
  2. The Steamworks library is actually used for both the game clients and the server which is why you have the code available for Facepunch.Steamworks.Client and Facepunch.Steamworks.Server. However these two are never initialized together. On a server an instance will be created of Facepunch.Steamworks.Server while the clients will have an instance of Facepunch.Steamworks.Client which is why you are running into a NullReferenceException.

    Some debug output:
    Code:
    SteamClient: null
    SteamServer: Facepunch.Steamworks.Server
     <Query>k__BackingField: Facepunch.Steamworks.ServerQuery
             server: Facepunch.Steamworks.Server
                     <Query>k__BackingField: Facepunch.Steamworks.ServerQuery
                     <Stats>k__BackingField: Facepunch.Steamworks.ServerStats
                     <Auth>k__BackingField: Facepunch.Steamworks.ServerAuth
                     _dedicatedServer: True
                     _maxplayers: 10
                     _botcount: 0
                     _mapname: Procedural Map
                     _modDir: rust
                     _product: 252490
                     _gameDescription: Rust
                     _serverName: Mughisi's Playground
                     _passworded: False
                     _gametags: mp10,cp0,qp0,v2017,h4bcc25f1,stok,born1505842473
                     KeyValue: System.Collections.Generic.Dictionary`2[System.String,System.String]
                     native: Facepunch.Steamworks.Interop.NativeInterface
     <Stats>k__BackingField: Facepunch.Steamworks.ServerStats
             server: Facepunch.Steamworks.Server
                     <Query>k__BackingField: Facepunch.Steamworks.ServerQuery
                     <Stats>k__BackingField: Facepunch.Steamworks.ServerStats
                     <Auth>k__BackingField: Facepunch.Steamworks.ServerAuth
                     _dedicatedServer: True
                     _maxplayers: 10
                     _botcount: 0
                     _mapname: Procedural Map
                     _modDir: rust
                     _product: 252490
                     _gameDescription: Rust
                     _serverName: Mughisi's Playground
                     _passworded: False
                     _gametags: mp10,cp0,qp0,v2017,h4bcc25f1,stok,born1505842473
                     KeyValue: System.Collections.Generic.Dictionary`2[System.String,System.String]
                     native: Facepunch.Steamworks.Interop.NativeInterface
     <Auth>k__BackingField: Facepunch.Steamworks.ServerAuth
             server: Facepunch.Steamworks.Server
                     <Query>k__BackingField: Facepunch.Steamworks.ServerQuery
                     <Stats>k__BackingField: Facepunch.Steamworks.ServerStats
                     <Auth>k__BackingField: Facepunch.Steamworks.ServerAuth
                     _dedicatedServer: True
                     _maxplayers: 10
                     _botcount: 0
                     _mapname: Procedural Map
                     _modDir: rust
                     _product: 252490
                     _gameDescription: Rust
                     _serverName: Mughisi's Playground
                     _passworded: False
                     _gametags: mp10,cp0,qp0,v2017,h4bcc25f1,stok,born1505842473
                     KeyValue: System.Collections.Generic.Dictionary`2[System.String,System.String]
                     native: Facepunch.Steamworks.Interop.NativeInterface
             OnAuthChange: System.Action`3[System.UInt64,System.UInt64,Facepunch.Steamworks.ServerAuth+Status]
     _dedicatedServer: True
     _maxplayers: 10
     _botcount: 0
     _mapname: Procedural Map
             length: 14
             start_char: P
     _modDir: rust
             length: 4
             start_char: r
     _product: 252490
             length: 6
             start_char: 2
     _gameDescription: Rust
             length: 4
             start_char: R
     _serverName: Mughisi's Playground
             length: 20
             start_char: M
     _passworded: False
     _gametags: mp10,cp0,qp0,v2017,h4bcc25f1,stok,born1505842473
             length: 48
             start_char: m
     KeyValue: System.Collections.Generic.Dictionary`2[System.String,System.String]
             table: Array[37]
                      0
                      28
                      27
                      0
                      0
                      25
                      0
                      26
                      0
                      24
                      22
                      0
                      11
                      12
                      13
                      17
                      15
                      16
                      23
                      2
                      3
                      20
                      5
                      6
                      7
                      8
                      9
                      18
                      0
                      0
                      0
                      0
                      0
                      0
                      0
                      21
                      29
             linkSlots: Array[37]
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
                          System.Collections.Generic.Link
             keySlots: Array[37]
                         description_0
                         description_01
                         description_02
                         description_03
                         description_04
                         description_05
                         description_06
                         description_07
                         description_08
                         description_09
                         description_10
                         description_11
                         description_12
                         description_13
                         description_14
                         description_15
                         hash
                         world.seed
                         world.size
                         pve
                         headerimage
                         url
                         uptime
                         gc_mb
                         gc_cl
                         fps
                         fps_avg
                         ent_cnt
                         build
             valueSlots: Array[37]
                           Powered by Oxide
                           4bcc25f1
                           1265
                           1000
                           False
                           http://oxidemod.org
                           1839
                           103
                           57
                           228
                           262.28
                           842
                           23033
             touchedSlots: 29
             emptySlot: -1
             count: 29
             threshold: 33
             hcp: System.Collections.Generic.GenericEqualityComparer`1[System.String]
             serialization_info: 
                     length: 4
                     start_char: n
             generation: 374
     native: Facepunch.Steamworks.Interop.NativeInterface
             api: SteamNative.SteamApi
                     platform: SteamNative.Platform+Win64
             client: SteamNative.SteamClient
                     platform: SteamNative.Platform+Win64
                     steamworks: Facepunch.Steamworks.Server
             user: SteamNative.SteamUser
                     platform: SteamNative.Platform+Win64
                     steamworks: Facepunch.Steamworks.Server
             apps: SteamNative.SteamApps
                     platform: SteamNative.Platform+Win64
                     steamworks: Facepunch.Steamworks.Server
             applist: SteamNative.SteamAppList
                     platform: SteamNative.Platform+Win64
                     steamworks: Facepunch.Steamworks.Server
             friends: SteamNative.SteamFriends
                     platform: SteamNative.Platform+Win64
                     steamworks: Facepunch.Steamworks.Server
             servers: SteamNative.SteamMatchmakingServers
                     platform: SteamNative.Platform+Win64
                     steamworks: Facepunch.Steamworks.Server
             matchmaking: SteamNative.SteamMatchmaking
                     platform: SteamNative.Platform+Win64
                     steamworks: Facepunch.Steamworks.Server
             inventory: SteamNative.SteamInventory
                     platform: SteamNative.Platform+Win64
                     steamworks: Facepunch.Steamworks.Server
             networking: SteamNative.SteamNetworking
                     platform: SteamNative.Platform+Win64
                     steamworks: Facepunch.Steamworks.Server
             userstats: SteamNative.SteamUserStats
                     platform: SteamNative.Platform+Win64
                     steamworks: Facepunch.Steamworks.Server
             utils: SteamNative.SteamUtils
                     platform: SteamNative.Platform+Win64
                     steamworks: Facepunch.Steamworks.Server
             screenshots: SteamNative.SteamScreenshots
                     platform: SteamNative.Platform+Win64
                     steamworks: Facepunch.Steamworks.Server
             http: SteamNative.SteamHTTP
                     platform: SteamNative.Platform+Win64
                     steamworks: Facepunch.Steamworks.Server
             ugc: SteamNative.SteamUGC
                     platform: SteamNative.Platform+Win64
                     steamworks: Facepunch.Steamworks.Server
             gameServer: SteamNative.SteamGameServer
                     platform: SteamNative.Platform+Win64
                     steamworks: Facepunch.Steamworks.Server
             gameServerStats: SteamNative.SteamGameServerStats
                     platform: SteamNative.Platform+Win64
                     steamworks: Facepunch.Steamworks.Server
             remoteStorage: SteamNative.SteamRemoteStorage
                     platform: SteamNative.Platform+Win64
                     steamworks: Facepunch.Steamworks.Server
             isServer: True
             hUser: SteamNative.HSteamUser
             hPipe: SteamNative.HSteamPipep
    
     
  3. OK that's helpful. What tool did you use to pull that info?

    The reason why I was chasing it from the SteamClient point of view is because that's where the actual BuildId is defined. I was thinking the Rust server is a Steam client during some early phase of Server boot and then also brought up the SteamServer at some point later.

    GitHub - Facepunch/Facepunch.Steamworks: Another fucking c# Steamworks implementation

    I believe inside the SteamServer structure, there's no BuildId. Again, I know it exists because I can find the AppId and BuildId current release values inside Windows Server memory by using a search from WinDbg when the Rust server is running. While they're not labeled as such, the values appear in hex very close to each other in only one place in RAM so I'm confident that's what it is. There has to be some way to get those values programmically through code.

    Let me see if I can track down where SteamServer is defined.
     
  4. The SteamClient is only created during the launch of the actual game client, if you'd open up Assembly-CSharp.dll from the client files you will notice some differences including SteamClient in Assembly-CSharp.dll to have an actual implementation and not an empty class like in the server version of the file.

    The debug output that I showed in my previous post was just grabbed through a plugin dumping all the fields and their values through reflection of the Facepunch.Steamworks.SteamServer instance.
     
  5. Ugh, I've gotten soo close, right up to being able to call the function that returns BuildId and then I get an indecipherable (to me) error.
    Code:
    var server_instance = Facepunch.Steamworks.Server.Instance;var server_native = server_instance.GetType().GetField("native", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
    var server_native_instance = server_native.GetValue(server_instance);var server_native_apps = server_native_instance.GetType().GetField("apps", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
    var server_native_apps_instance = server_native_apps.GetValue(server_native_instance);MethodInfo server_native_apps_method = server_native_apps_instance.GetType().GetMethod("GetAppBuildId");
    var bld = server_native_apps_method.Invoke(server_native_apps_instance, null);
    
    Which works all the way to the last line (where I can actually assign a local to the return value of the function providing the Steam BuildId, and I get this error on the Rust server console:
    Code:
    steamengine.cpp (2029) : Assertion Failed: Narrowing inPtr to toClass returned NULL.
    I know I'm close. I also verified the idea by running an older version Rust server install and checked with WinDbg. Like before, the AppId (258550) was in RAM nearby to the BuildId for that older server version.

    [Update]
    Even trying this creates the same error message:
    Code:
    [DllImport("steam_api64.dll", CharSet = CharSet.None, ExactSpelling = false)]
      internal static extern int SteamAPI_ISteamApps_GetAppBuildId(IntPtr ISteamApps);var server_native_apps_platform = server_native_apps_instance.GetType().GetField("platform", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
    var server_native_apps_platform_instance = server_native_apps_platform.GetValue(server_native_apps_instance);var server_native_apps_platform_ptr = server_native_apps_platform_instance.GetType().GetField("_ptr", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
    var server_native_apps_platform_ptr_instance = server_native_apps_platform_ptr.GetValue(server_native_apps_platform_instance);int b_id = SteamAPI_ISteamApps_GetAppBuildId((IntPtr)server_native_apps_platform_ptr_instance);
     
    Last edited by a moderator: Oct 3, 2017
  6. Just to post a follow-up to this, ultimately I had to run a file-based process to pull the installed buildid from parsing the file
    Code:
    C:\Rust\Server\rustds\steamapps\appmanifest_258550.acf
    Code:
    "SizeOnDisk"     "5803549654"
    "buildid"        "2217183"
    "LastOwner"      "76561200052722720"
    I'm not satisfied with that solution for a number of reasons. If anyone ever figures out how to find it through function calls to the running instance of Rust server/ Steam API, please post the answer here. Thanks