Steamworks SDK wrapper written in a darty way to be used in dart projects
Here are a list of games that have been published on Steam that utilise this package:
License: BSD 3-Clause "New" or "Revised" License
Steamworks SDK wrapper written in a darty way to be used in dart projects
Here are a list of games that have been published on Steam that utilise this package:
steam sdk v1.57
steamworks v0.4.0
I uploaded 2 ugc items to my game demo (reviewing, unpublish)
Query all ugc items for the game, follow the guide to do
extension SteamClientEx on SteamClient {
Future<List<SteamUgcDetails>> getAllItems(
{required int page}) async {
final completer = Completer<List<SteamUgcDetails>>();
final appId = steamUtils.getAppId();
UgcQueryHandle query = steamUgc.createQueryAllUgcRequestPage(
EUgcQuery.rankedByLastUpdatedDate,
EUgcMatchingUgcType.all,
appId,
appId,
page, // start from 1
);
steamUgc.setReturnOnlyIds(query, false);
steamUgc.setReturnKeyValueTags(query, true); // important, return item detail properties when set it to true
final callId = steamUgc.sendQueryUgcRequest(query);
registerCallResult<SteamUgcQueryCompleted>(
asyncCallId: callId,
cb: (result, failed) {
print('Query ugc items result ${{
'result': result.result,
'total': result.totalMatchingResults,
'current': result.numResultsReturned,
}}');
final List<SteamUgcDetails> details = [];
if (result.result == EResult.eResultOK) {
for (var i = 0; i < result.numResultsReturned; i++) {
/// use calloc
final detail = calloc<SteamUgcDetails>();
final result = steamUgc.getQueryUgcResult(query, i, detail);
print('getQueryUgcResult $i $result');
details.add(detail.ref);
}
}
print('details length ${details.length}');
if (details.isNotEmpty) {
print('item publishedFileId:${details.first.publishedFileId}');
print('item publishedFileId:${details.last.publishedFileId}');
}
completer.complete(details);
});
return completer.future;
}
}
Future main() async {
SteamClient.init();
SteamClient steamClient = SteamClient.instance;
SteamClient.instance.getAllItems(page:1);
while (true) {
steamClient.runFrame();
await Future<void>.delayed(const Duration(seconds: 1));
}
}
try...catch
to catch the error.The first item id equal 0 or other number like 140698323595664
The second item id is right
Setting breakpad minidump AppID = 2550380
SteamInternal_SetMinidumpSteamID: Caching Steam ID: 76561197999689957 [API loaded no]
Query ugc items result {result: EResult.eResultOK, total: 2, current: 2}
getQueryUgcResult 0 true
getQueryUgcResult 1 true
details length 2
item publishedFileId:0
item publishedFileId:3018190198
Process finished with exit code -1073741819 (0xC0000005)
This time missing some log, look like crashed from steamUgc.getQueryUgcResult
Setting breakpad minidump AppID = 2550380
SteamInternal_SetMinidumpSteamID: Caching Steam ID: 76561197999689957 [API loaded no]
Query ugc items result {result: EResult.eResultOK, total: 2, current: 2}
Process finished with exit code -1073741819 (0xC0000005)
When I want to print more item info, like that:
if (details.isNotEmpty) {
print(
'item publishedFileId:${details.first.publishedFileId} ${details.first.title.toDartString()}');
print(
'item publishedFileId:${details.last.publishedFileId} ${details.first.title.toDartString()}');
}
It will show red error strings.
Setting breakpad minidump AppID = 2550380
SteamInternal_SetMinidumpSteamID: Caching Steam ID: 76561197999689957 [API loaded no]
Query ugc items result {result: EResult.eResultOK, total: 2, current: 2}
===== CRASH =====
ExceptionCode=-1073741819, ExceptionFlags=0, ExceptionAddress=00007FFF20D51AFC
version=3.0.6 (stable) (Tue Jul 11 18:49:07 2023 +0000) on "windows_x64"
pid=18512, thread=16224, isolate_group=kernel-service(0000010B1B28A870), isolate=(nil)(0000000000000000)
os=windows, arch=x64, comp=no, sim=no
isolate_instructions=10b1e3a0000, vm_instructions=7ff6e1366970
fp=10b1b228df0, sp=2e4ffe7f0, pc=7fff20d51afc
Stack dump aborted because GetAndValidateThreadStackBounds failed.
Process finished with exit code -1073740940 (0xC0000374)
Setting breakpad minidump AppID = 2550380
SteamInternal_SetMinidumpSteamID: Caching Steam ID: 76561197999689957 [API loaded no]
Query ugc items result {result: EResult.eResultOK, total: 2, current: 2}
getQueryUgcResult 0 true
../../runtime/vm/dart_api_impl.cc: 3047: error: Dart_StringToUTF8 expects there to be a current isolate. Did you forget to call Dart_CreateIsolateGroup or Dart_EnterIsolate?
version=3.0.6 (stable) (Tue Jul 11 18:49:07 2023 +0000) on "windows_x64"
pid=19540, thread=5016, isolate_group=(nil)(0000000000000000), isolate=(nil)(0000000000000000)
os=windows, arch=x64, comp=no, sim=no
isolate_instructions=0, vm_instructions=7ff6e1366970
fp=501c6feb90, sp=501c6feb90, pc=7ff6e15748f2
===== CRASH =====
ExceptionCode=-1073741819, ExceptionFlags=0, ExceptionAddress=00007FF6E14B2508
Aborting reentrant request for stack trace.
Process finished with exit code -1073740791 (0xC0000409)
import 'package:ffi/ffi.dart';
for(var i = 0;i< total; i++){
final detail = calloc<SteamUgcDetails>();
steamUgc.getQueryUgcResult(result.handle, i, detail);
// above work fine
final previewUrl = calloc<Utf8>();
final getPreviewUrl = steamUgc.getQueryUgcPreviewUrl(
result.handle,
i,
previewUrl,
256,
);
print('previewUrl $getPreviewUrl');
}
throw
error G1DE28886: Expected type 'Utf8' to be a valid and instantiated subtype of 'NativeType'.
I'm make sure the Utf8 import from ffi-2.1.0\lib\src\utf8.dart, Same definition as steamUgc.getQueryUgcPreviewUrl.
And the steamUgc.getQueryUgcKeyValueTag has same issue.
https://partner.steamgames.com/doc/sdk/api#SteamAPI_RestartAppIfNecessary
This is optional but highly recommended as the Steam context associated with your application (including your App ID) will not be set up if the user launches the executable directly. If this occurs then SteamAPI_Init will fail and you will be unable to use the Steamworks API.
If you choose to use this then it should be the first Steamworks function call you make, right before SteamAPI_Init.
But it's hidden, at https://github.com/aeb-dev/steamworks/blob/main/lib/steamworks.dart#L3
[Dispatcher] Callback request has been made but there were no callback registered for it. CallbackId: 1280010
[Dispatcher] Callback request has been made but there were no callback registered for it. CallbackId: 1280009
extension SteamClientEx on SteamClient {
Future subscribe(int id) async {
final complete = Completer<EResult>();
registerCallResult<RemoteStorageUnsubscribePublishedFileResult>(
asyncCallId: steamUgc.subscribeItem(id),
cb: (r, f) {
print('subscribe $id ${r.result}');
complete.complete(r.result);
},
);
return complete.future;
}
Future unsubscribe(int id) async {
final complete = Completer<EResult>();
registerCallResult<RemoteStorageUnsubscribePublishedFileResult>(
asyncCallId: steamUgc.unsubscribeItem(id),
cb: (r, f) {
print('unsubscribeItem $id ${r.result}');
complete.complete(r.result);
},
);
return complete.future;
}
}
steamUgc.setItemTags
extension SteamClientEx on SteamClien {
updateItem(List<String> tags){
// create handle ...
final tag = calloc<SteamParamStringArray>();
// how to set tags to tags.strings
// tag.strings is getter
tag.strings[0] = 'tag'.toNativeUtf8(); // will crash
tag.numStrings = 1; // numStrings is getter too, can not set value
final setTags= steamUgc.setItemTags(handle, tag);
}
}
Hey @aeb-dev,
I'm looking at macOS versus Steam, and I have a few questions about pkg:steamworks
and pkg:steamworks_gen
. I thought I'd put my questions here so that others benefit. So, here goes.
I noticed that steamworks_gen
generates code like this (in dl.dart
):
import "dart:ffi";
import "dart:io";
DynamicLibrary dl = Platform.isWindows
? DynamicLibrary.open("./steam_api64.dll")
: Platform.isLinux
? DynamicLibrary.open("./libsteam_api.so")
: DynamicLibrary.open("./libsteam_api.dylib");
This loads the dynamic library from the current directory. I seem to remember you saying that this is what Steamworks DLLs expects. For that reason, we have code that moves the .dylib
file from Flutter assets to the current directory at startup.
That said, on macOS, things seem to work slightly differently. For security (or, "security"...) purposes, applications are packaged in directories (giant_robot.app/
), and those directories are signed and, presumably, read-only. At least once the app is notarized, which is something macOS might require at some point. The idea is that Apple runs tests and diagnostics on each notarized app after it has been cryptographically signed, and so it will flag apps that change themselves at runtime as dangerous. (But don't take my word for it, please, I'm easily confused.)
The way dynamic libraries are linked from Flutter is explained here on flutter.dev. Once you do all those steps in Xcode, you then open the library from Flutter like so:
- Use
DynamicLibrary.open('libyourlibrary.dylib')
to dynamically link to the symbols.
Note that the path is omitted. Xcode makes sure the dylib is in path (and code signed).
Is this viable for libsteam_api.dylib
? Or is there really some mechanism for which Steam absolutely must be in the current working directory?
I realize that the current approach (copy from assets to current working directory) seems to work. But it will (probably?) not work once we try to package the app for macOS and especially if we want to notarize it. So I'm exploring options.
Here's some on my research on the topic, for folks who're in the same boat:
libJoltFFI.dylib
).dmg
files)I tried copying the v1.58a steam_api64.dll file into the build, but it doesn't work.
My code
/// widget.file.id is steam ugc item id
///
ListTile(
title: Text(WindowsUI.openInSteam.tr),
trailing: Icon(Icons.chevron_right_rounded),
onTap: () {
SteamClient.instance.steamFriends.activateGameOverlayToWebPage(
'https://steamcommunity.com/sharedfiles/filedetails/?id=${widget.file.id}'
.toNativeUtf8(),
EActivateGameOverlayToWebPageMode.default_,
);
},
),
In the development environment, it successfully wake up the Steam native window and open the web page in the Steam window.
But in the production environment and the program startup from steam, when pressed this ListTile, it just display a webview overlay over the flutter window, the problem is it frozen the flutter program window, can not close, can not move the window, can not interactive anything in this window, it dead.
I record a screen video.
steamFriends.activateGameOverlay
, steamFriends.ActivateGameOverlayToStore
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.