Not yet, but I plan to in the future. I need to get myself one first
I can give it a try if you want.
I have a Deck. Let me know if you need help with testing.
@xuan @iamcreasy thanks for your offer. I am considering it after this release. I have two jobs besides this so I am slow in the progress…
well back to some stuff…
I did not know how nice the steam overlay UI can appear in the game.
I just have an AppState than handles everything Steam related and so far it works great
Depthis has now been released on Steam
I managed to add leaderboards support. No DRM:
- If Steam is online… you have access to online leaderboards.
- If Steam is offline (not opened): you have local leaderboards (type your name)
I wanted to add Steam Input support but Steamworks4j does not support yet, it so I am forking it to add that in the future
Nice job. congratulations on the shipped project milestone!
Well done. This is a fantastic milestone.
Currently working on releasing it on the Epic Games Store as well.
I found a library that warps their SDK for Java (eos4j) but I am waiting for leaderboards support and hopefully by the end of the month it will be released there. I will be documenting any differences in implementing this compared to Steamworks4j
Great work!
I’m impressed you managed to work out how to use steamworks4j leader boards! I’ve been trying and failing with my arcade shooter game and getting nowhere. If you have a moment could you give us some code snippets to show how it’s done? Pretty please I can connect to steam but it’s the leader board stuff that catches me out. I understand if not.
@Mike_Robinson
Sure, I will make a simplified version of what I did for the leaderboards. I hope it helps. Add or fix what fits your game better
NOTE: you will need either a callback listener or a poll to pass the scores to your game. If you choose the callback, I recommend calling it on the App State’s update
method
package org.mypackage.scores;
import com.codedisaster.steamworks.*;
import com.jme3.app.Application;
public class SteamScoresManager extends extends BaseAppState {
private static final float TIME_BETWEEN_CALLBACKS = 0.083f; // about 12 calls per second
private SteamUtils utils;
private SteamUserStats userStats;
private SteamFriends friends;
private SteamLeaderboardHandle currentLeaderboard = null;
private boolean saveFirst;
private float currentTime = 0;
private int scoreToSave = -1;
@Override
protected void initialize(Application app) {
try{
SteamAPI.loadLibraries();
if(!SteamAPI.init()){
return;
}
userStats = new SteamUserStats(userStatsCallback);
friends = new SteamFriends(friendsCallback);
utils = new SteamUtils(utilsCallback);
}catch(SteamException ex){
//Handle Error here
}
}
@Override
protected void cleanup(Application app) {
if(null != userStats) {
userStats.dispose();
userStats = null;
}
if(null != friends) {
friends.dispose();
friends = null;
}
if(null != utils) {
utils.dispose();
utils = null;
}
currentLeaderboard = null;
}
@Override
public void update(float tpf) {
// You need to tell Steamworks to run the callbacks or you will get nothing
if(SteamAPI.isSteamRunning()){
currentTime += tpf;
if(currentTime >= TIME_BETWEEN_CALLBACKS ){
currentTime = 0;
SteamAPI.runCallbacks();
}
}
}
public void loadScores(String leaderboardName) {
userStats.findOrCreateLeaderboard(leaderboardName,
SteamUserStats.LeaderboardSortMethod.Descending, SteamUserStats.LeaderboardDisplayType.Numeric);
}
public void saveScores(String leaderboardName, int score) {
// This is when you want to upload the score before before getting the leaderboard
if(null != currentLeaderboard){
userStats.uploadLeaderboardScore(currentLeaderboard, SteamUserStats.LeaderboardUploadScoreMethod.KeepBest,
score, null);
}else{
saveFirst = true;
scoreToSave = score;
userStats.findOrCreateLeaderboard(leaderboardName,
SteamUserStats.LeaderboardSortMethod.Descending, SteamUserStats.LeaderboardDisplayType.Numeric);
}
}
private final SteamUserStatsCallback userStatsCallback = new SteamUserStatsCallback() {
@Override
public void onUserStatsReceived(long gameId, SteamID steamIDUser, SteamResult result) {}
@Override
public void onUserStatsStored(long gameId, SteamResult result) {}
@Override
public void onUserStatsUnloaded(SteamID steamIDUser) {}
@Override
public void onUserAchievementStored(long gameId, boolean isGroupAchievement,
String achievementName, int curProgress, int maxProgress) {}
@Override
public void onNumberOfCurrentPlayersReceived(boolean success, int players) {}
@Override
public void onGlobalStatsReceived(long gameId, SteamResult result) {}
@Override
public void onLeaderboardFindResult(SteamLeaderboardHandle leaderboard, boolean found) {
if(!found){
//<Code here when no leaderboard is found>
return;
}
currentLeaderboard = leaderboard;
if(saveFirst){
saveFirst = false;
userStats.uploadLeaderboardScore(currentLeaderboard, SteamUserStats.LeaderboardUploadScoreMethod.KeepBest,
scoreToSave, new int[]{});
scoreToSave = -1;
}else{
performDownloadEntries(leaderboard);
}
}
@Override
public void onLeaderboardScoresDownloaded(SteamLeaderboardHandle leaderboard, SteamLeaderboardEntriesHandle entries, int numEntries) {
if(numEntries < 0){
//<Code here when no leaderboard entries are found>
return;
}
int[] details = new int[16];
for (int i = 0; i < numEntries; i++){
SteamLeaderboardEntry entry = new SteamLeaderboardEntry();
if(userStats.getDownloadedLeaderboardEntry(entries, i, entry, details)){
// Parse the scores here
}
}
}
@Override
public void onLeaderboardScoreUploaded(boolean success, SteamLeaderboardHandle leaderboard, int score,
boolean scoreChanged, int globalRankNew, int globalRankPrevious) {
// You can check for success or not if you want
performDownloadEntries(leaderboard);
}
private void performDownloadEntries(SteamLeaderboardHandle leaderboard){
SteamUserStats.LeaderboardDataRequest request = SteamUserStats.LeaderboardDataRequest.GlobalAroundUser;
int count = userStats.getLeaderboardEntryCount(leaderboard);
userStats.downloadLeaderboardEntries(leaderboard, request, 0, count);
}
};
private final SteamFriendsCallback friendsCallback = new SteamFriendsCallback() {
@Override
public void onSetPersonaNameResponse(boolean success, boolean localSuccess, SteamResult result) {}
@Override
public void onPersonaStateChange(SteamID steamID, SteamFriends.PersonaChange change) {}
@Override
public void onGameOverlayActivated(boolean active) {
// Pay Close attention...
// You MUST pause the game when the overlay appears ot your app will be rejected
}
@Override
public void onGameLobbyJoinRequested(SteamID steamIDLobby, SteamID steamIDFriend) {}
@Override
public void onAvatarImageLoaded(SteamID steamID, int image, int width, int height) {}
@Override
public void onFriendRichPresenceUpdate(SteamID steamIDFriend, int appID) {}
@Override
public void onGameRichPresenceJoinRequested(SteamID steamIDFriend, String connect) {}
@Override
public void onGameServerChangeRequested(String server, String password) {}
};
private final SteamUtilsCallback utilsCallback = new SteamUtilsCallback() {
@Override
public void onSteamShutdown() {
// Check when steam is down
}
};
}
Thank you for that, that’s incredibly helpful!
It’s also a little more complex than I was expecting. Perhaps in my naivety I was thinking you’d just have a call to connect to your leader board then another simple call to write the players score to it. Maybe even a call to read the leader board for those scores nearest to the player.
Are all the imports necessary? ie blocks, arrays etc.
Not really… I cleaned up the code from my game project but I may have forgotten to remove some imports (the java.util ones). and my package name let me edit that.
As for the complexity. Yes it is a bit complex but not as hard as you think. Epic’s Online Services on the other hand is a pain. I am still wrapping my head around it
Sorry for reviving this thread. Just letting everyone know that a free demo version is up on Steam
Also, controls are customizable on keyboard and gamepads