This commit is contained in:
2025-12-06 12:29:28 -06:00
parent 4887ef3db9
commit c9c2a848d6
5 changed files with 182 additions and 163 deletions

5
.gitignore vendored
View File

@@ -1 +1,4 @@
.DS_Store
.DS_Store
sdk/
*.dylib
*.o

View File

@@ -1,2 +0,0 @@
def:
c++ -fPIC -shared qjs_steam.cpp -Isdk/public -I../cell/source -Lsdk/redistributable_bin/osx -lsteam_api -lcell -o steam.dylib

View File

@@ -77,32 +77,25 @@ static ISteamHTTP *steam_http = NULL;
static ISteamInput *steam_input = NULL;
static ISteamInventory *steam_inventory = NULL;
static ISteamMusic *steam_music = NULL;
static bool steam_initialized = false;
extern "C" {
// STEAM INITIALIZATION
JSC_CCALL(steam_init,
if (steam_initialized) {
return JS_NewBool(js, true);
}
JSValue steam_init(JSContext *js) {
SteamErrMsg errMsg;
ESteamAPIInitResult init_result = SteamAPI_InitFlat(&errMsg);
ESteamAPIInitResult init_result = SteamAPI_InitFlat(nullptr);
switch (init_result) {
case k_ESteamAPIInitResult_OK:
steam_initialized = true;
break;
case k_ESteamAPIInitResult_NoSteamClient:
return JS_ThrowInternalError(js, "Failed to initialize Steam: Steam client is not running or not logged in");
case k_ESteamAPIInitResult_VersionMismatch:
return JS_ThrowInternalError(js, "Failed to initialize Steam: Steam client version is out of date");
case k_ESteamAPIInitResult_FailedGeneric:
default:
return JS_ThrowInternalError(js, "Failed to initialize Steam: Generic failure (check steam_appid.txt file exists and contains valid AppID)");
if (init_result != k_ESteamAPIInitResult_OK) {
switch (init_result) {
case k_ESteamAPIInitResult_NoSteamClient:
return JS_ThrowInternalError(js, "Failed to initialize Steam: Steam client is not running or not logged in - %s", errMsg);
case k_ESteamAPIInitResult_VersionMismatch:
return JS_ThrowInternalError(js, "Failed to initialize Steam: Steam client version is out of date - %s", errMsg);
case k_ESteamAPIInitResult_FailedGeneric:
default:
return JS_ThrowInternalError(js, "Failed to initialize Steam: %s", errMsg);
}
}
// Get interface pointers - must be called after SteamAPI_InitFlat succeeds
@@ -124,45 +117,31 @@ JSC_CCALL(steam_init,
// Verify critical interfaces were obtained
if (!steam_user) {
steam_initialized = false;
SteamAPI_Shutdown();
return JS_ThrowInternalError(js, "Failed to get Steam user interface");
}
if (!steam_utils) {
steam_initialized = false;
SteamAPI_Shutdown();
return JS_ThrowInternalError(js, "Failed to get Steam utils interface");
}
if (!steam_friends) {
steam_initialized = false;
SteamAPI_Shutdown();
return JS_ThrowInternalError(js, "Failed to get Steam friends interface");
}
// Verify user is logged in
if (!SteamAPI_ISteamUser_BLoggedOn(steam_user)) {
steam_initialized = false;
SteamAPI_Shutdown();
return JS_ThrowInternalError(js, "Steam user is not logged in");
}
return JS_NewBool(js, steam_initialized);
)
JSC_CCALL(steam_shutdown,
if (steam_initialized) {
SteamAPI_Shutdown();
steam_initialized = false;
}
return JS_NULL;
)
}
JSC_CCALL(steam_run_callbacks,
if (steam_initialized) {
SteamAPI_RunCallbacks();
}
SteamAPI_RunCallbacks();
return JS_NULL;
)
@@ -1247,6 +1226,7 @@ JSC_CCALL(user_decompress_voice,
size_t compressed_size;
void *compressed_data = js_get_blob_data(js, &compressed_size, argv[0]);
if (compressed_data == (void*)-1) return JS_EXCEPTION;
if (!compressed_data) return JS_ThrowTypeError(js, "First argument must be a blob");
uint32 dest_buffer_size = 22050 * 2;
@@ -1320,6 +1300,7 @@ JSC_CCALL(user_begin_auth_session,
size_t ticket_size;
void *ticket_data = js_get_blob_data(js, &ticket_size, argv[0]);
if (ticket_data == (void*)-1) return JS_EXCEPTION;
if (!ticket_data) return JS_ThrowTypeError(js, "First argument must be a blob");
steamid_t *sid = js2steamid(js, argv[1]);
@@ -1391,6 +1372,7 @@ JSC_CCALL(user_request_encrypted_app_ticket,
if (argc > 0 && !JS_IsNull(argv[0])) {
data = js_get_blob_data(js, &data_size, argv[0]);
if (data == (void*)-1) return JS_EXCEPTION;
if (!data) return JS_ThrowTypeError(js, "First argument must be a blob or null");
}
@@ -1752,6 +1734,10 @@ JSC_CCALL(cloud_write,
data = (uint8_t*)str;
} else {
data = (uint8_t*)js_get_blob_data(js, &data_len, argv[1]);
if (data == (uint8_t*)-1) {
JS_FreeCString(js, filename);
return JS_EXCEPTION;
}
if (!data) {
JS_FreeCString(js, filename);
return JS_ThrowTypeError(js, "Second argument must be string or ArrayBuffer");
@@ -2205,6 +2191,10 @@ JSC_CCALL(screenshots_write,
// Get image data from ArrayBuffer
size_t data_len;
uint8_t *data = (uint8_t*)js_get_blob_data(js, &data_len, argv[3]);
if (data == (uint8_t*)-1) {
JS_FreeCString(js, filename);
return JS_EXCEPTION;
}
if (!data) {
JS_FreeCString(js, filename);
return JS_ThrowTypeError(js, "Fourth argument must be ArrayBuffer containing RGB data");
@@ -2234,6 +2224,7 @@ JSC_CCALL(networking_send_p2p,
// Get data
size_t data_len;
uint8_t *data = (uint8_t*)js_get_blob_data(js, &data_len, argv[1]);
if (data == (uint8_t*)-1) return JS_EXCEPTION;
if (!data) return JS_ThrowTypeError(js, "Second argument must be ArrayBuffer");
// Get send type (optional, defaults to reliable)
@@ -3167,12 +3158,10 @@ static const JSCFunctionListEntry js_steam_ugc_funcs[] = {
// Main Steam API functions
static const JSCFunctionListEntry js_steam_funcs[] = {
MIST_FUNC_DEF(steam, init, 0),
MIST_FUNC_DEF(steam, shutdown, 0),
MIST_FUNC_DEF(steam, run_callbacks, 0),
};
JSValue js_steam_use(JSContext *js) {
CELL_USE_INIT(
// Register SteamID class if not already done
static int steamid_class_initialized = 0;
if (!steamid_class_initialized) {
@@ -3248,8 +3237,13 @@ JSValue js_steam_use(JSContext *js) {
JSValue ugc = JS_NewObject(js);
JS_SetPropertyFunctionList(js, ugc, js_steam_ugc_funcs, countof(js_steam_ugc_funcs));
JS_SetPropertyStr(js, steam, "ugc", ugc);
JSValue ret = steam_init(js);
if (JS_HasException(js))
return ret;
return steam;
}
)
} // extern "C"

View File

@@ -1,117 +0,0 @@
var steam = use('steam')
log.console("STEAM!")
// Test Steam functionality
if (steam && steam.user) {
var isLoggedOn = steam.user.logged_on()
log.console("Steam user logged on:", isLoggedOn)
if (isLoggedOn) {
var userName = steam.friends.name()
var steamId = steam.user.steam_id()
var appId = steam.app.id()
log.console("Steam user name: '" + userName + "'")
log.console("Steam user ID: '" + steamId.toString() + "'")
log.console("Steam app ID: " + appId.toString())
// Request user stats for achievements
steam.stats.request()
// Display online friends
log.console("\n=== ONLINE FRIENDS ===")
var friendCount = steam.friends.count(1) // EFriendFlagImmediate = 1
if (friendCount > 0) {
for (var i = 0; i < friendCount; i++) {
var friend = steam.friends.get_friend(i, 1)
if (friend) {
var friendName = steam.friends.get_friend_persona_name(friend)
var friendState = steam.friends.get_friend_persona_state(friend)
// Only show online friends (state > 0)
if (friendState > 0) {
var stateNames = ["Offline", "Online", "Busy", "Away", "Snooze", "Looking to Trade", "Looking to Play"]
var stateName = stateNames[friendState] || "Unknown"
log.console(" " + friendName + " - " + stateName)
// Check if friend is playing a game
var gameInfo = steam.friends.get_friend_game_played(friend)
if (gameInfo && gameInfo.game_id) {
var gameName = steam.friends.get_friend_rich_presence(friend, "steam_display")
if (gameName) {
log.console(" Playing: " + gameName)
} else {
log.console(" Playing game ID: " + gameInfo.game_id)
}
}
}
}
}
} else {
log.console(" No friends found or friends list not loaded yet")
}
// Display achievement information
log.console("\n=== ACHIEVEMENTS ===")
var achievementCount = steam.achievement.count()
if (achievementCount > 0) {
log.console("Total achievements: " + achievementCount)
for (var i = 0; i < achievementCount; i++) {
var achName = steam.achievement.name(i)
if (achName) {
var achieved = steam.achievement.get(achName)
var displayName = steam.achievement.get_display_attribute(achName, "name")
var description = steam.achievement.get_display_attribute(achName, "desc")
if (achieved) {
var unlockInfo = steam.achievement.get_and_unlock_time(achName)
if (unlockInfo) {
var unlockDate = new Date(unlockInfo.unlock_time * 1000)
log.console(" ✓ " + (displayName || achName) + " - Unlocked " + unlockDate.toLocaleDateString())
} else {
log.console(" ✓ " + (displayName || achName) + " - Unlocked")
}
} else {
// Check for progress on this achievement
var progressInt = steam.achievement.get_progress_limits_int(achName)
var progressFloat = steam.achievement.get_progress_limits_float(achName)
var progressText = ""
if (progressInt && progressInt.max_progress > 0) {
progressText = " (Progress: " + progressInt.min_progress + "/" + progressInt.max_progress + ")"
} else if (progressFloat && progressFloat.max_progress > 0) {
progressText = " (Progress: " + progressFloat.min_progress.toFixed(1) + "/" + progressFloat.max_progress.toFixed(1) + ")"
}
log.console(" ✗ " + (displayName || achName) + progressText)
if (description) {
log.console(" " + description)
}
}
}
}
// Show global achievement stats if available
steam.stats.request_global_achievement_percentages()
// Get most achieved achievement info
var mostAchieved = steam.achievement.get_most_achieved_info()
if (mostAchieved) {
log.console("\nMost achieved: " + mostAchieved.name + " (" + mostAchieved.percent.toFixed(1) + "% of players)")
}
} else {
log.console("No achievements found for this game")
}
// Display user phone/security status
log.console("\n=== ACCOUNT SECURITY ===")
log.console("Phone verified: " + steam.user.is_phone_verified())
log.console("Two-factor enabled: " + steam.user.is_two_factor_enabled())
log.console("Behind NAT: " + steam.user.is_behind_nat())
// Display user level and badge info if available
var userLevel = steam.user.level()
if (userLevel > 0) {
log.console("Steam level: " + userLevel)
}
}
}

141
tests/steam.cm Normal file
View File

@@ -0,0 +1,141 @@
var steam = use('steam')
var time = use('time')
return {
test_steam_login_status: function() {
var isLoggedOn = steam.user.logged_on()
log.console("Steam user logged on: " + isLoggedOn)
if (isLoggedOn) {
var userName = steam.friends.name()
var steamId = steam.user.steam_id()
var appId = steam.app.id()
log.console("Steam user name: '" + userName + "'")
log.console("Steam user ID: '" + steamId.toString() + "'")
log.console("Steam app ID: " + appId.toString())
}
},
test_steam_stats_request: function() {
if (steam.user.logged_on()) {
// Request user stats for achievements
steam.stats.request()
}
},
test_steam_friends: function() {
if (!steam.user.logged_on()) return;
// Display online friends
log.console("\n=== ONLINE FRIENDS ===")
var friendCount = steam.friends.count(1) // EFriendFlagImmediate = 1
if (friendCount > 0) {
for (var i = 0; i < friendCount; i++) {
var friend = steam.friends.get_friend(i)
if (friend) {
var friendName = steam.friends.get_friend_persona_name(friend)
var friendState = steam.friends.get_friend_persona_state(friend)
// Only show online friends (state > 0)
if (friendState > 0) {
var stateNames = ["Offline", "Online", "Busy", "Away", "Snooze", "Looking to Trade", "Looking to Play"]
var stateName = stateNames[friendState] || "Unknown"
log.console(" " + friendName + " - " + stateName)
// Check if friend is playing a game
var gameInfo = steam.friends.get_friend_game_played(friend)
if (gameInfo && gameInfo.game_id) {
var gameName = steam.friends.get_friend_rich_presence(friend, "steam_display")
if (gameName) {
log.console(" Playing: " + gameName)
} else {
log.console(" Playing game ID: " + gameInfo.game_id)
}
}
}
}
}
} else {
log.console(" No friends found or friends list not loaded yet")
}
},
test_steam_achievements: function() {
if (!steam.user.logged_on()) return;
// Display achievement information
log.console("\n=== ACHIEVEMENTS ===")
var achievementCount = steam.achievement.count()
if (achievementCount > 0) {
log.console("Total achievements: " + text(achievementCount))
for (var i = 0; i < achievementCount; i++) {
var achName = steam.achievement.name(i)
if (achName) {
var achieved = steam.achievement.get(achName)
var displayName = steam.achievement.get_display_attribute(achName, "name")
var description = steam.achievement.get_display_attribute(achName, "desc")
if (achieved) {
var unlockInfo = steam.achievement.get_and_unlock_time(achName)
if (unlockInfo) {
var unlockDate = time.text(unlockInfo.unlock_time * 1000)
log.console(" ✓ " + (displayName || achName) + " - Unlocked " + unlockDate)
} else {
log.console(" ✓ " + (displayName || achName) + " - Unlocked")
}
} else {
// Check for progress on this achievement
var progressInt = steam.achievement.get_progress_limits_int(achName)
var progressFloat = steam.achievement.get_progress_limits_float(achName)
var progressText = ""
if (progressInt && progressInt.max_progress > 0) {
progressText = " (Progress: " + progressInt.min_progress + "/" + progressInt.max_progress + ")"
} else if (progressFloat && progressFloat.max_progress > 0) {
progressText = " (Progress: " + progressFloat.min_progress.toFixed(1) + "/" + progressFloat.max_progress.toFixed(1) + ")"
}
log.console(" ✗ " + (displayName || achName) + progressText)
if (description) {
log.console(" " + description)
}
}
}
}
} else {
log.console("No achievements found for this game")
}
},
test_steam_global_stats: function() {
if (!steam.user.logged_on()) return;
// Show global achievement stats if available
steam.stats.request_global_achievement_percentages()
// Get most achieved achievement info
var mostAchieved = steam.achievement.get_most_achieved_info()
if (mostAchieved) {
log.console("\nMost achieved: " + mostAchieved.name + " (" + mostAchieved.percent.toFixed(1) + "% of players)")
}
},
test_steam_security: function() {
if (!steam.user.logged_on()) return;
// Display user phone/security status
log.console("\n=== ACCOUNT SECURITY ===")
log.console(`Phone verified: ${steam.user.is_phone_verified()}`)
log.console(`Two-factor enabled: ${steam.user.is_two_factor_enabled()}`)
log.console(`Behind NAT: ${steam.user.is_behind_nat()}`)
},
test_steam_level: function() {
if (!steam.user.logged_on()) return;
// Display user level and badge info if available
var userLevel = steam.user.level()
if (userLevel > 0) {
log.console(`Steam level: ${userLevel}`)
}
}
}