Skip to content

Instantly share code, notes, and snippets.

@miahmie
Last active October 11, 2019 15:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save miahmie/eb855fe738e76eaea683507742c9f58e to your computer and use it in GitHub Desktop.
Save miahmie/eb855fe738e76eaea683507742c9f58e to your computer and use it in GitHub Desktop.

Steam.RestartAppIfNecessary(APPID) を追加してみた

krkrsteamの現状の実装ではプラグインをリンクした瞬間に SteamAPI_Init() が走る問題があるので, そのままだと SteamAPI_RestartAppIfNecessary を吉里吉里側に出せないという設計上の不備がありました。 (リンクした瞬間にチェック関数を呼ぶまでもなくエラーが出てしまうケースが発生する)

取り急ぎの対策として,遅延して SteamAPI_Init を呼ぶためのオブジェクトのラッパー( SteamDelayRegister )を入れてみました。 ただしこれは暫定的な対応であり,根本から実装を見直したほうが良いと @wtnbgo に具申しておきます…。

使い方

/* startup.tjs とか Initialize.tjs とか return 一発で実行から即抜けられる場所に配置する */
var APPID = 0; // SteamのAPPIDの変数(別に直接埋め込みでも問題はないです)
Plugins.link("krkrsteam.dll");
if (Steam.RestartAppIfNecessary(APPID)) return System.terminate(1); // Steamクライアント経由で起動させる

みたいなコードを最初の方に埋め込んでください。

解説

プラグインリンク直後は,グローバルの Steam オブジェクトはラッパー( SteamDelayRegister )への参照となっており, Steam.RestartAppIfNecessary 関数はそのままアクセスできます。それ以外の関数・プロパティにアクセスすると, SteamAPI_Init() が走り,グローバルのSteamオブジェクトが本来のものにさし変わって本来の Steam オブジェクトの処理が行われます。

そのため,それ以前にSteamオブジェクトごと変数に保持しておくようなスクリプトは推奨しません。(ラッパーが保存されてしまうため)

#include <windows.h>
#include "tp_stub.h"
#include "ncbind.hpp"
#include <string>
#include "SteamAchievements.h"
#include <steam_api.h>
#pragma comment(lib,"steam_api.lib")
//ライブラリ互換がロストしてるようなので仮対策・機能的に無効かどうかは不明
#ifndef BroadcastUploadStart_t
STEAM_CALLBACK_BEGIN(BroadcastUploadStart_t, k_iClientVideoCallbacks + 4)
STEAM_CALLBACK_END(0)
#endif
#ifndef BroadcastUploadStop_t
STEAM_CALLBACK_BEGIN(BroadcastUploadStop_t, k_iClientVideoCallbacks + 5)
STEAM_CALLBACK_MEMBER(0, EBroadcastUploadResult, m_eResult)
STEAM_CALLBACK_END(1)
#endif
// ttstrをUTF8文字列へ変換
std::string
convertTtstrToUtf8String(ttstr &buf)
{
int maxlen = buf.length() * 6 + 1;
char *dat = new char[maxlen];
int len = TVPWideCharToUtf8String(buf.c_str(), dat);
std::string result(dat, len);
delete[] dat;
return result;
}
// std::stringをttstrに変換
ttstr
convertUtf8StringToTtstr(const char *buf, size_t length=-1)
{
if (length < 0) {
length = strlen(buf);
}
tjs_uint maxlen = (tjs_uint)length * 2 + 1;
tjs_char *dat = new tjs_char[maxlen];
tjs_uint len = TVPUtf8ToWideCharString(buf, dat);
ttstr result(dat, len);
delete[] dat;
return result;
}
ttstr
convertUtf8StringToTtstr(const std::string &buf)
{
return convertUtf8StringToTtstr(buf.c_str(), buf.length());
}
extern void initStorage();
extern void doneStorage();
class SteamScreenshotCallback;
class SteamBroadcastCallback;
class LayerImageToRGB;
/**
* Steam情報基礎クラス
*/
class Steam : public tTVPContinuousEventCallbackIntf {
public:
// 初期化
static void registerSteam() {
if (!instance) { // added
instance = new Steam();
instance->init();
initStorage();
} //added
}
// 解除
static void unregisterSteam() {
if (instance) { //added
doneStorage();
delete instance;
} //added
}
//vvv[added]
// 遅延初期化対応
class SteamDelayRegister : public tTJSDispatch {
tTJSVariantClosure closure;
bool terminate;
bool PassThroughCheck(const tjs_char * membername, tjs_uint32 *hint) {
TVPAddLog(TVPFormatMessage(TJS_W("PassThroughCheck %1"), ttstr(membername)));
if (membername && ttstr(membername) == TJS_W("RestartAppIfNecessary")) return true; // ※メソッド名を合わせること
if (membername && !terminate) {
tTJSVariant v;
if (TJS_SUCCEEDED(closure.PropGet(0, membername, hint, &v, 0))) {
Steam::registerSteam(); // 遅延初期化実行
SetClosure(TJS_W("Steam"), closure);
this->Release();
terminate = true;
}
}
return false;
}
static bool GetClosure(const tjs_char * membername, tTJSVariantClosure &closure) {
bool r = false;
iTJSDispatch2 * global = TVPGetScriptDispatch();
if (global) {
tTJSVariant v;
if (TJS_SUCCEEDED(global->PropGet(0, membername, 0, &v, global))) {
closure = v.AsObjectClosure();
r = true;
}
global->Release();
}
return r;
}
static bool SetClosure(const tjs_char * membername, const tTJSVariantClosure &closure) {
bool r = false;
iTJSDispatch2 * global = TVPGetScriptDispatch();
if (global) {
tTJSVariant v(closure.Object, closure.ObjThis);
r = TJS_SUCCEEDED(global->PropSet(TJS_MEMBERENSURE|TJS_IGNOREPROP, membername, 0, &v, global));
global->Release();
}
return r;
}
public:
SteamDelayRegister() : closure(NULL,NULL), terminate(false) {
GetClosure(TJS_W("Steam"), closure);
}
~SteamDelayRegister() {
closure.Release();
}
void SetSelf() {
SetClosure(TJS_W("Steam"), tTJSVariantClosure(this, this));
}
// とりあえず FuncCall と PropGet だけ実装すれば十分と思われる
tjs_error TJS_INTF_METHOD FuncCall(tjs_uint32 flag, const tjs_char * membername, tjs_uint32 *hint, tTJSVariant *result, tjs_int numparams, tTJSVariant **param, iTJSDispatch2 *objthis) {
PassThroughCheck(membername, hint);
return closure.FuncCall(flag, membername, hint, result, numparams, param, objthis);
}
tjs_error TJS_INTF_METHOD PropGet(tjs_uint32 flag, const tjs_char * membername, tjs_uint32 *hint, tTJSVariant *result, iTJSDispatch2 *objthis) {
PassThroughCheck(membername, hint);
return closure.PropGet(flag, membername, hint, result, objthis);
}
};
static void setupSteam() {
SteamDelayRegister *delay = new SteamDelayRegister(); // Steam.*参照で自動破棄される
delay->SetSelf();
}
//^^^[added]
// Steamクライアント起動チェック
static bool RestartAppIfNecessary(tTVInteger appid) {
return SteamAPI_RestartAppIfNecessary(static_cast<uint32>(appid));
}
// コンストラクタ用(常に例外)
static tjs_error Factory(Steam **obj, tjs_int numparams, tTJSVariant **param, iTJSDispatch2 *objthis) {
TVPThrowExceptionMessage(TJSGetMessageMapMessage(TJS_W("TVPCannotCreateInstance")).c_str());
return TJS_E_FAIL;
}
// コンストラクタ
Steam() : inited(false), achieve(0), screenshot(0), broadcast(0) {}
// デストラクタ
virtual ~Steam();
// 初期化
void init() {
if ((inited = SteamAPI_Init())) {
ISteamUtils *utils = SteamUtils();
if (utils) {
utils->SetOverlayNotificationPosition(k_EPositionTopLeft);
}
achieve = new SteamAchievements();
TVPAddContinuousEventHook(this);
} else {
TVPThrowExceptionMessage(L"Steam must be running to play this game (SteamAPI_Init() failed)");
}
}
// ---------------------------------------------------------
// 実績
// ---------------------------------------------------------
static bool requestInitialize() {
if (instance && instance->achieve) {
return instance->achieve->requestInitialize();
}
return false;
}
static bool getInitialized() {
if (instance && instance->achieve) {
return instance->achieve->getInitialized();
}
return false;
}
static int getAchievementsCount() {
if (instance && instance->achieve) {
return instance->achieve->getAchievementsCount();
}
return 0;
}
static tTJSVariant getAchievement(tTJSVariant n) {
if (instance && instance->achieve) {
return instance->achieve->getAchievement(n);
}
return tTJSVariant();
}
static bool setAchievement(tTJSVariant n) {
if (instance && instance->achieve) {
return instance->achieve->setAchievement(n);
}
return false;
}
static bool clearAchievement(tTJSVariant n) {
if (instance && instance->achieve) {
return instance->achieve->clearAchievement(n);
}
return false;
}
// ---------------------------------------------------------
// 情報
// ---------------------------------------------------------
// 現在の言語を取得
static ttstr getLanguage() {
ttstr ret;
ISteamApps *app = SteamApps();
if (app) {
std::string lang = app->GetCurrentGameLanguage();
ret = convertUtf8StringToTtstr(lang);
}
return ret;
}
// ---------------------------------------------------------
// クラウド
// ---------------------------------------------------------
static bool getCloudEnabled() {
ISteamRemoteStorage *storage = SteamRemoteStorage();
if (storage) {
return storage->IsCloudEnabledForApp();
}
return false;
}
static void setCloudEnabled(bool enabled) {
ISteamRemoteStorage *storage = SteamRemoteStorage();
if (storage) {
storage->SetCloudEnabledForApp(enabled);
}
}
static tTJSVariant getCloudQuota() {
tTJSVariant ret;
ISteamRemoteStorage *storage = SteamRemoteStorage();
if (storage) {
uint64 total;
uint64 available;
if (storage->GetQuota(&total, &available)) {
iTJSDispatch2 *dict = TJSCreateDictionaryObject();
if (dict) {
ncbPropAccessor obj(dict);
obj.SetValue(L"total", static_cast<tTVInteger>(total));
obj.SetValue(L"available", static_cast<tTVInteger>(available));
ret = tTJSVariant(dict, dict);
dict->Release();
}
}
}
return ret;
}
static int getCloudFileCount() {
ISteamRemoteStorage *storage = SteamRemoteStorage();
if (storage) {
return storage->GetFileCount();
}
return 0;
}
static tTJSVariant getCloudFileInfo(int n) {
tTJSVariant ret;
ISteamRemoteStorage *storage = SteamRemoteStorage();
if (storage) {
int32 size;
std::string name = storage->GetFileNameAndSize(n, &size);
int64 time = storage->GetFileTimestamp(name.c_str());
iTJSDispatch2 *dict = TJSCreateDictionaryObject();
if (dict) {
ncbPropAccessor obj(dict);
obj.SetValue(L"filename", convertUtf8StringToTtstr(name));
obj.SetValue(L"size", size);
obj.SetValue(L"time", time);
ret = tTJSVariant(dict, dict);
dict->Release();
}
}
return ret;
}
static bool deleteCloudFile(ttstr name) {
bool ret = false;
ISteamRemoteStorage *storage = SteamRemoteStorage();
if (storage) {
ttstr n = name;
n.ToLowerCase();
std::string filename = convertTtstrToUtf8String(n);
ret = storage->FileDelete(filename.c_str());
}
return ret;
}
static bool copyCloudFile(ttstr src, ttstr dest) {
bool ret = false;
ISteamRemoteStorage *storage = SteamRemoteStorage();
if (storage) {
ttstr n = src;
n.ToLowerCase();
std::string srcfile = convertTtstrToUtf8String(n);
n = dest;
n.ToLowerCase();
std::string destfile = convertTtstrToUtf8String(n);
if (storage->FileExists(srcfile.c_str())) {
int size = storage->GetFileSize(srcfile.c_str());
unsigned char *buffer = new unsigned char[size];
if (buffer) {
if (storage->FileRead(srcfile.c_str(), buffer, size) == size) {
if (storage->FileWrite(destfile.c_str(), buffer, size)) {
ret = true;
}
}
delete[] buffer;
}
}
}
return ret;
}
// ---------------------------------------------------------
// スクリーンショット制御
// ---------------------------------------------------------
/**
* スクリーンショット処理をアプリ側から起動させる
*/
static void triggerScreenshot() {
ISteamScreenshots *shots = SteamScreenshots();
if (shots) {
shots->TriggerScreenshot();
}
}
/**
* @param callback をフックするコールバック関数(voidなら開放)
* @return 登録・解除に成功したらtrue
*/
static bool hookScreenshots(tTJSVariant callback) {
if (instance) {
return instance->_hookScreenshots(callback);
}
return false;
}
/**
* スクリーンショットの登録
* @param layer 画像
* @param location 場所文字列
*/
static void writeScreenshot(iTJSDispatch2 *layer, ttstr location) {
if (instance) {
instance->_writeScreenshot(layer, location);
}
}
// ---------------------------------------------------------
// ブロードキャスト制御
// ---------------------------------------------------------
static bool isBroadcasting() {
int numViewers = 0;
ISteamVideo *video = SteamVideo();
return video && video->IsBroadcasting(&numViewers);
}
static bool hookBroadcasting(tTJSVariant callback) {
if (instance) {
return instance->_hookBroadcasting(callback);
}
return false;
}
// ---------------------------------------------------------
// DLC情報
// ---------------------------------------------------------
// only use this member if you need to check ownership of another game related to yours, a demo for example
static bool isIsSubscribedApp(uint32 appID) {
bool ret = false;
ISteamApps *app = SteamApps();
if (app) {
ret = app->BIsSubscribedApp( (AppId_t)appID );
}
return ret;
}
// Takes AppID of DLC and checks if the user owns the DLC & if the DLC is installed
static bool isDlcInstalled(uint32 appID) {
bool ret = false;
ISteamApps *app = SteamApps();
if (app) {
ret = app->BIsDlcInstalled( (AppId_t)appID );
}
return ret;
}
/**
* @return DLCの個数を返す
*/
static int getDLCCount() {
int ret = 0;
ISteamApps *app = SteamApps();
if (app) {
ret = app->GetDLCCount();
}
return ret;
}
/**
* @param no DLCの番号
* @rerurn DLC情報
appID
aAvailable
chName
*
*/
static tTJSVariant getDLCData(int no) {
tTJSVariant ret;
ISteamApps *app = SteamApps();
if (app) {
AppId_t appId;
bool available;
char chName[4096];
if (app->BGetDLCDataByIndex(no, &appId, &available, chName, sizeof chName-1)) {
ttstr name = convertUtf8StringToTtstr(chName);
iTJSDispatch2 *dict = TJSCreateDictionaryObject();
if (dict) {
ncbPropAccessor obj(dict);
obj.SetValue(L"appId", appId);
obj.SetValue(L"available", available);
obj.SetValue(L"chName", name);
ret = tTJSVariant(dict, dict);
dict->Release();
}
}
}
return ret;
}
public:
virtual void TJS_INTF_METHOD OnContinuousCallback(tjs_uint64 tick) {
SteamAPI_RunCallbacks();
}
protected:
static Steam *instance;
bool _hookScreenshots(tTJSVariant callback);
bool _hookBroadcasting(tTJSVariant callback);
void _writeScreenshot(iTJSDispatch2 *layer, ttstr location);
bool inited;
SteamAchievements *achieve;
SteamScreenshotCallback *screenshot;
SteamBroadcastCallback *broadcast;
};
/**
* SteamCallbackクラス
*/
class SteamCallback : public tTJSVariantClosure {
public:
SteamCallback() : tTJSVariantClosure(NULL, NULL) {}
virtual ~SteamCallback() { Release(); }
bool setCallback(tTJSVariant const &var) {
if (var.Type() == tvtObject) {
tTJSVariantClosure closure(var.AsObjectClosureNoAddRef());
Object = closure.Object;
ObjThis = closure.ObjThis;
AddRef();
} else {
Object = NULL;
ObjThis = NULL;
}
return Object != NULL;
}
bool invokeCallback(tjs_uint numparams , tTJSVariant * * params) const {
tTJSVariant result;
return Object && Try_iTJSDispatch2_FuncCall(Object, 0, NULL, NULL, &result, numparams, params, ObjThis) == TJS_S_OK;
}
private:
tTJSVariantClosure callback;
};
/**
* SteamScreenshotCallbackクラス
*/
class SteamScreenshotCallback : public SteamCallback {
public:
SteamScreenshotCallback() : SteamCallback()
,m_CallbackScreenshotRequested(this, &SteamScreenshotCallback::OnScreenShotRequested)
// ,m_CallbackScreenshotReady(this, &SteamScreenshotCallback::OnScreenShotReady)
{
}
STEAM_CALLBACK(SteamScreenshotCallback, OnScreenShotRequested, ScreenshotRequested_t, m_CallbackScreenshotRequested);
// STEAM_CALLBACK(SteamScreenshotCallback, OnScreenShotReady, ScreenshotReady_t, m_CallbackScreenshotReady);
};
void
SteamScreenshotCallback::OnScreenShotRequested(ScreenshotRequested_t *pCallback)
{
// 恐らくSteamAPI_RunCallbacks() のタイミングで呼ばれると思われる
invokeCallback(0, NULL);
// FuncCall でなくて TVPPostEvent なんたらのがよかったかも
//::TVPPostEvent(source, target, TJS_W("onSteamScreenShotRequested"), 0, TVP_EPT_EXCLUSIVE, numargs, args);
}
/*
// スクリーンショットを撮った後に呼ばれる?(タグ付けやlocationを設定する場合など)
void
SteamScreenshotCallback::OnScreenShotReady(ScreenshotReady_t *pCallback)
{
ISteamScreenshots *shots = SteamScreenshots();
if (shots) {
ScreenshotHandle hScreenshot = pCallback->m_hLocal;
//shorts->SetLocation(hScreenshot, pchLocation);
//shorts->TagPublishedFile(hScreenshot, unPublishedFileID);
}
}
*/
/**
* SteamBroadcastCallbackクラス
*/
class SteamBroadcastCallback : public SteamCallback {
public:
SteamBroadcastCallback() : SteamCallback()
,m_CallbackBroadcastStart(this, &SteamBroadcastCallback::OnBroadcastStart)
,m_CallbackBroadcastStop(this, &SteamBroadcastCallback::OnBroadcastStop)
{
}
STEAM_CALLBACK(SteamBroadcastCallback, OnBroadcastStart, BroadcastUploadStart_t, m_CallbackBroadcastStart);
STEAM_CALLBACK(SteamBroadcastCallback, OnBroadcastStop, BroadcastUploadStop_t, m_CallbackBroadcastStop);
};
void
SteamBroadcastCallback::OnBroadcastStart(BroadcastUploadStart_t *pCallback)
{
tTJSVariant state(1), *args = &state;
invokeCallback(1, &args);
}
void
SteamBroadcastCallback::OnBroadcastStop(BroadcastUploadStop_t *pCallback)
{
tTJSVariant state(0), *args = &state;
invokeCallback(1, &args);
}
class LayerImageToRGB {
public:
/**/ LayerImageToRGB() : buffer(0), size(0), width(0), height(0) {}
/**/~LayerImageToRGB() { term(); }
void init(iTJSDispatch2 *lay) {
term();
if (!lay) return;
const BYTE *ptr;
long pitch;
if (!GetLayerSize(lay, width, height) ||
!GetLayerImage(lay, ptr, pitch)) return;
size = width * height * 3;
buffer = new BYTE[size];
BYTE *w = buffer;
for (size_t y = 0; y < height; y++) {
const BYTE* r = ptr + pitch * y;
for (size_t x = 0; x < width; x++, r+=4) {
*w++ = r[2];
*w++ = r[1];
*w++ = r[0];
}
}
}
void term() {
if (buffer) delete[] buffer;
buffer = 0;
size = width = height = 0;
}
inline void *getRGB() const { return buffer; }
inline uint32 getSize() const { return size; }
inline int getWidth() const { return width; }
inline int getHeight() const { return height; }
private:
BYTE *buffer;
size_t size, width, height;
static iTJSDispatch2 *LayerClass;
static bool GetLayerSize(iTJSDispatch2 *lay, size_t &w, size_t &h) {
static ttstr hasImage (TJS_W("hasImage"));
static ttstr imageWidth (TJS_W("imageWidth"));
static ttstr imageHeight(TJS_W("imageHeight"));
tTVInteger lw, lh;
if (!LayerPropGet(lay, hasImage) ||
(lw = LayerPropGet(lay, imageWidth )) <= 0 ||
(lh = LayerPropGet(lay, imageHeight)) <= 0) return false;
w = (size_t)lw;
h = (size_t)lh;
return true;
}
static bool GetLayerImage(iTJSDispatch2 *lay, const BYTE* &ptr, long &pitch) {
static ttstr mainImageBufferPitch(TJS_W("mainImageBufferPitch"));
static ttstr mainImageBuffer(TJS_W("mainImageBuffer"));
tTVInteger lpitch, lptr;
if ((lpitch = LayerPropGet(lay, mainImageBufferPitch)) == 0 ||
(lptr = LayerPropGet(lay, mainImageBuffer)) == 0) return false;
pitch = (long)lpitch;
ptr = reinterpret_cast<const BYTE*>(lptr);
return true;
}
static tTVInteger LayerPropGet(iTJSDispatch2 *lay, ttstr &prop, tTVInteger defval = 0) {
if (!LayerClass) {
tTJSVariant var;
TVPExecuteExpression(TJS_W("Layer"), &var);
LayerClass = var.AsObjectNoAddRef();
}
tTJSVariant val;
return (TJS_FAILED(LayerClass->PropGet(0, prop.c_str(), prop.GetHint(), &val, lay))) ? defval : val.AsInteger();
}
};
iTJSDispatch2 *LayerImageToRGB::LayerClass = 0;
// ---------------------------------------------------------
// 実装
Steam::~Steam() {
if (achieve) {
delete achieve;
achieve = 0;
}
if (screenshot) {
delete screenshot;
screenshot = 0;
}
if (broadcast) {
delete broadcast;
broadcast = 0;
}
if (inited){
TVPRemoveContinuousEventHook(this);
SteamAPI_Shutdown();
}
}
bool Steam::_hookScreenshots(tTJSVariant callback) {
ISteamScreenshots *shots = SteamScreenshots();
if (shots) {
if(!screenshot) {
screenshot = new SteamScreenshotCallback();
}
if (screenshot) {
bool bHook = screenshot->setCallback(callback);
shots->HookScreenshots(bHook);
return true;
}
}
return false;
}
bool Steam::_hookBroadcasting(tTJSVariant callback) {
if(!broadcast) {
broadcast = new SteamBroadcastCallback();
}
if (broadcast) {
broadcast->setCallback(callback);
return true;
}
return false;
}
void Steam::_writeScreenshot(iTJSDispatch2 *layer, ttstr location) {
if (!layer) return;
ISteamScreenshots *shots = SteamScreenshots();
if (shots) {
LayerImageToRGB *image = new LayerImageToRGB();
try {
image->init(layer);
ScreenshotHandle handle = shots->WriteScreenshot(image->getRGB(), image->getSize(), image->getWidth(), image->getHeight());
if (!location.IsEmpty()) {
std::string text = convertTtstrToUtf8String(location);
shots->SetLocation(handle, text.c_str());
}
} catch (...) {
delete image;
throw;
}
delete image;
}
}
Steam *Steam::instance = 0;
NCB_REGISTER_CLASS(Steam) {
Factory(&Class::Factory);
NCB_METHOD(getLanguage);
NCB_METHOD(RestartAppIfNecessary); // added
NCB_METHOD(requestInitialize);
NCB_PROPERTY_RO(initialized, getInitialized);
NCB_PROPERTY_RO(achievementsCount, getAchievementsCount);
NCB_METHOD(getAchievement);
NCB_METHOD(setAchievement);
NCB_METHOD(clearAchievement);
NCB_PROPERTY(cloudEnabled, getCloudEnabled, setCloudEnabled);
NCB_METHOD(getCloudQuota);
NCB_PROPERTY_RO(cloudFileCount, getCloudFileCount);
NCB_METHOD(getCloudFileInfo);
NCB_METHOD(deleteCloudFile);
NCB_METHOD(copyCloudFile);
NCB_METHOD(triggerScreenshot);
NCB_METHOD(hookScreenshots);
NCB_METHOD(writeScreenshot);
NCB_METHOD(isBroadcasting);
NCB_METHOD(hookBroadcasting);
NCB_METHOD(isIsSubscribedApp);
NCB_METHOD(isDlcInstalled);
NCB_METHOD(getDLCCount);
NCB_METHOD(getDLCData);
}
//NCB_REGISTER_CALLBACK(PreRegist, Steam::registerSteam, 0, registerSteam_0); // CUT
NCB_REGISTER_CALLBACK(PostRegist, Steam::setupSteam, 0, 0_setupSteam); //added
NCB_REGISTER_CALLBACK(PostRegist, 0, Steam::unregisterSteam, 0_unregisterSteam);
#include "SteamAchievements.h"
#include "ncbind.hpp"
#include <tchar.h>
extern std::string convertTtstrToUtf8String(ttstr &buf);
extern ttstr convertUtf8StringToTtstr(const std::string &buf);
SteamAchievements::SteamAchievements() :
m_iAppID( 0),
m_bInitialized( false),
m_CallbackUserStatsReceived(this, &SteamAchievements::OnUserStatsReceived),
m_CallbackUserStatsStored(this, &SteamAchievements::OnUserStatsStored),
m_CallbackAchievementStored(this, &SteamAchievements::OnAchievementStored)
{
m_iAppID = SteamUtils()->GetAppID();
requestInitialize();
}
SteamAchievements::~SteamAchievements()
{
}
bool
SteamAchievements::requestInitialize()
{
// Is Steam loaded? If not we can't get stats.
if (NULL == SteamUserStats() || NULL == SteamUser()) {
return false;
}
// Is the user logged on? If not we can't get stats.
if (!SteamUser()->BLoggedOn()) {
return false;
}
// Request user stats.
return SteamUserStats()->RequestCurrentStats();
}
int
SteamAchievements::getAchievementsCount()
{
return SteamUserStats()->GetNumAchievements();
}
tTJSVariant
SteamAchievements::getAchievement(tTJSVariant n)
{
tTJSVariant ret;
if (m_bInitialized) {
std::string name;
getName(n, name);
if (name != "") {
const char *ach = name.c_str();
bool achieved = false;
uint32 unlockTime;
if (SteamUserStats()->GetAchievementAndUnlockTime(ach, &achieved, &unlockTime)) {
std::string name = SteamUserStats()->GetAchievementDisplayAttribute(ach, "name");
std::string desc = SteamUserStats()->GetAchievementDisplayAttribute(ach, "desc");
iTJSDispatch2 *dict = TJSCreateDictionaryObject();
if (dict) {
ncbPropAccessor obj(dict);
obj.SetValue(L"ach", ach);
obj.SetValue(L"achieved", achieved);
obj.SetValue(L"unlockTime", unlockTime);
obj.SetValue(L"name", convertUtf8StringToTtstr(name));
obj.SetValue(L"desc", convertUtf8StringToTtstr(desc));
ret = tTJSVariant(dict, dict);
dict->Release();
}
}
}
}
return ret;
}
bool
SteamAchievements::setAchievement(tTJSVariant n)
{
// Have we received a call back from Steam yet?
if (m_bInitialized) {
std::string name;
getName(n, name);
if (name != "") {
SteamUserStats()->SetAchievement(name.c_str());
return SteamUserStats()->StoreStats();
}
}
// If not then we can't set achievements yet
return false;
}
bool
SteamAchievements::clearAchievement(tTJSVariant n)
{
// Have we received a call back from Steam yet?
if (m_bInitialized) {
std::string name;
getName(n, name);
if (name != "") {
return SteamUserStats()->ClearAchievement(name.c_str());
}
}
// If not then we can't set achievements yet
return false;
}
#undef OutputDebugString
static void OutputDebugString(const TCHAR *buffer) {
TVPAddImportantLog(buffer);
::OutputDebugStringW(buffer);
}
void
SteamAchievements::OnUserStatsReceived(UserStatsReceived_t *pCallback)
{
// we may get callbacks for other games' stats arriving, ignore them
if (m_iAppID == pCallback->m_nGameID && k_EResultOK == pCallback->m_eResult) {
OutputDebugString(_T("Received stats and achievements from Steam\n"));
m_bInitialized = true;
} else {
TCHAR buffer[128];
_sntprintf(buffer, 128, _T("RequestStats - failed, %d\n"), pCallback->m_eResult);
OutputDebugString(buffer);
}
}
void
SteamAchievements::OnUserStatsStored(UserStatsStored_t *pCallback)
{
// we may get callbacks for other games' stats arriving, ignore them
if (m_iAppID == pCallback->m_nGameID && k_EResultOK == pCallback->m_eResult) {
OutputDebugString(_T("Stored stats for Steam\n"));
} else {
TCHAR buffer[128];
_sntprintf(buffer, 128, _T("StatsStored - failed, %d\n"), pCallback->m_eResult);
OutputDebugString(buffer);
}
}
void
SteamAchievements::OnAchievementStored(UserAchievementStored_t *pCallback)
{
// we may get callbacks for other games' stats arriving, ignore them
if (m_iAppID == pCallback->m_nGameID) {
OutputDebugString(_T("Stored Achievement for Steam\n"));
}
}
void
SteamAchievements::getName(tTJSVariant n, std::string &name)
{
if (n.Type() == tvtInteger) {
tjs_int num = n;
name = SteamUserStats()->GetAchievementName(num);
} else if (n.Type() == tvtString) {
ttstr str = n.GetString();
name = convertTtstrToUtf8String(str);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment