add steam module
This commit is contained in:
187
examples/steam_example.js
Normal file
187
examples/steam_example.js
Normal file
@@ -0,0 +1,187 @@
|
||||
// Steam Integration Example
|
||||
// This example shows how to use Steam achievements and stats
|
||||
|
||||
var steam = use("steam");
|
||||
|
||||
// Achievement names (these should match your Steam app configuration)
|
||||
var ACHIEVEMENTS = {
|
||||
FIRST_WIN: "ACH_FIRST_WIN",
|
||||
PLAY_10_GAMES: "ACH_PLAY_10_GAMES",
|
||||
HIGH_SCORE: "ACH_HIGH_SCORE_1000"
|
||||
};
|
||||
|
||||
// Stat names
|
||||
var STATS = {
|
||||
GAMES_PLAYED: "stat_games_played",
|
||||
TOTAL_SCORE: "stat_total_score",
|
||||
PLAY_TIME: "stat_play_time"
|
||||
};
|
||||
|
||||
var steam_available = false;
|
||||
var stats_loaded = false;
|
||||
|
||||
// Initialize Steam
|
||||
function init_steam() {
|
||||
if (!steam) {
|
||||
console.log("Steam module not available");
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log("Initializing Steam...");
|
||||
steam_available = steam.steam_init();
|
||||
|
||||
if (steam_available) {
|
||||
console.log("Steam initialized successfully");
|
||||
|
||||
// Request current stats/achievements
|
||||
if (steam.stats.stats_request()) {
|
||||
console.log("Stats requested");
|
||||
stats_loaded = true;
|
||||
}
|
||||
} else {
|
||||
console.log("Failed to initialize Steam");
|
||||
}
|
||||
|
||||
return steam_available;
|
||||
}
|
||||
|
||||
// Update Steam (call this regularly, e.g., once per frame)
|
||||
function update_steam() {
|
||||
if (steam_available) {
|
||||
steam.steam_run_callbacks();
|
||||
}
|
||||
}
|
||||
|
||||
// Unlock an achievement
|
||||
function unlock_achievement(achievement_name) {
|
||||
if (!steam_available || !stats_loaded) return false;
|
||||
|
||||
// Check if already unlocked
|
||||
var unlocked = steam.achievement.achievement_get(achievement_name);
|
||||
if (unlocked) {
|
||||
console.log("Achievement already unlocked:", achievement_name);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Unlock it
|
||||
if (steam.achievement.achievement_set(achievement_name)) {
|
||||
console.log("Achievement unlocked:", achievement_name);
|
||||
|
||||
// Store stats to make it permanent
|
||||
steam.stats.stats_store();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update a stat
|
||||
function update_stat(stat_name, value, is_float) {
|
||||
if (!steam_available || !stats_loaded) return false;
|
||||
|
||||
var success;
|
||||
if (is_float) {
|
||||
success = steam.stats.stats_set_float(stat_name, value);
|
||||
} else {
|
||||
success = steam.stats.stats_set_int(stat_name, value);
|
||||
}
|
||||
|
||||
if (success) {
|
||||
console.log("Stat updated:", stat_name, "=", value);
|
||||
steam.stats.stats_store();
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
// Get a stat value
|
||||
function get_stat(stat_name, is_float) {
|
||||
if (!steam_available || !stats_loaded) return 0;
|
||||
|
||||
if (is_float) {
|
||||
return steam.stats.stats_get_float(stat_name) || 0;
|
||||
} else {
|
||||
return steam.stats.stats_get_int(stat_name) || 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Example game logic
|
||||
var games_played = 0;
|
||||
var total_score = 0;
|
||||
var current_score = 0;
|
||||
|
||||
function start_game() {
|
||||
games_played = get_stat(STATS.GAMES_PLAYED, false);
|
||||
total_score = get_stat(STATS.TOTAL_SCORE, false);
|
||||
current_score = 0;
|
||||
|
||||
console.log("Starting game #" + (games_played + 1));
|
||||
}
|
||||
|
||||
function end_game(score) {
|
||||
current_score = score;
|
||||
games_played++;
|
||||
total_score += score;
|
||||
|
||||
// Update stats
|
||||
update_stat(STATS.GAMES_PLAYED, games_played, false);
|
||||
update_stat(STATS.TOTAL_SCORE, total_score, false);
|
||||
|
||||
// Check for achievements
|
||||
if (games_played === 1) {
|
||||
unlock_achievement(ACHIEVEMENTS.FIRST_WIN);
|
||||
}
|
||||
|
||||
if (games_played >= 10) {
|
||||
unlock_achievement(ACHIEVEMENTS.PLAY_10_GAMES);
|
||||
}
|
||||
|
||||
if (score >= 1000) {
|
||||
unlock_achievement(ACHIEVEMENTS.HIGH_SCORE);
|
||||
}
|
||||
}
|
||||
|
||||
// Cloud save example
|
||||
function save_to_cloud(save_data) {
|
||||
if (!steam_available) return false;
|
||||
|
||||
var json_data = JSON.stringify(save_data);
|
||||
return steam.cloud.cloud_write("savegame.json", json_data);
|
||||
}
|
||||
|
||||
function load_from_cloud() {
|
||||
if (!steam_available) return null;
|
||||
|
||||
var data = steam.cloud.cloud_read("savegame.json");
|
||||
if (data) {
|
||||
// Convert ArrayBuffer to string
|
||||
var decoder = new TextDecoder();
|
||||
var json_str = decoder.decode(data);
|
||||
return JSON.parse(json_str);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
function cleanup_steam() {
|
||||
if (steam_available) {
|
||||
steam.steam_shutdown();
|
||||
console.log("Steam shut down");
|
||||
}
|
||||
}
|
||||
|
||||
// Export the API
|
||||
module.exports = {
|
||||
init: init_steam,
|
||||
update: update_steam,
|
||||
cleanup: cleanup_steam,
|
||||
unlock_achievement: unlock_achievement,
|
||||
update_stat: update_stat,
|
||||
get_stat: get_stat,
|
||||
start_game: start_game,
|
||||
end_game: end_game,
|
||||
save_to_cloud: save_to_cloud,
|
||||
load_from_cloud: load_from_cloud,
|
||||
is_available: function() { return steam_available; }
|
||||
};
|
||||
33
meson.build
33
meson.build
@@ -8,6 +8,8 @@ libtype = get_option('default_library')
|
||||
link = []
|
||||
src = []
|
||||
|
||||
fs = import('fs')
|
||||
|
||||
add_project_arguments('-pedantic', language: ['c'])
|
||||
|
||||
git_tag_cmd = run_command('git', 'describe', '--tags', '--abbrev=0', check: false)
|
||||
@@ -157,6 +159,37 @@ endif
|
||||
deps += dependency('soloud', static:true)
|
||||
deps += dependency('libqrencode', static:true)
|
||||
|
||||
# Storefront SDK support
|
||||
storefront = get_option('storefront')
|
||||
if storefront == 'steam'
|
||||
steam_sdk_path = meson.current_source_dir() / 'sdk'
|
||||
|
||||
if host_machine.system() == 'darwin'
|
||||
steam_lib_path = steam_sdk_path / 'redistributable_bin' / 'osx' / 'libsteam_api.dylib'
|
||||
elif host_machine.system() == 'linux'
|
||||
steam_lib_path = steam_sdk_path / 'redistributable_bin' / 'linux64' / 'libsteam_api.so'
|
||||
elif host_machine.system() == 'windows'
|
||||
steam_lib_path = steam_sdk_path / 'redistributable_bin' / 'win64' / 'steam_api64.lib'
|
||||
else
|
||||
steam_lib_path = ''
|
||||
endif
|
||||
|
||||
if fs.exists(steam_lib_path)
|
||||
steam_dep = declare_dependency(
|
||||
include_directories: include_directories('sdk/public'),
|
||||
link_args: [steam_lib_path]
|
||||
)
|
||||
deps += steam_dep
|
||||
src += 'qjs_steam.cpp'
|
||||
message('Steam SDK enabled')
|
||||
else
|
||||
error('Steam SDK required but not found at: ' + steam_lib_path)
|
||||
endif
|
||||
else
|
||||
add_project_arguments('-DNSTEAM', language: ['c', 'cpp'])
|
||||
message('Storefront: ' + storefront)
|
||||
endif
|
||||
|
||||
link_args = link
|
||||
sources = []
|
||||
src += [
|
||||
|
||||
@@ -48,6 +48,9 @@
|
||||
#include "qjs_spline.h"
|
||||
#include "qjs_js.h"
|
||||
#include "qjs_debug.h"
|
||||
#ifndef NSTEAM
|
||||
#include "qjs_steam.h"
|
||||
#endif
|
||||
|
||||
SDL_Window *global_window;
|
||||
|
||||
@@ -1041,6 +1044,9 @@ JS_FreeValue(js,v); \
|
||||
} \
|
||||
|
||||
JSC_CCALL(os_engine_start,
|
||||
if (!SDL_Init(SDL_INIT_VIDEO))
|
||||
return JS_ThrowInternalError(js, "Unable to initialize video subsystem: %s", SDL_GetError());
|
||||
|
||||
if (SDL_GetCurrentThreadID() != main_thread)
|
||||
return JS_ThrowInternalError(js, "This can only be called from the root actor.");
|
||||
|
||||
@@ -2870,6 +2876,10 @@ void ffi_load(JSContext *js)
|
||||
arrput(rt->module_registry, MISTLINE(tracy));
|
||||
#endif
|
||||
|
||||
#ifndef NSTEAM
|
||||
arrput(rt->module_registry, MISTLINE(steam));
|
||||
#endif
|
||||
|
||||
JSValue globalThis = JS_GetGlobalObject(js);
|
||||
|
||||
JSValue prosp = JS_NewObject(js);
|
||||
|
||||
515
source/qjs_steam.cpp
Normal file
515
source/qjs_steam.cpp
Normal file
@@ -0,0 +1,515 @@
|
||||
#ifndef NSTEAM
|
||||
|
||||
extern "C" {
|
||||
#include "quickjs.h"
|
||||
#include "qjs_macros.h"
|
||||
#include "jsffi.h"
|
||||
}
|
||||
|
||||
#include <steam/steam_api.h>
|
||||
#include <steam/steam_api_flat.h>
|
||||
#include <string.h>
|
||||
|
||||
// Steam interface pointers
|
||||
static ISteamUserStats *steam_stats = NULL;
|
||||
static ISteamApps *steam_apps = NULL;
|
||||
static ISteamRemoteStorage *steam_remote = NULL;
|
||||
static ISteamUGC *steam_ugc = NULL;
|
||||
static ISteamUser *steam_user = NULL;
|
||||
static ISteamFriends *steam_friends = NULL;
|
||||
static ISteamUtils *steam_utils = NULL;
|
||||
static bool steam_initialized = false;
|
||||
|
||||
// STEAM INITIALIZATION
|
||||
JSC_CCALL(steam_init,
|
||||
if (steam_initialized) {
|
||||
return JS_NewBool(js, true);
|
||||
}
|
||||
|
||||
SteamErrMsg err;
|
||||
steam_initialized = SteamAPI_InitEx(&err);
|
||||
|
||||
if (!steam_initialized) {
|
||||
return JS_ThrowInternalError(js, "Failed to initialize Steam: %s", err);
|
||||
}
|
||||
|
||||
// Get interface pointers
|
||||
steam_stats = SteamAPI_SteamUserStats();
|
||||
steam_apps = SteamAPI_SteamApps();
|
||||
steam_remote = SteamAPI_SteamRemoteStorage();
|
||||
steam_ugc = SteamAPI_SteamUGC();
|
||||
steam_user = SteamAPI_SteamUser();
|
||||
steam_friends = SteamAPI_SteamFriends();
|
||||
steam_utils = SteamAPI_SteamUtils();
|
||||
|
||||
return JS_NewBool(js, steam_initialized);
|
||||
)
|
||||
|
||||
JSC_CCALL(steam_shutdown,
|
||||
if (steam_initialized) {
|
||||
SteamAPI_Shutdown();
|
||||
steam_initialized = false;
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
)
|
||||
|
||||
JSC_CCALL(steam_run_callbacks,
|
||||
if (steam_initialized) {
|
||||
SteamAPI_RunCallbacks();
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
)
|
||||
|
||||
// USER STATS & ACHIEVEMENTS
|
||||
JSC_CCALL(stats_request,
|
||||
if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized");
|
||||
|
||||
bool success = SteamAPI_ISteamUserStats_RequestCurrentStats(steam_stats);
|
||||
return JS_NewBool(js, success);
|
||||
)
|
||||
|
||||
JSC_CCALL(stats_store,
|
||||
if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized");
|
||||
|
||||
bool success = SteamAPI_ISteamUserStats_StoreStats(steam_stats);
|
||||
return JS_NewBool(js, success);
|
||||
)
|
||||
|
||||
JSC_CCALL(stats_get_int,
|
||||
if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized");
|
||||
|
||||
const char *name = JS_ToCString(js, argv[0]);
|
||||
if (!name) return JS_EXCEPTION;
|
||||
|
||||
int32 value;
|
||||
bool success = SteamAPI_ISteamUserStats_GetStatInt32(steam_stats, name, &value);
|
||||
JS_FreeCString(js, name);
|
||||
|
||||
if (!success) return JS_UNDEFINED;
|
||||
return JS_NewInt32(js, value);
|
||||
)
|
||||
|
||||
JSC_CCALL(stats_get_float,
|
||||
if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized");
|
||||
|
||||
const char *name = JS_ToCString(js, argv[0]);
|
||||
if (!name) return JS_EXCEPTION;
|
||||
|
||||
float value;
|
||||
bool success = SteamAPI_ISteamUserStats_GetStatFloat(steam_stats, name, &value);
|
||||
JS_FreeCString(js, name);
|
||||
|
||||
if (!success) return JS_UNDEFINED;
|
||||
return JS_NewFloat64(js, value);
|
||||
)
|
||||
|
||||
JSC_CCALL(stats_set_int,
|
||||
if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized");
|
||||
|
||||
const char *name = JS_ToCString(js, argv[0]);
|
||||
if (!name) return JS_EXCEPTION;
|
||||
|
||||
int32 value;
|
||||
JS_ToInt32(js, &value, argv[1]);
|
||||
|
||||
bool success = SteamAPI_ISteamUserStats_SetStatInt32(steam_stats, name, value);
|
||||
JS_FreeCString(js, name);
|
||||
|
||||
return JS_NewBool(js, success);
|
||||
)
|
||||
|
||||
JSC_CCALL(stats_set_float,
|
||||
if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized");
|
||||
|
||||
const char *name = JS_ToCString(js, argv[0]);
|
||||
if (!name) return JS_EXCEPTION;
|
||||
|
||||
double dvalue;
|
||||
JS_ToFloat64(js, &dvalue, argv[1]);
|
||||
float value = (float)dvalue;
|
||||
|
||||
bool success = SteamAPI_ISteamUserStats_SetStatFloat(steam_stats, name, value);
|
||||
JS_FreeCString(js, name);
|
||||
|
||||
return JS_NewBool(js, success);
|
||||
)
|
||||
|
||||
JSC_CCALL(achievement_get,
|
||||
if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized");
|
||||
|
||||
const char *name = JS_ToCString(js, argv[0]);
|
||||
if (!name) return JS_EXCEPTION;
|
||||
|
||||
bool achieved;
|
||||
bool success = SteamAPI_ISteamUserStats_GetAchievement(steam_stats, name, &achieved);
|
||||
JS_FreeCString(js, name);
|
||||
|
||||
if (!success) return JS_UNDEFINED;
|
||||
return JS_NewBool(js, achieved);
|
||||
)
|
||||
|
||||
JSC_CCALL(achievement_set,
|
||||
if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized");
|
||||
|
||||
const char *name = JS_ToCString(js, argv[0]);
|
||||
if (!name) return JS_EXCEPTION;
|
||||
|
||||
bool success = SteamAPI_ISteamUserStats_SetAchievement(steam_stats, name);
|
||||
JS_FreeCString(js, name);
|
||||
|
||||
return JS_NewBool(js, success);
|
||||
)
|
||||
|
||||
JSC_CCALL(achievement_clear,
|
||||
if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized");
|
||||
|
||||
const char *name = JS_ToCString(js, argv[0]);
|
||||
if (!name) return JS_EXCEPTION;
|
||||
|
||||
bool success = SteamAPI_ISteamUserStats_ClearAchievement(steam_stats, name);
|
||||
JS_FreeCString(js, name);
|
||||
|
||||
return JS_NewBool(js, success);
|
||||
)
|
||||
|
||||
JSC_CCALL(achievement_count,
|
||||
if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized");
|
||||
|
||||
uint32 count = SteamAPI_ISteamUserStats_GetNumAchievements(steam_stats);
|
||||
return JS_NewUint32(js, count);
|
||||
)
|
||||
|
||||
JSC_CCALL(achievement_name,
|
||||
if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized");
|
||||
|
||||
uint32 index;
|
||||
JS_ToUint32(js, &index, argv[0]);
|
||||
|
||||
const char *name = SteamAPI_ISteamUserStats_GetAchievementName(steam_stats, index);
|
||||
if (!name) return JS_UNDEFINED;
|
||||
|
||||
return JS_NewString(js, name);
|
||||
)
|
||||
|
||||
// APPS
|
||||
JSC_CCALL(app_id,
|
||||
if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized");
|
||||
|
||||
AppId_t appid = SteamAPI_ISteamUtils_GetAppID(steam_utils);
|
||||
return JS_NewUint32(js, appid);
|
||||
)
|
||||
|
||||
JSC_CCALL(app_owner,
|
||||
if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized");
|
||||
|
||||
uint64_steamid owner = SteamAPI_ISteamApps_GetAppOwner(steam_apps);
|
||||
return JS_NewBigUint64(js, owner);
|
||||
)
|
||||
|
||||
JSC_CCALL(app_installed,
|
||||
if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized");
|
||||
|
||||
uint32 appid;
|
||||
JS_ToUint32(js, &appid, argv[0]);
|
||||
|
||||
bool installed = SteamAPI_ISteamApps_BIsAppInstalled(steam_apps, appid);
|
||||
return JS_NewBool(js, installed);
|
||||
)
|
||||
|
||||
JSC_CCALL(app_subscribed,
|
||||
if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized");
|
||||
|
||||
bool subscribed = SteamAPI_ISteamApps_BIsSubscribed(steam_apps);
|
||||
return JS_NewBool(js, subscribed);
|
||||
)
|
||||
|
||||
JSC_CCALL(app_language,
|
||||
if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized");
|
||||
|
||||
const char *lang = SteamAPI_ISteamApps_GetCurrentGameLanguage(steam_apps);
|
||||
return JS_NewString(js, lang ? lang : "english");
|
||||
)
|
||||
|
||||
JSC_CCALL(app_dlc_installed,
|
||||
if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized");
|
||||
|
||||
AppId_t dlcid;
|
||||
JS_ToUint32(js, &dlcid, argv[0]);
|
||||
|
||||
bool installed = SteamAPI_ISteamApps_BIsDlcInstalled(steam_apps, dlcid);
|
||||
return JS_NewBool(js, installed);
|
||||
)
|
||||
|
||||
// USER
|
||||
JSC_CCALL(user_logged_on,
|
||||
if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized");
|
||||
|
||||
bool logged = SteamAPI_ISteamUser_BLoggedOn(steam_user);
|
||||
return JS_NewBool(js, logged);
|
||||
)
|
||||
|
||||
JSC_CCALL(user_steam_id,
|
||||
if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized");
|
||||
|
||||
uint64_steamid id = SteamAPI_ISteamUser_GetSteamID(steam_user);
|
||||
return JS_NewBigUint64(js, id);
|
||||
)
|
||||
|
||||
JSC_CCALL(user_level,
|
||||
if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized");
|
||||
|
||||
int level = SteamAPI_ISteamUser_GetPlayerSteamLevel(steam_user);
|
||||
return JS_NewInt32(js, level);
|
||||
)
|
||||
|
||||
// FRIENDS
|
||||
JSC_CCALL(friends_name,
|
||||
if (!steam_friends) return JS_ThrowInternalError(js, "Steam friends not initialized");
|
||||
|
||||
const char *name = SteamAPI_ISteamFriends_GetPersonaName(steam_friends);
|
||||
return JS_NewString(js, name ? name : "");
|
||||
)
|
||||
|
||||
JSC_CCALL(friends_state,
|
||||
if (!steam_friends) return JS_ThrowInternalError(js, "Steam friends not initialized");
|
||||
|
||||
EPersonaState state = SteamAPI_ISteamFriends_GetPersonaState(steam_friends);
|
||||
const char *state_str;
|
||||
switch(state) {
|
||||
case k_EPersonaStateOffline: state_str = "offline"; break;
|
||||
case k_EPersonaStateOnline: state_str = "online"; break;
|
||||
case k_EPersonaStateBusy: state_str = "busy"; break;
|
||||
case k_EPersonaStateAway: state_str = "away"; break;
|
||||
case k_EPersonaStateSnooze: state_str = "snooze"; break;
|
||||
case k_EPersonaStateLookingToTrade: state_str = "trade"; break;
|
||||
case k_EPersonaStateLookingToPlay: state_str = "play"; break;
|
||||
default: state_str = "unknown"; break;
|
||||
}
|
||||
return JS_NewString(js, state_str);
|
||||
)
|
||||
|
||||
// CLOUD STORAGE
|
||||
JSC_CCALL(cloud_enabled_app,
|
||||
if (!steam_remote) return JS_ThrowInternalError(js, "Steam remote storage not initialized");
|
||||
|
||||
bool enabled = SteamAPI_ISteamRemoteStorage_IsCloudEnabledForApp(steam_remote);
|
||||
return JS_NewBool(js, enabled);
|
||||
)
|
||||
|
||||
JSC_CCALL(cloud_enabled_account,
|
||||
if (!steam_remote) return JS_ThrowInternalError(js, "Steam remote storage not initialized");
|
||||
|
||||
bool enabled = SteamAPI_ISteamRemoteStorage_IsCloudEnabledForAccount(steam_remote);
|
||||
return JS_NewBool(js, enabled);
|
||||
)
|
||||
|
||||
JSC_CCALL(cloud_enable,
|
||||
if (!steam_remote) return JS_ThrowInternalError(js, "Steam remote storage not initialized");
|
||||
|
||||
bool enable = JS_ToBool(js, argv[0]);
|
||||
SteamAPI_ISteamRemoteStorage_SetCloudEnabledForApp(steam_remote, enable);
|
||||
return JS_UNDEFINED;
|
||||
)
|
||||
|
||||
JSC_CCALL(cloud_quota,
|
||||
if (!steam_remote) return JS_ThrowInternalError(js, "Steam remote storage not initialized");
|
||||
|
||||
uint64 total, available;
|
||||
bool success = SteamAPI_ISteamRemoteStorage_GetQuota(steam_remote, &total, &available);
|
||||
|
||||
if (!success) return JS_UNDEFINED;
|
||||
|
||||
JSValue obj = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js, obj, "total", JS_NewBigUint64(js, total));
|
||||
JS_SetPropertyStr(js, obj, "available", JS_NewBigUint64(js, available));
|
||||
return obj;
|
||||
)
|
||||
|
||||
JSC_CCALL(cloud_write,
|
||||
if (!steam_remote) return JS_ThrowInternalError(js, "Steam remote storage not initialized");
|
||||
|
||||
const char *filename = JS_ToCString(js, argv[0]);
|
||||
if (!filename) return JS_EXCEPTION;
|
||||
|
||||
size_t data_len;
|
||||
uint8_t *data = NULL;
|
||||
|
||||
if (JS_IsString(argv[1])) {
|
||||
const char *str = JS_ToCStringLen(js, &data_len, argv[1]);
|
||||
if (!str) {
|
||||
JS_FreeCString(js, filename);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
data = (uint8_t*)str;
|
||||
} else {
|
||||
data = JS_GetArrayBuffer(js, &data_len, argv[1]);
|
||||
if (!data) {
|
||||
JS_FreeCString(js, filename);
|
||||
return JS_ThrowTypeError(js, "Second argument must be string or ArrayBuffer");
|
||||
}
|
||||
}
|
||||
|
||||
bool success = SteamAPI_ISteamRemoteStorage_FileWrite(steam_remote, filename, data, data_len);
|
||||
|
||||
if (JS_IsString(argv[1])) {
|
||||
JS_FreeCString(js, (const char*)data);
|
||||
}
|
||||
JS_FreeCString(js, filename);
|
||||
|
||||
return JS_NewBool(js, success);
|
||||
)
|
||||
|
||||
JSC_CCALL(cloud_read,
|
||||
if (!steam_remote) return JS_ThrowInternalError(js, "Steam remote storage not initialized");
|
||||
|
||||
const char *filename = JS_ToCString(js, argv[0]);
|
||||
if (!filename) return JS_EXCEPTION;
|
||||
|
||||
int32 size = SteamAPI_ISteamRemoteStorage_GetFileSize(steam_remote, filename);
|
||||
if (size <= 0) {
|
||||
JS_FreeCString(js, filename);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
uint8_t *buffer = (uint8_t*)js_malloc(js, size);
|
||||
if (!buffer) {
|
||||
JS_FreeCString(js, filename);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
int32 read = SteamAPI_ISteamRemoteStorage_FileRead(steam_remote, filename, buffer, size);
|
||||
JS_FreeCString(js, filename);
|
||||
|
||||
if (read != size) {
|
||||
js_free(js, buffer);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
JSValue result = JS_NewArrayBufferCopy(js, buffer, size);
|
||||
js_free(js, buffer);
|
||||
return result;
|
||||
)
|
||||
|
||||
JSC_CCALL(cloud_delete,
|
||||
if (!steam_remote) return JS_ThrowInternalError(js, "Steam remote storage not initialized");
|
||||
|
||||
const char *filename = JS_ToCString(js, argv[0]);
|
||||
if (!filename) return JS_EXCEPTION;
|
||||
|
||||
bool success = SteamAPI_ISteamRemoteStorage_FileDelete(steam_remote, filename);
|
||||
JS_FreeCString(js, filename);
|
||||
|
||||
return JS_NewBool(js, success);
|
||||
)
|
||||
|
||||
JSC_CCALL(cloud_exists,
|
||||
if (!steam_remote) return JS_ThrowInternalError(js, "Steam remote storage not initialized");
|
||||
|
||||
const char *filename = JS_ToCString(js, argv[0]);
|
||||
if (!filename) return JS_EXCEPTION;
|
||||
|
||||
bool exists = SteamAPI_ISteamRemoteStorage_FileExists(steam_remote, filename);
|
||||
JS_FreeCString(js, filename);
|
||||
|
||||
return JS_NewBool(js, exists);
|
||||
)
|
||||
|
||||
// Function lists for sub-objects
|
||||
static const JSCFunctionListEntry js_steam_stats_funcs[] = {
|
||||
MIST_FUNC_DEF(steam, stats_request, 0),
|
||||
MIST_FUNC_DEF(steam, stats_store, 0),
|
||||
MIST_FUNC_DEF(steam, stats_get_int, 1),
|
||||
MIST_FUNC_DEF(steam, stats_get_float, 1),
|
||||
MIST_FUNC_DEF(steam, stats_set_int, 2),
|
||||
MIST_FUNC_DEF(steam, stats_set_float, 2),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_steam_achievement_funcs[] = {
|
||||
MIST_FUNC_DEF(steam, achievement_get, 1),
|
||||
MIST_FUNC_DEF(steam, achievement_set, 1),
|
||||
MIST_FUNC_DEF(steam, achievement_clear, 1),
|
||||
MIST_FUNC_DEF(steam, achievement_count, 0),
|
||||
MIST_FUNC_DEF(steam, achievement_name, 1),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_steam_app_funcs[] = {
|
||||
MIST_FUNC_DEF(steam, app_id, 0),
|
||||
MIST_FUNC_DEF(steam, app_owner, 0),
|
||||
MIST_FUNC_DEF(steam, app_installed, 1),
|
||||
MIST_FUNC_DEF(steam, app_subscribed, 0),
|
||||
MIST_FUNC_DEF(steam, app_language, 0),
|
||||
MIST_FUNC_DEF(steam, app_dlc_installed, 1),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_steam_user_funcs[] = {
|
||||
MIST_FUNC_DEF(steam, user_logged_on, 0),
|
||||
MIST_FUNC_DEF(steam, user_steam_id, 0),
|
||||
MIST_FUNC_DEF(steam, user_level, 0),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_steam_friends_funcs[] = {
|
||||
MIST_FUNC_DEF(steam, friends_name, 0),
|
||||
MIST_FUNC_DEF(steam, friends_state, 0),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_steam_cloud_funcs[] = {
|
||||
MIST_FUNC_DEF(steam, cloud_enabled_app, 0),
|
||||
MIST_FUNC_DEF(steam, cloud_enabled_account, 0),
|
||||
MIST_FUNC_DEF(steam, cloud_enable, 1),
|
||||
MIST_FUNC_DEF(steam, cloud_quota, 0),
|
||||
MIST_FUNC_DEF(steam, cloud_write, 2),
|
||||
MIST_FUNC_DEF(steam, cloud_read, 1),
|
||||
MIST_FUNC_DEF(steam, cloud_delete, 1),
|
||||
MIST_FUNC_DEF(steam, cloud_exists, 1),
|
||||
};
|
||||
|
||||
// Main Steam API functions
|
||||
static const JSCFunctionListEntry js_steam_funcs[] = {
|
||||
MIST_FUNC_DEF(steam, steam_init, 0),
|
||||
MIST_FUNC_DEF(steam, steam_shutdown, 0),
|
||||
MIST_FUNC_DEF(steam, steam_run_callbacks, 0),
|
||||
};
|
||||
|
||||
extern "C" JSValue js_steam_use(JSContext *js) {
|
||||
JSValue steam = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, steam, js_steam_funcs, countof(js_steam_funcs));
|
||||
|
||||
// Stats sub-object
|
||||
JSValue stats = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, stats, js_steam_stats_funcs, countof(js_steam_stats_funcs));
|
||||
JS_SetPropertyStr(js, steam, "stats", stats);
|
||||
|
||||
// Achievement sub-object
|
||||
JSValue achievement = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, achievement, js_steam_achievement_funcs, countof(js_steam_achievement_funcs));
|
||||
JS_SetPropertyStr(js, steam, "achievement", achievement);
|
||||
|
||||
// App sub-object
|
||||
JSValue app = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, app, js_steam_app_funcs, countof(js_steam_app_funcs));
|
||||
JS_SetPropertyStr(js, steam, "app", app);
|
||||
|
||||
// User sub-object
|
||||
JSValue user = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, user, js_steam_user_funcs, countof(js_steam_user_funcs));
|
||||
JS_SetPropertyStr(js, steam, "user", user);
|
||||
|
||||
// Friends sub-object
|
||||
JSValue friends = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, friends, js_steam_friends_funcs, countof(js_steam_friends_funcs));
|
||||
JS_SetPropertyStr(js, steam, "friends", friends);
|
||||
|
||||
// Cloud sub-object
|
||||
JSValue cloud = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, cloud, js_steam_cloud_funcs, countof(js_steam_cloud_funcs));
|
||||
JS_SetPropertyStr(js, steam, "cloud", cloud);
|
||||
|
||||
return steam;
|
||||
}
|
||||
|
||||
#else
|
||||
// Stub when Steam is disabled
|
||||
extern "C" JSValue js_steam_use(JSContext *js) {
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
#endif
|
||||
8
source/qjs_steam.h
Normal file
8
source/qjs_steam.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef QJS_STEAM_H
|
||||
#define QJS_STEAM_H
|
||||
|
||||
#include "quickjs.h"
|
||||
|
||||
JSValue js_steam_use(JSContext *js);
|
||||
|
||||
#endif // QJS_STEAM_H
|
||||
40
tests/steam.js
Normal file
40
tests/steam.js
Normal file
@@ -0,0 +1,40 @@
|
||||
var steam = use("steam");
|
||||
|
||||
console.log("Steam module loaded:", steam);
|
||||
|
||||
if (steam) {
|
||||
console.log("Steam functions available:");
|
||||
console.log("- steam_init:", typeof steam.steam_init);
|
||||
console.log("- steam_shutdown:", typeof steam.steam_shutdown);
|
||||
console.log("- steam_run_callbacks:", typeof steam.steam_run_callbacks);
|
||||
|
||||
console.log("\nSteam sub-modules:");
|
||||
console.log("- stats:", steam.stats);
|
||||
console.log("- achievement:", steam.achievement);
|
||||
console.log("- app:", steam.app);
|
||||
console.log("- user:", steam.user);
|
||||
console.log("- friends:", steam.friends);
|
||||
console.log("- cloud:", steam.cloud);
|
||||
|
||||
// Try to initialize Steam
|
||||
console.log("\nAttempting to initialize Steam...");
|
||||
var init_result = steam.steam_init();
|
||||
console.log("Initialization result:", init_result);
|
||||
|
||||
if (init_result) {
|
||||
// Get some basic info
|
||||
console.log("\nApp ID:", steam.app.app_id());
|
||||
console.log("User logged on:", steam.user.user_logged_on());
|
||||
|
||||
if (steam.user.user_logged_on()) {
|
||||
console.log("User name:", steam.friends.friends_name());
|
||||
console.log("User state:", steam.friends.friends_state());
|
||||
}
|
||||
|
||||
// Shutdown when done
|
||||
steam.steam_shutdown();
|
||||
console.log("Steam shut down");
|
||||
}
|
||||
} else {
|
||||
console.log("Steam module not available (compiled without Steam support)");
|
||||
}
|
||||
Reference in New Issue
Block a user