Results 1 to 10 of 13

Thread: Steam wrapper, exploring options

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Member
    Join Date
    Apr 2014
    Location
    Lower Saxony, Germany
    Posts
    38
    Yeah, I was sparing with hints. Let me be more specific about what I have in mind with my pragmatic approach: I want to use just a few Steam features in my game (Steam achievements for example) without translating the whole API. I don't want to install a C environment (Visual Studio e.g.) for this limited purpose. And I want to avoid an additional custom DLL (which requires maintenance).

    Thanks for linking the steam_api_flat.h! Nice transition

    So, these are the first 10 lines (of nearly 700) of function declarations in steam_api_flat.h:

    Code:
    S_API HSteamPipe SteamAPI_ISteamClient_CreateSteamPipe(intptr_t instancePtr);
    S_API bool SteamAPI_ISteamClient_BReleaseSteamPipe(intptr_t instancePtr, HSteamPipe hSteamPipe);
    S_API HSteamUser SteamAPI_ISteamClient_ConnectToGlobalUser(intptr_t instancePtr, HSteamPipe hSteamPipe);
    S_API HSteamUser SteamAPI_ISteamClient_CreateLocalUser(intptr_t instancePtr, HSteamPipe * phSteamPipe, EAccountType eAccountType);
    S_API void SteamAPI_ISteamClient_ReleaseUser(intptr_t instancePtr, HSteamPipe hSteamPipe, HSteamUser hUser);
    S_API class ISteamUser * SteamAPI_ISteamClient_GetISteamUser(intptr_t instancePtr, HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char * pchVersion);
    S_API class ISteamGameServer * SteamAPI_ISteamClient_GetISteamGameServer(intptr_t instancePtr, HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char * pchVersion);
    S_API void SteamAPI_ISteamClient_SetLocalIPBinding(intptr_t instancePtr, uint32 unIP, uint16 usPort);
    S_API class ISteamFriends * SteamAPI_ISteamClient_GetISteamFriends(intptr_t instancePtr, HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char * pchVersion);
    S_API class ISteamUtils * SteamAPI_ISteamClient_GetISteamUtils(intptr_t instancePtr, HSteamPipe hSteamPipe, const char * pchVersion);
    ...
    "Flat" C-style means to me that some C++ classes reveal their public methods.
    The first function is SteamAPI_ISteamClient_CreateSteamPipe. I take this as: within the SteamAPI there is a Steam class ISteamClient which has a method CreateSteamPipe().
    In C++ one would call it like this: ISteamClient->CreateSteamPipe(). Or in Object Pascal: ISteamClient.CreateSteamPipe().
    The return value HSteamPipe seems to be a handle. That's longint to me.
    That instancePtr is puzzling for now. Just pointer.

    According to my code above I would declare:

    Code:
    var
      steamapi_isteamclient_createsteampipe: function(instanceptr: pointer): longint; cdecl = nil;
      steampipe_handle: longint;
    And in the above function steam_init I would add:

    Code:
    pointer(steamapi_isteamclient_createsteampipe) := getprocedureaddress(steamlib_handle, pchar('SteamAPI_ISteamClient_CreateSteamPipe'));
    Now if you call it like that ...

    Code:
    steampipe_handle := steamapi_isteamclient_createsteampipe(nil);
    ... you get Access Violation, which comes as no surprise.
    Let me reveal for now, that Nil has to be replaced by a meaningful value, namely a pointer to ISteamClient, which is the Interface of the Steam Client.

    To be continued.

  2. #2
    That instancePtr is puzzling for now. Just pointer.
    Consider this:

    Code:
    type 
    TMyClass = class
      function MyMethod(): boolean;
    end;
    But inside MyMethod implementation, you have access to automatically added variables Result and Self. Result is what received the function result while Self is the class instance from which this method was called:

    Code:
    var 
    MyInstance: TMyClass;
    b: boolean;
    ...
    b:= MyInstance.MyMethod;
    ..but where does Self come from?
    It's not magic, it's not rocket science. Any class method is in fact a regular procedure/function with one hidden "Self" parameter in its parameter list. Both Pascal and C++, could be boiled to

    Code:
    function MyClass_MyMethod(Self: TMyClass): boolean;
    Edit: virtual methods are the same it's the process of *calling* them that is more tricky.

    In this form you can *export* it from, say, your DLL, C-style. I'm sure *lots* of windows API functions that require you first get a handle to object then pass that handle to various other functions, may very well be constructor and class methods in disguise. Because THandle is ptruint, a pointer-sized unsigned integer.
    Things like TMyClass are typed pointers as well.
    Last edited by Chebmaster; 04-09-2018 at 05:56 PM.

  3. #3
    P.S. A working sample from my GUI code that exploits this:
    (inspited by web programming in JavaScript I use for living)
    Code:
      
      TEventSubscriptionMethodType = (
        esmt_simple, //a method with no parameters
        esmt_string  //a method receiving single UnicodeString
      );  
    
      TEventSubscription = record
        Receiver: TChepersyObject ;
        Method: AnsiString;
        MethodType: TEventSubscriptionMethodType;
        DefaultString: UnicodeString;
      end; 
    
    function TControl.PassEvent(var ES: TEventSubscription; w: UnicodeString): boolean;
      type tmethod_with_str_par = procedure(slf: TObject; par: UnicodeString);
      var
        method: procedure(slf: TObject);
        rclass: CChepersyObject;
        //method_with_str_par: procedure(slf: TObject; par: ansistring) //#$%@#. It compiles, works, but code completion is shoot >:( absolute method; //har har, two variables accupying the same address Ain't I clever?
        //a hack: calling orbitrary class method by its string name.
        //watch the parameter list! (shall either be none or one ansistring,
        //with ClickReceiverParam either nil or a non-empty string accordingly!)
      begin
        if not Assigned(ES.Receiver) then begin
    //...irrelevant to this topic
        end
        else begin
          if w = '' then w:= ES.DefaultString;
          Result:= Yes;
          pointer(method):= ES.Receiver.MethodAddress(ES.Method);
          if not Assigned(pointer(method)) then begin
            AddLog(RuEn(
              'Класс %0: не удалось передать событие, подписчик (%1) не имеет метода "%2" '#13'  параметр: "%3"',
              'Class %0: failed to pass event, the subscriber (%1) doesn''t have a method "%2" '#13'  parameter "%3"'),
                [self.ClassName, ES.Receiver.Classname, ES.Method, w]);
            Exit(No);
          end;
          try
            if ES.MethodType = esmt_simple then method(ES.Receiver)
            else begin
              tmethod_with_str_par(method)(ES.Receiver, w);
            end;
          except
            Die(RuEn(
              'Крах класса %0 при вызове подписчика события, %1.%2(%3)',
              'Class %0 crashed at calling the event subscriber %1.%2(%3)'),
              [self.ClassName, ES.Receiver.Classname, ES.Method, w])
          end;
        end;
      end;

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •