mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2025-01-13 07:37:25 +00:00
newgrounds io shit incoming
This commit is contained in:
parent
8dfc1c2bbd
commit
bc08fb2c3d
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
|||
export/
|
||||
.vscode/
|
||||
.vscode/
|
||||
APIStuff.hx
|
BIN
art/thumbnailNewer.png
Normal file
BIN
art/thumbnailNewer.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 132 KiB |
140
source/NGio.hx
Normal file
140
source/NGio.hx
Normal file
|
@ -0,0 +1,140 @@
|
|||
package;
|
||||
|
||||
import flixel.FlxG;
|
||||
import flixel.util.FlxSignal;
|
||||
import io.newgrounds.NG;
|
||||
import io.newgrounds.components.ScoreBoardComponent.Period;
|
||||
import io.newgrounds.objects.Medal;
|
||||
import io.newgrounds.objects.Score;
|
||||
import io.newgrounds.objects.ScoreBoard;
|
||||
import openfl.display.Stage;
|
||||
|
||||
/**
|
||||
* MADE BY GEOKURELI THE LEGENED GOD HERO MVP
|
||||
*/
|
||||
class NGio
|
||||
{
|
||||
public static var isLoggedIn:Bool = false;
|
||||
public static var scoreboardsLoaded:Bool = false;
|
||||
|
||||
public static var scoreboardArray:Array<Score> = [];
|
||||
|
||||
public static var ngDataLoaded(default, null):FlxSignal = new FlxSignal();
|
||||
public static var ngScoresLoaded(default, null):FlxSignal = new FlxSignal();
|
||||
|
||||
public function new(api:String, encKey:String, ?sessionId:String)
|
||||
{
|
||||
trace("connecting to newgrounds");
|
||||
|
||||
NG.createAndCheckSession(api, sessionId);
|
||||
|
||||
NG.core.verbose = true;
|
||||
// Set the encryption cipher/format to RC4/Base64. AES128 and Hex are not implemented yet
|
||||
NG.core.initEncryption(encKey); // Found in you NG project view
|
||||
|
||||
trace(NG.core.attemptingLogin);
|
||||
|
||||
if (NG.core.attemptingLogin)
|
||||
{
|
||||
/* a session_id was found in the loadervars, this means the user is playing on newgrounds.com
|
||||
* and we should login shortly. lets wait for that to happen
|
||||
*/
|
||||
trace("attempting login");
|
||||
NG.core.onLogin.add(onNGLogin);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* They are NOT playing on newgrounds.com, no session id was found. We must start one manually, if we want to.
|
||||
* Note: This will cause a new browser window to pop up where they can log in to newgrounds
|
||||
*/
|
||||
NG.core.requestLogin(onNGLogin);
|
||||
}
|
||||
}
|
||||
|
||||
function onNGLogin():Void
|
||||
{
|
||||
trace('logged in! user:${NG.core.user.name}');
|
||||
isLoggedIn = true;
|
||||
FlxG.save.data.sessionId = NG.core.sessionId;
|
||||
// FlxG.save.flush();
|
||||
// Load medals then call onNGMedalFetch()
|
||||
NG.core.requestMedals(onNGMedalFetch);
|
||||
|
||||
// Load Scoreboards hten call onNGBoardsFetch()
|
||||
NG.core.requestScoreBoards(onNGBoardsFetch);
|
||||
|
||||
ngDataLoaded.dispatch();
|
||||
}
|
||||
|
||||
// --- MEDALS
|
||||
function onNGMedalFetch():Void
|
||||
{
|
||||
/*
|
||||
// Reading medal info
|
||||
for (id in NG.core.medals.keys())
|
||||
{
|
||||
var medal = NG.core.medals.get(id);
|
||||
trace('loaded medal id:$id, name:${medal.name}, description:${medal.description}');
|
||||
}
|
||||
|
||||
// Unlocking medals
|
||||
var unlockingMedal = NG.core.medals.get(54352);// medal ids are listed in your NG project viewer
|
||||
if (!unlockingMedal.unlocked)
|
||||
unlockingMedal.sendUnlock();
|
||||
*/
|
||||
}
|
||||
|
||||
// --- SCOREBOARDS
|
||||
function onNGBoardsFetch():Void
|
||||
{
|
||||
/*
|
||||
// Reading medal info
|
||||
for (id in NG.core.scoreBoards.keys())
|
||||
{
|
||||
var board = NG.core.scoreBoards.get(id);
|
||||
trace('loaded scoreboard id:$id, name:${board.name}');
|
||||
}
|
||||
*/
|
||||
// var board = NG.core.scoreBoards.get(8004);// ID found in NG project view
|
||||
|
||||
// Posting a score thats OVER 9000!
|
||||
// board.postScore(FlxG.random.int(0, 1000));
|
||||
|
||||
// --- To view the scores you first need to select the range of scores you want to see ---
|
||||
|
||||
// add an update listener so we know when we get the new scores
|
||||
// board.onUpdate.add(onNGScoresFetch);
|
||||
trace("shoulda got score by NOW!");
|
||||
// board.requestScores(20);// get the best 10 scores ever logged
|
||||
// more info on scores --- http://www.newgrounds.io/help/components/#scoreboard-getscores
|
||||
}
|
||||
|
||||
function onNGScoresFetch():Void
|
||||
{
|
||||
scoreboardsLoaded = true;
|
||||
|
||||
ngScoresLoaded.dispatch();
|
||||
/*
|
||||
for (score in NG.core.scoreBoards.get(8737).scores)
|
||||
{
|
||||
trace('score loaded user:${score.user.name}, score:${score.formatted_value}');
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
// var board = NG.core.scoreBoards.get(8004);// ID found in NG project view
|
||||
// board.postScore(HighScore.score);
|
||||
|
||||
// NGio.scoreboardArray = NG.core.scoreBoards.get(8004).scores;
|
||||
}
|
||||
|
||||
inline static public function unlockMedal(id:Int)
|
||||
{
|
||||
if (isLoggedIn)
|
||||
{
|
||||
var medal = NG.core.medals.get(id);
|
||||
if (!medal.unlocked)
|
||||
medal.sendUnlock();
|
||||
}
|
||||
}
|
||||
}
|
227
source/io/newgrounds/Call.hx
Normal file
227
source/io/newgrounds/Call.hx
Normal file
|
@ -0,0 +1,227 @@
|
|||
package io.newgrounds;
|
||||
|
||||
import io.newgrounds.utils.Dispatcher;
|
||||
import io.newgrounds.utils.AsyncHttp;
|
||||
import io.newgrounds.objects.Error;
|
||||
import io.newgrounds.objects.events.Result;
|
||||
import io.newgrounds.objects.events.Result.ResultBase;
|
||||
import io.newgrounds.objects.events.Response;
|
||||
|
||||
import haxe.ds.StringMap;
|
||||
import haxe.Json;
|
||||
|
||||
/** A generic way to handle calls agnostic to their type */
|
||||
interface ICallable {
|
||||
|
||||
public var component(default, null):String;
|
||||
|
||||
public function send():Void;
|
||||
public function queue():Void;
|
||||
public function destroy():Void;
|
||||
}
|
||||
|
||||
class Call<T:ResultBase>
|
||||
implements ICallable {
|
||||
|
||||
public var component(default, null):String;
|
||||
|
||||
var _core:NGLite;
|
||||
var _properties:StringMap<Dynamic>;
|
||||
var _parameters:StringMap<Dynamic>;
|
||||
var _requireSession:Bool;
|
||||
var _isSecure:Bool;
|
||||
|
||||
// --- BASICALLY SIGNALS
|
||||
var _dataHandlers:TypedDispatcher<Response<T>>;
|
||||
var _successHandlers:Dispatcher;
|
||||
var _httpErrorHandlers:TypedDispatcher<Error>;
|
||||
var _statusHandlers:TypedDispatcher<Int>;
|
||||
|
||||
public function new (core:NGLite, component:String, requireSession:Bool = false, isSecure:Bool = false) {
|
||||
|
||||
_core = core;
|
||||
this.component = component;
|
||||
_requireSession = requireSession;
|
||||
_isSecure = isSecure && core.encryptionHandler != null;
|
||||
}
|
||||
|
||||
/** adds a property to the input's object. **/
|
||||
public function addProperty(name:String, value:Dynamic):Call<T> {
|
||||
|
||||
if (_properties == null)
|
||||
_properties = new StringMap<Dynamic>();
|
||||
|
||||
_properties.set(name, value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/** adds a parameter to the call's component object. **/
|
||||
public function addComponentParameter(name:String, value:Dynamic, defaultValue:Dynamic = null):Call<T> {
|
||||
|
||||
if (value == defaultValue)//TODO?: allow sending null value
|
||||
return this;
|
||||
|
||||
if (_parameters == null)
|
||||
_parameters = new StringMap<Dynamic>();
|
||||
|
||||
_parameters.set(name, value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Handy callback setter for chained call modifiers. Called when ng.io replies successfully */
|
||||
public function addDataHandler(handler:Response<T>->Void):Call<T> {
|
||||
|
||||
if (_dataHandlers == null)
|
||||
_dataHandlers = new TypedDispatcher<Response<T>>();
|
||||
|
||||
_dataHandlers.add(handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Handy callback setter for chained call modifiers. Called when ng.io replies successfully */
|
||||
public function addSuccessHandler(handler:Void->Void):Call<T> {
|
||||
|
||||
if (_successHandlers == null)
|
||||
_successHandlers = new Dispatcher();
|
||||
|
||||
_successHandlers.add(handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Handy callback setter for chained call modifiers. Called when ng.io does not reply for any reason */
|
||||
public function addErrorHandler(handler:Error->Void):Call<T> {
|
||||
|
||||
if (_httpErrorHandlers == null)
|
||||
_httpErrorHandlers = new TypedDispatcher<Error>();
|
||||
|
||||
_httpErrorHandlers.add(handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Handy callback setter for chained call modifiers. No idea when this is called; */
|
||||
public function addStatusHandler(handler:Int->Void):Call<T> {//TODO:learn what this is for
|
||||
|
||||
if (_statusHandlers == null)
|
||||
_statusHandlers = new TypedDispatcher<Int>();
|
||||
|
||||
_statusHandlers.add(handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the call to the server, do not modify this object after calling this
|
||||
* @param secure If encryption is enabled, it will encrypt the call.
|
||||
**/
|
||||
public function send():Void {
|
||||
|
||||
var data:Dynamic = {};
|
||||
data.app_id = _core.appId;
|
||||
data.call = {};
|
||||
data.call.component = component;
|
||||
|
||||
if (_core.debug)
|
||||
addProperty("debug", true);
|
||||
|
||||
if (_properties == null || !_properties.exists("session_id")) {
|
||||
// --- HAS NO SESSION ID
|
||||
|
||||
if (_core.sessionId != null) {
|
||||
// --- AUTO ADD SESSION ID
|
||||
|
||||
addProperty("session_id", _core.sessionId);
|
||||
|
||||
} else if (_requireSession){
|
||||
|
||||
_core.logError(new Error('cannot send "$component" call without a sessionId'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (_properties != null) {
|
||||
|
||||
for (field in _properties.keys())
|
||||
Reflect.setField(data, field, _properties.get(field));
|
||||
}
|
||||
|
||||
if (_parameters != null) {
|
||||
|
||||
data.call.parameters = {};
|
||||
|
||||
for (field in _parameters.keys())
|
||||
Reflect.setField(data.call.parameters, field, _parameters.get(field));
|
||||
}
|
||||
|
||||
_core.logVerbose('Post - ${Json.stringify(data)}');
|
||||
|
||||
if (_isSecure) {
|
||||
|
||||
var secureData = _core.encryptionHandler(Json.stringify(data.call));
|
||||
data.call = {};
|
||||
data.call.secure = secureData;
|
||||
|
||||
_core.logVerbose(' secure - $secureData');
|
||||
}
|
||||
|
||||
_core.markCallPending(this);
|
||||
|
||||
AsyncHttp.send(_core, Json.stringify(data), onData, onHttpError, onStatus);
|
||||
}
|
||||
|
||||
/** Adds the call to the queue */
|
||||
public function queue():Void {
|
||||
|
||||
_core.queueCall(this);
|
||||
}
|
||||
|
||||
function onData(reply:String):Void {
|
||||
|
||||
_core.logVerbose('Reply - $reply');
|
||||
|
||||
if (_dataHandlers == null && _successHandlers == null)
|
||||
return;
|
||||
|
||||
var response = new Response<T>(_core, reply);
|
||||
|
||||
if (_dataHandlers != null)
|
||||
_dataHandlers.dispatch(response);
|
||||
|
||||
if (response.success && response.result.success && _successHandlers != null)
|
||||
_successHandlers.dispatch();
|
||||
|
||||
destroy();
|
||||
}
|
||||
|
||||
function onHttpError(message:String):Void {
|
||||
|
||||
_core.logError(message);
|
||||
|
||||
if (_httpErrorHandlers == null)
|
||||
return;
|
||||
|
||||
var error = new Error(message);
|
||||
_httpErrorHandlers.dispatch(error);
|
||||
}
|
||||
|
||||
function onStatus(status:Int):Void {
|
||||
|
||||
if (_statusHandlers == null)
|
||||
return;
|
||||
|
||||
_statusHandlers.dispatch(status);
|
||||
}
|
||||
|
||||
public function destroy():Void {
|
||||
|
||||
_core = null;
|
||||
|
||||
_properties = null;
|
||||
_parameters = null;
|
||||
|
||||
_dataHandlers = null;
|
||||
_successHandlers = null;
|
||||
_httpErrorHandlers = null;
|
||||
_statusHandlers = null;
|
||||
}
|
||||
}
|
475
source/io/newgrounds/NG.hx
Normal file
475
source/io/newgrounds/NG.hx
Normal file
|
@ -0,0 +1,475 @@
|
|||
package io.newgrounds;
|
||||
|
||||
#if ng_lite
|
||||
typedef NG = NGLite; //TODO: test and make lite UI
|
||||
#else
|
||||
import io.newgrounds.utils.Dispatcher;
|
||||
import io.newgrounds.objects.Error;
|
||||
import io.newgrounds.objects.events.Result.SessionResult;
|
||||
import io.newgrounds.objects.events.Result.MedalListResult;
|
||||
import io.newgrounds.objects.events.Result.ScoreBoardResult;
|
||||
import io.newgrounds.objects.events.Response;
|
||||
import io.newgrounds.objects.User;
|
||||
import io.newgrounds.objects.Medal;
|
||||
import io.newgrounds.objects.Session;
|
||||
import io.newgrounds.objects.ScoreBoard;
|
||||
|
||||
import haxe.ds.IntMap;
|
||||
import haxe.Timer;
|
||||
|
||||
/**
|
||||
* The Newgrounds API for Haxe.
|
||||
* Contains many things ripped from MSGhero
|
||||
* - https://github.com/MSGhero/NG.hx
|
||||
* @author GeoKureli
|
||||
*/
|
||||
class NG extends NGLite {
|
||||
|
||||
static public var core(default, null):NG;
|
||||
static public var onCoreReady(default, null):Dispatcher = new Dispatcher();
|
||||
|
||||
// --- DATA
|
||||
|
||||
/** The logged in user */
|
||||
public var user(get, never):User;
|
||||
function get_user():User {
|
||||
|
||||
if (_session == null)
|
||||
return null;
|
||||
|
||||
return _session.user;
|
||||
}
|
||||
public var passportUrl(get, never):String;
|
||||
function get_passportUrl():String {
|
||||
|
||||
if (_session == null || _session.status != SessionStatus.REQUEST_LOGIN)
|
||||
return null;
|
||||
|
||||
return _session.passportUrl;
|
||||
}
|
||||
public var medals(default, null):IntMap<Medal>;
|
||||
public var scoreBoards(default, null):IntMap<ScoreBoard>;
|
||||
|
||||
// --- EVENTS
|
||||
|
||||
public var onLogin(default, null):Dispatcher;
|
||||
public var onLogOut(default, null):Dispatcher;
|
||||
public var onMedalsLoaded(default, null):Dispatcher;
|
||||
public var onScoreBoardsLoaded(default, null):Dispatcher;
|
||||
|
||||
// --- MISC
|
||||
|
||||
public var loggedIn(default, null):Bool;
|
||||
public var attemptingLogin(default, null):Bool;
|
||||
|
||||
var _loginCancelled:Bool;
|
||||
var _passportCallback:Void->Void;
|
||||
|
||||
var _session:Session;
|
||||
|
||||
/**
|
||||
* Iniitializes the API, call before utilizing any other component
|
||||
* @param appId The unique ID of your app as found in the 'API Tools' tab of your Newgrounds.com project.
|
||||
* @param sessionId A unique session id used to identify the active user.
|
||||
**/
|
||||
public function new(appId = "test", sessionId:String = null, ?onSessionFail:Error->Void) {
|
||||
|
||||
_session = new Session(this);
|
||||
onLogin = new Dispatcher();
|
||||
onLogOut = new Dispatcher();
|
||||
onMedalsLoaded = new Dispatcher();
|
||||
onScoreBoardsLoaded = new Dispatcher();
|
||||
|
||||
attemptingLogin = sessionId != null;
|
||||
|
||||
super(appId, sessionId, onSessionFail);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates NG.core, the heart and soul of the API. This is not the only way to create an instance,
|
||||
* nor is NG a forced singleton, but it's the only way to set the static NG.core.
|
||||
**/
|
||||
static public function create(appId = "test", sessionId:String = null, ?onSessionFail:Error->Void):Void {
|
||||
|
||||
core = new NG(appId, sessionId, onSessionFail);
|
||||
|
||||
onCoreReady.dispatch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates NG.core, and tries to create a session. This is not the only way to create an instance,
|
||||
* nor is NG a forced singleton, but it's the only way to set the static NG.core.
|
||||
**/
|
||||
static public function createAndCheckSession
|
||||
( appId = "test"
|
||||
, backupSession:String = null
|
||||
, ?onSessionFail:Error->Void
|
||||
):Void {
|
||||
|
||||
var session = NGLite.getSessionId();
|
||||
if (session == null)
|
||||
session = backupSession;
|
||||
|
||||
create(appId, session, onSessionFail);
|
||||
|
||||
core.host = getHost();
|
||||
if (core.sessionId != null)
|
||||
core.attemptingLogin = true;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// APP
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
override function checkInitialSession(failHandler:Error->Void, response:Response<SessionResult>):Void {
|
||||
|
||||
onSessionReceive(response, null, null, failHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Begins the login process
|
||||
*
|
||||
* @param onSuccess Called when the login is a success
|
||||
* @param onPending Called when the passportUrl has been identified, call NG.core.openPassportLink
|
||||
* to open the link continue the process. Leave as null to open the url automatically
|
||||
* NOTE: Browser games must open links on click events or else it will be blocked by
|
||||
* the popup blocker.
|
||||
* @param onFail
|
||||
* @param onCancel Called when the user denies the passport connection.
|
||||
*/
|
||||
public function requestLogin
|
||||
( onSuccess:Void->Void = null
|
||||
, onPending:Void->Void = null
|
||||
, onFail :Error->Void = null
|
||||
, onCancel :Void->Void = null
|
||||
):Void {
|
||||
|
||||
if (attemptingLogin) {
|
||||
|
||||
logError("cannot request another login until the previous attempt is complete");
|
||||
return;
|
||||
}
|
||||
|
||||
if (loggedIn) {
|
||||
|
||||
logError("cannot log in, already logged in");
|
||||
return;
|
||||
}
|
||||
|
||||
attemptingLogin = true;
|
||||
_loginCancelled = false;
|
||||
_passportCallback = null;
|
||||
|
||||
var call = calls.app.startSession(true)
|
||||
.addDataHandler(onSessionReceive.bind(_, onSuccess, onPending, onFail, onCancel));
|
||||
|
||||
if (onFail != null)
|
||||
call.addErrorHandler(onFail);
|
||||
|
||||
call.send();
|
||||
}
|
||||
|
||||
function onSessionReceive
|
||||
( response :Response<SessionResult>
|
||||
, onSuccess:Void->Void = null
|
||||
, onPending:Void->Void = null
|
||||
, onFail :Error->Void = null
|
||||
, onCancel :Void->Void = null
|
||||
):Void {
|
||||
|
||||
if (!response.success || !response.result.success) {
|
||||
|
||||
sessionId = null;
|
||||
endLoginAndCall(null);
|
||||
|
||||
if (onFail != null)
|
||||
onFail(!response.success ? response.error : response.result.error);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_session.parse(response.result.data.session);
|
||||
sessionId = _session.id;
|
||||
|
||||
logVerbose('session started - status: ${_session.status}');
|
||||
|
||||
if (_session.status == SessionStatus.REQUEST_LOGIN) {
|
||||
|
||||
_passportCallback = checkSession.bind(null, onSuccess, onCancel);
|
||||
if (onPending != null)
|
||||
onPending();
|
||||
else
|
||||
openPassportUrl();
|
||||
|
||||
} else
|
||||
checkSession(null, onSuccess, onCancel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this once the passport link is established and it will load the passport URL and
|
||||
* start checking for session connect periodically
|
||||
*/
|
||||
public function openPassportUrl():Void {
|
||||
|
||||
if (passportUrl != null) {
|
||||
|
||||
logVerbose('loading passport: ${passportUrl}');
|
||||
openPassportHelper(passportUrl);
|
||||
onPassportUrlOpen();
|
||||
|
||||
} else
|
||||
logError("Cannot open passport");
|
||||
}
|
||||
|
||||
|
||||
static function openPassportHelper(url:String):Void {
|
||||
var window = "_blank";
|
||||
|
||||
#if flash
|
||||
flash.Lib.getURL(new flash.net.URLRequest(url), window);
|
||||
#elseif (js && html5)
|
||||
js.Browser.window.open(url, window);
|
||||
#elseif desktop
|
||||
|
||||
#if (sys && windows)
|
||||
Sys.command("start", ["", url]);
|
||||
#elseif mac
|
||||
Sys.command("/usr/bin/open", [url]);
|
||||
#elseif linux
|
||||
Sys.command("/usr/bin/xdg-open", [path, "&"]);
|
||||
#end
|
||||
|
||||
#elseif android
|
||||
JNI.createStaticMethod
|
||||
( "org/haxe/lime/GameActivity"
|
||||
, "openURL"
|
||||
, "(Ljava/lang/String;Ljava/lang/String;)V"
|
||||
) (url, window);
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this once the passport link is established and it will start checking for session connect periodically
|
||||
*/
|
||||
public function onPassportUrlOpen():Void {
|
||||
|
||||
if (_passportCallback != null)
|
||||
_passportCallback();
|
||||
|
||||
_passportCallback = null;
|
||||
}
|
||||
|
||||
function checkSession(response:Response<SessionResult>, onSucceess:Void->Void, onCancel:Void->Void):Void {
|
||||
|
||||
if (response != null) {
|
||||
|
||||
if (!response.success || !response.result.success) {
|
||||
|
||||
log("login cancelled via passport");
|
||||
|
||||
endLoginAndCall(onCancel);
|
||||
return;
|
||||
}
|
||||
|
||||
_session.parse(response.result.data.session);
|
||||
}
|
||||
|
||||
if (_session.status == SessionStatus.USER_LOADED) {
|
||||
|
||||
loggedIn = true;
|
||||
endLoginAndCall(onSucceess);
|
||||
onLogin.dispatch();
|
||||
|
||||
} else if (_session.status == SessionStatus.REQUEST_LOGIN){
|
||||
|
||||
var call = calls.app.checkSession()
|
||||
.addDataHandler(checkSession.bind(_, onSucceess, onCancel));
|
||||
|
||||
// Wait 3 seconds and try again
|
||||
timer(3.0,
|
||||
function():Void {
|
||||
|
||||
// Check if cancelLoginRequest was called
|
||||
if (!_loginCancelled)
|
||||
call.send();
|
||||
else {
|
||||
|
||||
log("login cancelled via cancelLoginRequest");
|
||||
endLoginAndCall(onCancel);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
} else
|
||||
// The user cancelled the passport
|
||||
endLoginAndCall(onCancel);
|
||||
}
|
||||
|
||||
public function cancelLoginRequest():Void {
|
||||
|
||||
if (attemptingLogin)
|
||||
_loginCancelled = true;
|
||||
}
|
||||
|
||||
function endLoginAndCall(callback:Void->Void):Void {
|
||||
|
||||
attemptingLogin = false;
|
||||
_loginCancelled = false;
|
||||
|
||||
if (callback != null)
|
||||
callback();
|
||||
}
|
||||
|
||||
public function logOut(onComplete:Void->Void = null):Void {
|
||||
|
||||
var call = calls.app.endSession()
|
||||
.addSuccessHandler(onLogOutSuccessful);
|
||||
|
||||
if (onComplete != null)
|
||||
call.addSuccessHandler(onComplete);
|
||||
|
||||
call.addSuccessHandler(onLogOut.dispatch)
|
||||
.send();
|
||||
}
|
||||
|
||||
function onLogOutSuccessful():Void {
|
||||
|
||||
_session.expire();
|
||||
sessionId = null;
|
||||
loggedIn = false;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// MEDALS
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
public function requestMedals(onSuccess:Void->Void = null, onFail:Error->Void = null):Void {
|
||||
|
||||
var call = calls.medal.getList()
|
||||
.addDataHandler(onMedalsReceived);
|
||||
|
||||
if (onSuccess != null)
|
||||
call.addSuccessHandler(onSuccess);
|
||||
|
||||
if (onFail != null)
|
||||
call.addErrorHandler(onFail);
|
||||
|
||||
call.send();
|
||||
}
|
||||
|
||||
function onMedalsReceived(response:Response<MedalListResult>):Void {
|
||||
|
||||
if (!response.success || !response.result.success)
|
||||
return;
|
||||
|
||||
var idList:Array<Int> = new Array<Int>();
|
||||
|
||||
if (medals == null) {
|
||||
|
||||
medals = new IntMap<Medal>();
|
||||
|
||||
for (medalData in response.result.data.medals) {
|
||||
|
||||
var medal = new Medal(this, medalData);
|
||||
medals.set(medal.id, medal);
|
||||
idList.push(medal.id);
|
||||
}
|
||||
} else {
|
||||
|
||||
for (medalData in response.result.data.medals) {
|
||||
|
||||
medals.get(medalData.id).parse(medalData);
|
||||
idList.push(medalData.id);
|
||||
}
|
||||
}
|
||||
|
||||
logVerbose('${response.result.data.medals.length} Medals received [${idList.join(", ")}]');
|
||||
|
||||
onMedalsLoaded.dispatch();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// SCOREBOARDS
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
public function requestScoreBoards(onSuccess:Void->Void = null, onFail:Error->Void = null):Void {
|
||||
|
||||
if (scoreBoards != null) {
|
||||
|
||||
log("aborting scoreboard request, all scoreboards are loaded");
|
||||
|
||||
if (onSuccess != null)
|
||||
onSuccess();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var call = calls.scoreBoard.getBoards()
|
||||
.addDataHandler(onBoardsReceived);
|
||||
|
||||
if (onSuccess != null)
|
||||
call.addSuccessHandler(onSuccess);
|
||||
|
||||
if (onFail != null)
|
||||
call.addErrorHandler(onFail);
|
||||
|
||||
call.send();
|
||||
}
|
||||
|
||||
function onBoardsReceived(response:Response<ScoreBoardResult>):Void {
|
||||
|
||||
if (!response.success || !response.result.success)
|
||||
return;
|
||||
|
||||
var idList:Array<Int> = new Array<Int>();
|
||||
|
||||
if (scoreBoards == null) {
|
||||
|
||||
scoreBoards = new IntMap<ScoreBoard>();
|
||||
|
||||
for (boardData in response.result.data.scoreboards) {
|
||||
|
||||
var board = new ScoreBoard(this, boardData);
|
||||
scoreBoards.set(board.id, board);
|
||||
idList.push(board.id);
|
||||
}
|
||||
}
|
||||
|
||||
logVerbose('${response.result.data.scoreboards.length} ScoreBoards received [${idList.join(", ")}]');
|
||||
|
||||
onScoreBoardsLoaded.dispatch();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// HELPERS
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
function timer(delay:Float, callback:Void->Void):Void {
|
||||
|
||||
var timer = new Timer(Std.int(delay * 1000));
|
||||
timer.run = function func():Void {
|
||||
|
||||
timer.stop();
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
static var urlParser:EReg = ~/^(?:http[s]?:\/\/)?([^:\/\s]+)(:[0-9]+)?((?:\/\w+)*\/)([\w\-\.]+[^#?\s]+)([^#\s]*)?(#[\w\-]+)?$/i;//TODO:trim
|
||||
/** Used to get the current web host of your game. */
|
||||
static public function getHost():String {
|
||||
|
||||
var url = NGLite.getUrl();
|
||||
|
||||
if (url == null || url == "")
|
||||
return "<AppView>";
|
||||
|
||||
if (url.indexOf("file") == 0)
|
||||
return "<LocalHost>";
|
||||
|
||||
if (urlParser.match(url))
|
||||
return urlParser.matched(1);
|
||||
|
||||
return "<Unknown>";
|
||||
}
|
||||
}
|
||||
#end
|
287
source/io/newgrounds/NGLite.hx
Normal file
287
source/io/newgrounds/NGLite.hx
Normal file
|
@ -0,0 +1,287 @@
|
|||
package io.newgrounds;
|
||||
|
||||
import haxe.crypto.Base64;
|
||||
import haxe.io.Bytes;
|
||||
import haxe.PosInfos;
|
||||
|
||||
import io.newgrounds.Call.ICallable;
|
||||
import io.newgrounds.components.ComponentList;
|
||||
import io.newgrounds.crypto.EncryptionFormat;
|
||||
import io.newgrounds.crypto.Cipher;
|
||||
import io.newgrounds.crypto.Rc4;
|
||||
import io.newgrounds.objects.Error;
|
||||
import io.newgrounds.objects.events.Response;
|
||||
import io.newgrounds.objects.events.Result.ResultBase;
|
||||
import io.newgrounds.objects.events.Result.SessionResult;
|
||||
import io.newgrounds.utils.Dispatcher;
|
||||
|
||||
#if !(html5 || flash || desktop || neko)
|
||||
#error "Target not supported, use: Flash, JS/HTML5, cpp or maybe neko";
|
||||
#end
|
||||
|
||||
/**
|
||||
* The barebones NG.io API. Allows API calls with code completion
|
||||
* and retrieves server data via strongly typed Objects
|
||||
*
|
||||
* Contains many things ripped from MSGhero's repo
|
||||
* - https://github.com/MSGhero/NG.hx
|
||||
*
|
||||
* @author GeoKureli
|
||||
*/
|
||||
class NGLite {
|
||||
|
||||
static public var core(default, null):NGLite;
|
||||
static public var onCoreReady(default, null):Dispatcher = new Dispatcher();
|
||||
|
||||
/** Enables verbose logging */
|
||||
public var verbose:Bool;
|
||||
public var debug:Bool;
|
||||
/** The unique ID of your app as found in the 'API Tools' tab of your Newgrounds.com project. */
|
||||
public var appId(default, null):String;
|
||||
/** The name of the host the game is being played on */
|
||||
public var host:String;
|
||||
|
||||
@:isVar
|
||||
public var sessionId(default, set):String;
|
||||
function set_sessionId(value:String):String {
|
||||
|
||||
return this.sessionId = value == "" ? null : value;
|
||||
}
|
||||
|
||||
/** Components used to call the NG server directly */
|
||||
public var calls(default, null):ComponentList;
|
||||
|
||||
/**
|
||||
* Converts an object to an encrypted string that can be decrypted by the server.
|
||||
* Set your preffered encrypter here,
|
||||
* or just call setDefaultEcryptionHandler with your app's encryption settings
|
||||
**/
|
||||
public var encryptionHandler:String->String;
|
||||
|
||||
/**
|
||||
* Iniitializes the API, call before utilizing any other component
|
||||
* @param appId The unique ID of your app as found in the 'API Tools' tab of your Newgrounds.com project.
|
||||
* @param sessionId A unique session id used to identify the active user.
|
||||
**/
|
||||
public function new(appId = "test", sessionId:String = null, ?onSessionFail:Error->Void) {
|
||||
|
||||
this.appId = appId;
|
||||
this.sessionId = sessionId;
|
||||
|
||||
calls = new ComponentList(this);
|
||||
|
||||
if (this.sessionId != null) {
|
||||
|
||||
calls.app.checkSession()
|
||||
.addDataHandler(checkInitialSession.bind(onSessionFail))
|
||||
.addErrorHandler(initialSessionFail.bind(onSessionFail))
|
||||
.send();
|
||||
}
|
||||
}
|
||||
|
||||
function checkInitialSession(onFail:Error->Void, response:Response<SessionResult>):Void {
|
||||
|
||||
if (!response.success || !response.result.success || response.result.data.session.expired) {
|
||||
|
||||
initialSessionFail(onFail, response.success ? response.result.error : response.error);
|
||||
}
|
||||
}
|
||||
|
||||
function initialSessionFail(onFail:Error->Void, error:Error):Void {
|
||||
|
||||
sessionId = null;
|
||||
|
||||
if (onFail != null)
|
||||
onFail(error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates NG.core, the heart and soul of the API. This is not the only way to create an instance,
|
||||
* nor is NG a forced singleton, but it's the only way to set the static NG.core.
|
||||
**/
|
||||
static public function create(appId = "test", sessionId:String = null, ?onSessionFail:Error->Void):Void {
|
||||
|
||||
core = new NGLite(appId, sessionId, onSessionFail);
|
||||
|
||||
onCoreReady.dispatch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates NG.core, and tries to create a session. This is not the only way to create an instance,
|
||||
* nor is NG a forced singleton, but it's the only way to set the static NG.core.
|
||||
**/
|
||||
static public function createAndCheckSession
|
||||
( appId = "test"
|
||||
, backupSession:String = null
|
||||
, ?onSessionFail:Error->Void
|
||||
):Void {
|
||||
|
||||
var session = getSessionId();
|
||||
if (session == null)
|
||||
session = backupSession;
|
||||
|
||||
create(appId, session, onSessionFail);
|
||||
}
|
||||
|
||||
inline static public function getUrl():String {
|
||||
|
||||
#if html5
|
||||
return js.Browser.document.location.href;
|
||||
#elseif flash
|
||||
return flash.Lib.current.stage.loaderInfo != null
|
||||
? flash.Lib.current.stage.loaderInfo.url
|
||||
: null;
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
|
||||
static public function getSessionId():String {
|
||||
|
||||
#if html5
|
||||
|
||||
var url = getUrl();
|
||||
|
||||
// Check for URL params
|
||||
var index = url.indexOf("?");
|
||||
if (index != -1) {
|
||||
|
||||
// Check for session ID in params
|
||||
for (param in url.substr(index + 1).split("&")) {
|
||||
|
||||
index = param.indexOf("=");
|
||||
if (index != -1 && param.substr(0, index) == "ngio_session_id")
|
||||
return param.substr(index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
#elseif flash
|
||||
|
||||
if (flash.Lib.current.stage.loaderInfo != null
|
||||
&& Reflect.hasField(flash.Lib.current.stage.loaderInfo.parameters, "ngio_session_id"))
|
||||
return Reflect.field(flash.Lib.current.stage.loaderInfo.parameters, "ngio_session_id");
|
||||
|
||||
#end
|
||||
|
||||
return null;
|
||||
|
||||
// --- EXAMPLE LOADER PARAMS
|
||||
//{ "1517703669" : ""
|
||||
//, "ng_username" : "GeoKureli"
|
||||
//, "NewgroundsAPI_SessionID" : "F1LusbG6P8Qf91w7zeUE37c1752563f366688ac6153996d12eeb111a2f60w2xn"
|
||||
//, "NewgroundsAPI_PublisherID" : 1
|
||||
//, "NewgroundsAPI_UserID" : 488329
|
||||
//, "NewgroundsAPI_SandboxID" : "5a76520e4ae1e"
|
||||
//, "ngio_session_id" : "0c6c4e02567a5116734ba1a0cd841dac28a42e79302290"
|
||||
//, "NewgroundsAPI_UserName" : "GeoKureli"
|
||||
//}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// CALLS
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
var _queuedCalls:Array<ICallable> = new Array<ICallable>();
|
||||
var _pendingCalls:Array<ICallable> = new Array<ICallable>();
|
||||
|
||||
@:allow(io.newgrounds.Call)
|
||||
@:generic
|
||||
function queueCall<T:ResultBase>(call:Call<T>):Void {
|
||||
|
||||
logVerbose('queued - ${call.component}');
|
||||
|
||||
_queuedCalls.push(call);
|
||||
checkQueue();
|
||||
}
|
||||
|
||||
@:allow(io.newgrounds.Call)
|
||||
@:generic
|
||||
function markCallPending<T:ResultBase>(call:Call<T>):Void {
|
||||
|
||||
_pendingCalls.push(call);
|
||||
|
||||
call.addDataHandler(function (_):Void { onCallComplete(call); });
|
||||
call.addErrorHandler(function (_):Void { onCallComplete(call); });
|
||||
}
|
||||
|
||||
function onCallComplete(call:ICallable):Void {
|
||||
|
||||
_pendingCalls.remove(call);
|
||||
checkQueue();
|
||||
}
|
||||
|
||||
function checkQueue():Void {
|
||||
|
||||
if (_pendingCalls.length == 0 && _queuedCalls.length > 0)
|
||||
_queuedCalls.shift().send();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// LOGGING / ERRORS
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
/** Called internally, set this to your preferred logging method */
|
||||
dynamic public function log(any:Dynamic, ?pos:PosInfos):Void {//TODO: limit access via @:allow
|
||||
|
||||
haxe.Log.trace('[Newgrounds API] :: ${any}', pos);
|
||||
}
|
||||
|
||||
/** used internally, logs if verbose is true */
|
||||
inline public function logVerbose(any:Dynamic, ?pos:PosInfos):Void {//TODO: limit access via @:allow
|
||||
|
||||
if (verbose)
|
||||
log(any, pos);
|
||||
}
|
||||
|
||||
/** Used internally. Logs by default, set this to your preferred error handling method */
|
||||
dynamic public function logError(any:Dynamic, ?pos:PosInfos):Void {//TODO: limit access via @:allow
|
||||
|
||||
log('Error: $any', pos);
|
||||
}
|
||||
|
||||
/** used internally, calls log error if the condition is false. EX: if (assert(data != null, "null data")) */
|
||||
inline public function assert(condition:Bool, msg:Dynamic, ?pos:PosInfos):Bool {//TODO: limit access via @:allow
|
||||
if (!condition)
|
||||
logError(msg, pos);
|
||||
|
||||
return condition;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// ENCRYPTION
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
/** Sets */
|
||||
public function initEncryption
|
||||
( key :String
|
||||
, cipher:Cipher = Cipher.RC4
|
||||
, format:EncryptionFormat = EncryptionFormat.BASE_64
|
||||
):Void {
|
||||
|
||||
if (cipher == Cipher.NONE)
|
||||
encryptionHandler = null;
|
||||
else if (cipher == Cipher.RC4)
|
||||
encryptionHandler = encryptRc4.bind(key, format);
|
||||
else
|
||||
throw "aes not yet implemented";
|
||||
}
|
||||
|
||||
function encryptRc4(key:String, format:EncryptionFormat, data:String):String {
|
||||
|
||||
if (format == EncryptionFormat.HEX)
|
||||
throw "hex format not yet implemented";
|
||||
|
||||
var keyBytes:Bytes;
|
||||
if (format == EncryptionFormat.BASE_64)
|
||||
keyBytes = Base64.decode(key);
|
||||
else
|
||||
keyBytes = null;//TODO
|
||||
|
||||
var dataBytes = new Rc4(keyBytes).crypt(Bytes.ofString(data));
|
||||
|
||||
if (format == EncryptionFormat.BASE_64)
|
||||
return Base64.encode(dataBytes);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
44
source/io/newgrounds/components/AppComponent.hx
Normal file
44
source/io/newgrounds/components/AppComponent.hx
Normal file
|
@ -0,0 +1,44 @@
|
|||
package io.newgrounds.components;
|
||||
|
||||
import io.newgrounds.objects.events.Result;
|
||||
import io.newgrounds.objects.events.Result.SessionResult;
|
||||
import io.newgrounds.NGLite;
|
||||
|
||||
class AppComponent extends Component {
|
||||
|
||||
public function new (core:NGLite) { super(core); }
|
||||
|
||||
public function startSession(force:Bool = false):Call<SessionResult> {
|
||||
|
||||
return new Call<SessionResult>(_core, "App.startSession")
|
||||
.addComponentParameter("force", force, false);
|
||||
}
|
||||
|
||||
public function checkSession():Call<SessionResult> {
|
||||
|
||||
return new Call<SessionResult>(_core, "App.checkSession", true);
|
||||
}
|
||||
|
||||
public function endSession():Call<SessionResult> {
|
||||
|
||||
return new Call<SessionResult>(_core, "App.endSession", true);
|
||||
}
|
||||
|
||||
public function getCurrentVersion(version:String):Call<GetCurrentVersionResult> {
|
||||
|
||||
return new Call<GetCurrentVersionResult>(_core, "App.getCurrentVersion")
|
||||
.addComponentParameter("version", version);
|
||||
}
|
||||
|
||||
public function getHostLicense():Call<GetHostResult> {
|
||||
|
||||
return new Call<GetHostResult>(_core, "App.getHostLicense")
|
||||
.addComponentParameter("host", _core.host);
|
||||
}
|
||||
|
||||
public function logView():Call<ResultBase> {
|
||||
|
||||
return new Call<ResultBase>(_core, "App.logView")
|
||||
.addComponentParameter("host", _core.host);
|
||||
}
|
||||
}
|
13
source/io/newgrounds/components/Component.hx
Normal file
13
source/io/newgrounds/components/Component.hx
Normal file
|
@ -0,0 +1,13 @@
|
|||
package io.newgrounds.components;
|
||||
|
||||
import io.newgrounds.NGLite;
|
||||
|
||||
class Component {
|
||||
|
||||
var _core:NGLite;
|
||||
|
||||
public function new(core:NGLite) {
|
||||
|
||||
this._core = core;
|
||||
}
|
||||
}
|
25
source/io/newgrounds/components/ComponentList.hx
Normal file
25
source/io/newgrounds/components/ComponentList.hx
Normal file
|
@ -0,0 +1,25 @@
|
|||
package io.newgrounds.components;
|
||||
class ComponentList {
|
||||
|
||||
var _core:NGLite;
|
||||
|
||||
// --- COMPONENTS
|
||||
public var medal : MedalComponent;
|
||||
public var app : AppComponent;
|
||||
public var event : EventComponent;
|
||||
public var scoreBoard: ScoreBoardComponent;
|
||||
public var loader : LoaderComponent;
|
||||
public var gateway : GatewayComponent;
|
||||
|
||||
public function new(core:NGLite) {
|
||||
|
||||
_core = core;
|
||||
|
||||
medal = new MedalComponent (_core);
|
||||
app = new AppComponent (_core);
|
||||
event = new EventComponent (_core);
|
||||
scoreBoard = new ScoreBoardComponent(_core);
|
||||
loader = new LoaderComponent (_core);
|
||||
gateway = new GatewayComponent (_core);
|
||||
}
|
||||
}
|
16
source/io/newgrounds/components/EventComponent.hx
Normal file
16
source/io/newgrounds/components/EventComponent.hx
Normal file
|
@ -0,0 +1,16 @@
|
|||
package io.newgrounds.components;
|
||||
|
||||
import io.newgrounds.objects.events.Result.LogEventResult;
|
||||
import io.newgrounds.NGLite;
|
||||
|
||||
class EventComponent extends Component {
|
||||
|
||||
public function new (core:NGLite){ super(core); }
|
||||
|
||||
public function logEvent(eventName:String):Call<LogEventResult> {
|
||||
|
||||
return new Call<LogEventResult>(_core, "Event.logEvent")
|
||||
.addComponentParameter("event_name", eventName)
|
||||
.addComponentParameter("host", _core.host);
|
||||
}
|
||||
}
|
25
source/io/newgrounds/components/GatewayComponent.hx
Normal file
25
source/io/newgrounds/components/GatewayComponent.hx
Normal file
|
@ -0,0 +1,25 @@
|
|||
package io.newgrounds.components;
|
||||
|
||||
import io.newgrounds.objects.events.Result;
|
||||
import io.newgrounds.NGLite;
|
||||
|
||||
class GatewayComponent extends Component {
|
||||
|
||||
public function new (core:NGLite){ super(core); }
|
||||
|
||||
public function getDatetime():Call<GetDateTimeResult> {
|
||||
|
||||
return new Call<GetDateTimeResult>(_core, "Gateway.getDatetime");
|
||||
}
|
||||
|
||||
public function getVersion():Call<GetVersionResult> {
|
||||
|
||||
return new Call<GetVersionResult>(_core, "Gateway.getVersion");
|
||||
}
|
||||
|
||||
public function ping():Call<PingResult> {
|
||||
|
||||
return new Call<PingResult>(_core, "Gateway.ping");
|
||||
}
|
||||
|
||||
}
|
44
source/io/newgrounds/components/LoaderComponent.hx
Normal file
44
source/io/newgrounds/components/LoaderComponent.hx
Normal file
|
@ -0,0 +1,44 @@
|
|||
package io.newgrounds.components;
|
||||
|
||||
import io.newgrounds.objects.events.Result;
|
||||
import io.newgrounds.NGLite;
|
||||
|
||||
class LoaderComponent extends Component {
|
||||
|
||||
public function new (core:NGLite){ super(core); }
|
||||
|
||||
public function loadAuthorUrl(redirect:Bool = false):Call<ResultBase> {
|
||||
|
||||
return new Call<ResultBase>(_core, "Loader.loadAuthorUrl")
|
||||
.addComponentParameter("host", _core.host)
|
||||
.addComponentParameter("redirect", redirect, true);
|
||||
}
|
||||
|
||||
public function loadMoreGames(redirect:Bool = false):Call<ResultBase> {
|
||||
|
||||
return new Call<ResultBase>(_core, "Loader.loadMoreGames")
|
||||
.addComponentParameter("host", _core.host)
|
||||
.addComponentParameter("redirect", redirect, true);
|
||||
}
|
||||
|
||||
public function loadNewgrounds(redirect:Bool = false):Call<ResultBase> {
|
||||
|
||||
return new Call<ResultBase>(_core, "Loader.loadNewgrounds")
|
||||
.addComponentParameter("host", _core.host)
|
||||
.addComponentParameter("redirect", redirect, true);
|
||||
}
|
||||
|
||||
public function loadOfficialUrl(redirect:Bool = false):Call<ResultBase> {
|
||||
|
||||
return new Call<ResultBase>(_core, "Loader.loadOfficialUrl")
|
||||
.addComponentParameter("host", _core.host)
|
||||
.addComponentParameter("redirect", redirect, true);
|
||||
}
|
||||
|
||||
public function loadReferral(redirect:Bool = false):Call<ResultBase> {
|
||||
|
||||
return new Call<ResultBase>(_core, "Loader.loadReferral")
|
||||
.addComponentParameter("host", _core.host)
|
||||
.addComponentParameter("redirect", redirect, true);
|
||||
}
|
||||
}
|
21
source/io/newgrounds/components/MedalComponent.hx
Normal file
21
source/io/newgrounds/components/MedalComponent.hx
Normal file
|
@ -0,0 +1,21 @@
|
|||
package io.newgrounds.components;
|
||||
|
||||
import io.newgrounds.objects.events.Result;
|
||||
import io.newgrounds.Call;
|
||||
import io.newgrounds.NGLite;
|
||||
|
||||
class MedalComponent extends Component {
|
||||
|
||||
public function new(core:NGLite):Void { super(core); }
|
||||
|
||||
public function unlock(id:Int):Call<MedalUnlockResult> {
|
||||
|
||||
return new Call<MedalUnlockResult>(_core, "Medal.unlock", true, true)
|
||||
.addComponentParameter("id", id);
|
||||
}
|
||||
|
||||
public function getList():Call<MedalListResult> {
|
||||
|
||||
return new Call<MedalListResult>(_core, "Medal.getList");
|
||||
}
|
||||
}
|
114
source/io/newgrounds/components/ScoreBoardComponent.hx
Normal file
114
source/io/newgrounds/components/ScoreBoardComponent.hx
Normal file
|
@ -0,0 +1,114 @@
|
|||
package io.newgrounds.components;
|
||||
|
||||
import io.newgrounds.objects.User;
|
||||
import io.newgrounds.objects.events.Response;
|
||||
import io.newgrounds.objects.events.Result;
|
||||
import io.newgrounds.objects.events.Result.ScoreBoardResult;
|
||||
import io.newgrounds.objects.events.Result.ScoreResult;
|
||||
import io.newgrounds.NGLite;
|
||||
import io.newgrounds.objects.ScoreBoard;
|
||||
|
||||
import haxe.ds.IntMap;
|
||||
|
||||
class ScoreBoardComponent extends Component {
|
||||
|
||||
public var allById:IntMap<ScoreBoard>;
|
||||
|
||||
public function new (core:NGLite){ super(core); }
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// GET SCORES
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
public function getBoards():Call<ScoreBoardResult> {
|
||||
|
||||
return new Call<ScoreBoardResult>(_core, "ScoreBoard.getBoards");
|
||||
}
|
||||
|
||||
/*function onBoardsReceive(response:Response<ScoreBoardResult>):Void {
|
||||
|
||||
if (!response.result.success)
|
||||
return;
|
||||
|
||||
allById = new IntMap<ScoreBoard>();
|
||||
|
||||
for (boardData in response.result.scoreboards)
|
||||
createBoard(boardData);
|
||||
|
||||
_core.log('${response.result.scoreboards.length} ScoreBoards loaded');
|
||||
}*/
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// GET SCORES
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
public function getScores
|
||||
( id :Int
|
||||
, limit :Int = 10
|
||||
, skip :Int = 0
|
||||
, period:Period = Period.DAY
|
||||
, social:Bool = false
|
||||
, tag :String = null
|
||||
, user :Dynamic = null
|
||||
):Call<ScoreResult> {
|
||||
|
||||
if (user != null && !Std.is(user, String) && !Std.is(user, Int))
|
||||
user = user.id;
|
||||
|
||||
return new Call<ScoreResult>(_core, "ScoreBoard.getScores")
|
||||
.addComponentParameter("id" , id )
|
||||
.addComponentParameter("limit" , limit , 10)
|
||||
.addComponentParameter("skip" , skip , 0)
|
||||
.addComponentParameter("period", period, Period.DAY)
|
||||
.addComponentParameter("social", social, false)
|
||||
.addComponentParameter("tag" , tag , null)
|
||||
.addComponentParameter("user" , user , null);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// POST SCORE
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
public function postScore(id:Int, value:Int, tag:String = null):Call<PostScoreResult> {
|
||||
|
||||
return new Call<PostScoreResult>(_core, "ScoreBoard.postScore", true, true)
|
||||
.addComponentParameter("id" , id)
|
||||
.addComponentParameter("value", value)
|
||||
.addComponentParameter("tag" , tag , null);
|
||||
}
|
||||
|
||||
/*function onScorePosted(response:Response<ResultBase>):Void {
|
||||
|
||||
if (!response.result.success)
|
||||
return;
|
||||
|
||||
allById = new IntMap<ScoreBoard>();
|
||||
|
||||
//createBoard(data.data.scoreBoard).parseScores(data.data.scores);
|
||||
}*/
|
||||
|
||||
inline function createBoard(data:Dynamic):ScoreBoard {
|
||||
|
||||
var board = new ScoreBoard(_core, data);
|
||||
_core.logVerbose('created $board');
|
||||
|
||||
allById.set(board.id, board);
|
||||
|
||||
return board;
|
||||
}
|
||||
}
|
||||
|
||||
@:enum
|
||||
abstract Period(String) to String from String{
|
||||
|
||||
/** Indicates scores are from the current day. */
|
||||
var DAY = "D";
|
||||
/** Indicates scores are from the current week. */
|
||||
var WEEK = "W";
|
||||
/** Indicates scores are from the current month. */
|
||||
var MONTH = "M";
|
||||
/** Indicates scores are from the current year. */
|
||||
var YEAR = "Y";
|
||||
/** Indicates scores are from all-time. */
|
||||
var ALL = "A";
|
||||
}
|
8
source/io/newgrounds/crypto/Cipher.hx
Normal file
8
source/io/newgrounds/crypto/Cipher.hx
Normal file
|
@ -0,0 +1,8 @@
|
|||
package io.newgrounds.crypto;
|
||||
|
||||
@:enum
|
||||
abstract Cipher(String) to String{
|
||||
var NONE = "none";
|
||||
var AES_128 = "aes128";
|
||||
var RC4 = "rc4";
|
||||
}
|
7
source/io/newgrounds/crypto/EncryptionFormat.hx
Normal file
7
source/io/newgrounds/crypto/EncryptionFormat.hx
Normal file
|
@ -0,0 +1,7 @@
|
|||
package io.newgrounds.crypto;
|
||||
|
||||
@:enum
|
||||
abstract EncryptionFormat(String) to String {
|
||||
var BASE_64 = "base64";
|
||||
var HEX = "hex";
|
||||
}
|
68
source/io/newgrounds/crypto/Rc4.hx
Normal file
68
source/io/newgrounds/crypto/Rc4.hx
Normal file
|
@ -0,0 +1,68 @@
|
|||
package io.newgrounds.crypto;
|
||||
|
||||
import haxe.io.Bytes;
|
||||
|
||||
/**
|
||||
* The following was straight-up ganked from https://github.com/iskolbin/rc4hx
|
||||
*
|
||||
* You da real MVP iskolbin...
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 iskolbin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
**/
|
||||
class Rc4 {
|
||||
var perm = Bytes.alloc( 256 );
|
||||
var index1: Int = 0;
|
||||
var index2: Int = 0;
|
||||
|
||||
public function new( key: Bytes ) {
|
||||
for ( i in 0...256 ) {
|
||||
perm.set( i, i );
|
||||
}
|
||||
|
||||
var j: Int = 0;
|
||||
for ( i in 0...256 ) {
|
||||
j = ( j + perm.get( i ) + key.get( i % key.length )) % 256;
|
||||
swap( i, j );
|
||||
}
|
||||
}
|
||||
|
||||
inline function swap( i: Int, j: Int ): Void {
|
||||
var temp = perm.get( i );
|
||||
perm.set( i, perm.get( j ));
|
||||
perm.set( j, temp );
|
||||
}
|
||||
|
||||
public function crypt( input: Bytes ): Bytes {
|
||||
var output = Bytes.alloc( input.length );
|
||||
|
||||
for ( i in 0...input.length ) {
|
||||
index1 = ( index1 + 1 ) % 256;
|
||||
index2 = ( index2 + perm.get( index1 )) % 256;
|
||||
swap( index1, index2 );
|
||||
var j = ( perm.get( index1 ) + perm.get( index2 )) % 256;
|
||||
output.set( i, input.get( i ) ^ perm.get( j ));
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
20
source/io/newgrounds/objects/Error.hx
Normal file
20
source/io/newgrounds/objects/Error.hx
Normal file
|
@ -0,0 +1,20 @@
|
|||
package io.newgrounds.objects;
|
||||
class Error {
|
||||
|
||||
public var code(default, null):Int;
|
||||
public var message(default, null):String;
|
||||
|
||||
public function new (message:String, code:Int = 0) {
|
||||
|
||||
this.message = message;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public function toString():String {
|
||||
|
||||
if (code > 0)
|
||||
return '#$code - $message';
|
||||
|
||||
return message;
|
||||
}
|
||||
}
|
118
source/io/newgrounds/objects/Medal.hx
Normal file
118
source/io/newgrounds/objects/Medal.hx
Normal file
|
@ -0,0 +1,118 @@
|
|||
package io.newgrounds.objects;
|
||||
|
||||
import io.newgrounds.objects.events.Response;
|
||||
import io.newgrounds.objects.events.Result.MedalUnlockResult;
|
||||
import io.newgrounds.utils.Dispatcher;
|
||||
import io.newgrounds.NGLite;
|
||||
|
||||
class Medal extends Object {
|
||||
|
||||
inline static public var EASY :Int = 1;
|
||||
inline static public var MODERATE :Int = 2;
|
||||
inline static public var CHALLENGING:Int = 3;
|
||||
inline static public var DIFFICULT :Int = 4;
|
||||
inline static public var BRUTAL :Int = 5;
|
||||
|
||||
static var difficultyNames:Array<String> =
|
||||
[ "Easy"
|
||||
, "Moderate"
|
||||
, "Challenging"
|
||||
, "Difficult"
|
||||
, "Brutal"
|
||||
];
|
||||
// --- FROM SERVER
|
||||
public var id (default, null):Int;
|
||||
public var name (default, null):String;
|
||||
public var description(default, null):String;
|
||||
public var icon (default, null):String;
|
||||
public var value (default, null):Int;
|
||||
public var difficulty (default, null):Int;
|
||||
public var secret (default, null):Bool;
|
||||
public var unlocked (default, null):Bool;
|
||||
// --- HELPERS
|
||||
public var difficultyName(get, never):String;
|
||||
|
||||
public var onUnlock:Dispatcher;
|
||||
|
||||
public function new(core:NGLite, data:Dynamic = null):Void {
|
||||
|
||||
onUnlock = new Dispatcher();
|
||||
|
||||
super(core, data);
|
||||
}
|
||||
|
||||
@:allow(io.newgrounds.NG)
|
||||
override function parse(data:Dynamic):Void {
|
||||
|
||||
var wasLocked = !unlocked;
|
||||
|
||||
id = data.id;
|
||||
name = data.name;
|
||||
description = data.description;
|
||||
icon = data.icon;
|
||||
value = data.value;
|
||||
difficulty = data.difficulty;
|
||||
secret = data.secret == 1;
|
||||
unlocked = data.unlocked;
|
||||
|
||||
super.parse(data);
|
||||
|
||||
if (wasLocked && unlocked)
|
||||
onUnlock.dispatch();
|
||||
|
||||
}
|
||||
|
||||
public function sendUnlock():Void {
|
||||
|
||||
if (_core.sessionId == null) {
|
||||
// --- Unlock regardless, show medal popup to encourage NG signup
|
||||
unlocked = true;
|
||||
onUnlock.dispatch();
|
||||
//TODO: save unlock in local save
|
||||
}
|
||||
|
||||
_core.calls.medal.unlock(id)
|
||||
.addDataHandler(onUnlockResponse)
|
||||
.send();
|
||||
}
|
||||
|
||||
function onUnlockResponse(response:Response<MedalUnlockResult>):Void {
|
||||
|
||||
if (response.success && response.result.success) {
|
||||
|
||||
parse(response.result.data.medal);
|
||||
|
||||
// --- Unlock response doesn't include unlock=true, so parse won't change it.
|
||||
if (!unlocked) {
|
||||
|
||||
unlocked = true;
|
||||
onUnlock.dispatch();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Locks the medal on the client and sends an unlock request, Server responds the same either way. */
|
||||
public function sendDebugUnlock():Void {
|
||||
|
||||
if (NG.core.sessionId == null) {
|
||||
|
||||
onUnlock.dispatch();
|
||||
|
||||
} else {
|
||||
|
||||
unlocked = false;
|
||||
|
||||
sendUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
public function get_difficultyName():String {
|
||||
|
||||
return difficultyNames[difficulty - 1];
|
||||
}
|
||||
|
||||
public function toString():String {
|
||||
|
||||
return 'Medal: $id@$name (${unlocked ? "unlocked" : "locked"}, $value pts, $difficultyName).';
|
||||
}
|
||||
}
|
33
source/io/newgrounds/objects/Object.hx
Normal file
33
source/io/newgrounds/objects/Object.hx
Normal file
|
@ -0,0 +1,33 @@
|
|||
package io.newgrounds.objects;
|
||||
|
||||
import io.newgrounds.utils.Dispatcher;
|
||||
import io.newgrounds.NGLite;
|
||||
|
||||
class Object {
|
||||
|
||||
var _core:NGLite;
|
||||
|
||||
public var onUpdate(default, null):Dispatcher;
|
||||
|
||||
public function new(core:NGLite, data:Dynamic = null) {
|
||||
|
||||
this._core = core;
|
||||
|
||||
onUpdate = new Dispatcher();
|
||||
|
||||
if (data != null)
|
||||
parse(data);
|
||||
}
|
||||
|
||||
@:allow(io.newgrounds.NGLite)
|
||||
function parse(data:Dynamic):Void {
|
||||
|
||||
onUpdate.dispatch();
|
||||
}
|
||||
|
||||
|
||||
public function destroy():Void {
|
||||
|
||||
_core = null;
|
||||
}
|
||||
}
|
17
source/io/newgrounds/objects/Score.hx
Normal file
17
source/io/newgrounds/objects/Score.hx
Normal file
|
@ -0,0 +1,17 @@
|
|||
package io.newgrounds.objects;
|
||||
|
||||
/** We don't want to serialize scores since there's a bajillion of them. */
|
||||
typedef Score = {
|
||||
|
||||
/** The value value in the format selected in your scoreboard settings. */
|
||||
var formatted_value:String;
|
||||
|
||||
/** The tag attached to this value (if any). */
|
||||
var tag:String;
|
||||
|
||||
/** The user who earned value. If this property is absent, the value belongs to the active user. */
|
||||
var user:User;
|
||||
|
||||
/** The integer value of the value. */
|
||||
var value:Int;
|
||||
}
|
76
source/io/newgrounds/objects/ScoreBoard.hx
Normal file
76
source/io/newgrounds/objects/ScoreBoard.hx
Normal file
|
@ -0,0 +1,76 @@
|
|||
package io.newgrounds.objects;
|
||||
|
||||
import io.newgrounds.components.ScoreBoardComponent.Period;
|
||||
import io.newgrounds.objects.events.Response;
|
||||
import io.newgrounds.objects.events.Result;
|
||||
import io.newgrounds.objects.events.Result.ScoreResult;
|
||||
import io.newgrounds.NGLite;
|
||||
|
||||
class ScoreBoard extends Object {
|
||||
|
||||
public var scores(default, null):Array<Score>;
|
||||
|
||||
/** The numeric ID of the scoreboard.*/
|
||||
public var id(default, null):Int;
|
||||
|
||||
/** The name of the scoreboard. */
|
||||
public var name(default, null):String;
|
||||
|
||||
public function new(core:NGLite, data:Dynamic):Void {super(core, data); }
|
||||
|
||||
override function parse(data:Dynamic):Void {
|
||||
|
||||
id = data.id;
|
||||
name = data.name;
|
||||
|
||||
super.parse(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches score data from the server, this removes all of the existing scores cached
|
||||
*
|
||||
* We don't unify the old and new scores because a user's rank or score may change between requests
|
||||
*/
|
||||
public function requestScores
|
||||
( limit :Int = 10
|
||||
, skip :Int = 0
|
||||
, period:Period = Period.ALL
|
||||
, social:Bool = false
|
||||
, tag :String = null
|
||||
, user :Dynamic = null
|
||||
):Void {
|
||||
|
||||
_core.calls.scoreBoard.getScores(id, limit, skip, period, social, tag, user)
|
||||
.addDataHandler(onScoresReceived)
|
||||
.send();
|
||||
}
|
||||
|
||||
function onScoresReceived(response:Response<ScoreResult>):Void {
|
||||
|
||||
if (!response.success || !response.result.success)
|
||||
return;
|
||||
|
||||
scores = response.result.data.scores;
|
||||
_core.logVerbose('received ${scores.length} scores');
|
||||
|
||||
onUpdate.dispatch();
|
||||
}
|
||||
|
||||
public function postScore(value :Int, tag:String = null):Void {
|
||||
|
||||
_core.calls.scoreBoard.postScore(id, value, tag)
|
||||
.addDataHandler(onScorePosted)
|
||||
.send();
|
||||
}
|
||||
|
||||
function onScorePosted(response:Response<PostScoreResult>):Void {
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function toString():String {
|
||||
|
||||
return 'ScoreBoard: $id@$name';
|
||||
}
|
||||
|
||||
}
|
65
source/io/newgrounds/objects/Session.hx
Normal file
65
source/io/newgrounds/objects/Session.hx
Normal file
|
@ -0,0 +1,65 @@
|
|||
package io.newgrounds.objects;
|
||||
|
||||
class Session extends Object {
|
||||
|
||||
/** If true, the session_id is expired. Use App.startSession to get a new one.*/
|
||||
public var expired(default, null):Bool;
|
||||
|
||||
/** A unique session identifier */
|
||||
public var id(default, null):String;
|
||||
|
||||
/** If the session has no associated user but is not expired, this property will provide a URL that can be used to sign the user in. */
|
||||
public var passportUrl(default, null):String;
|
||||
|
||||
/** If true, the user would like you to remember their session id. */
|
||||
public var remember(default, null):Bool;
|
||||
|
||||
/** If the user has not signed in, or granted access to your app, this will be null */
|
||||
public var user(default, null):User;
|
||||
|
||||
//TODO:desciption
|
||||
public var status(get, never):SessionStatus;
|
||||
|
||||
public function new(core:NGLite, data:Dynamic = null) { super(core, data); }
|
||||
|
||||
override public function parse(data:Dynamic):Void {
|
||||
|
||||
id = data.id;
|
||||
expired = data.expired;
|
||||
passportUrl = data.passport_url;
|
||||
remember = data.remember;
|
||||
|
||||
// --- KEEP THE SAME INSTANCE
|
||||
if (user == null)
|
||||
user = data.user;
|
||||
// TODO?: update original user instance with new data. (probly not)
|
||||
|
||||
super.parse(data);
|
||||
}
|
||||
|
||||
public function get_status():SessionStatus {
|
||||
|
||||
if (expired || id == null || id == "")
|
||||
return SessionStatus.SESSION_EXPIRED;
|
||||
|
||||
if (user != null && user.name != null && user.name != "")
|
||||
return SessionStatus.USER_LOADED;
|
||||
|
||||
return SessionStatus.REQUEST_LOGIN;
|
||||
}
|
||||
|
||||
public function expire():Void {
|
||||
|
||||
expired = true;
|
||||
id = null;
|
||||
user = null;
|
||||
}
|
||||
}
|
||||
|
||||
@:enum
|
||||
abstract SessionStatus(String) {
|
||||
|
||||
var SESSION_EXPIRED = "session-expired";
|
||||
var REQUEST_LOGIN = "request-login";
|
||||
var USER_LOADED = "user-loaded";
|
||||
}
|
19
source/io/newgrounds/objects/User.hx
Normal file
19
source/io/newgrounds/objects/User.hx
Normal file
|
@ -0,0 +1,19 @@
|
|||
package io.newgrounds.objects;
|
||||
|
||||
typedef User = {
|
||||
|
||||
/** The user's icon images. */
|
||||
var icons:UserIcons;
|
||||
|
||||
/** The user's numeric ID. */
|
||||
var id:Int;
|
||||
|
||||
/** The user's textual name. */
|
||||
var name:String;
|
||||
|
||||
/** Returns true if the user has a Newgrounds Supporter upgrade. */
|
||||
var supporter:Bool;
|
||||
|
||||
/** The user's NG profile url. */
|
||||
var url:String;
|
||||
}
|
14
source/io/newgrounds/objects/UserIcons.hx
Normal file
14
source/io/newgrounds/objects/UserIcons.hx
Normal file
|
@ -0,0 +1,14 @@
|
|||
package io.newgrounds.objects;
|
||||
|
||||
typedef UserIcons = {
|
||||
|
||||
/**The URL of the user's large icon. */
|
||||
var large:String;
|
||||
|
||||
/** The URL of the user's medium icon. */
|
||||
var medium:String;
|
||||
|
||||
/** The URL of the user's small icon. */
|
||||
var small:String;
|
||||
}
|
||||
|
43
source/io/newgrounds/objects/events/Response.hx
Normal file
43
source/io/newgrounds/objects/events/Response.hx
Normal file
|
@ -0,0 +1,43 @@
|
|||
package io.newgrounds.objects.events;
|
||||
|
||||
import io.newgrounds.objects.events.Result.ResultBase;
|
||||
import haxe.Json;
|
||||
import io.newgrounds.objects.Error;
|
||||
|
||||
typedef DebugResponse = {
|
||||
|
||||
var exec_time:Int;
|
||||
var input:Dynamic;
|
||||
}
|
||||
|
||||
class Response<T:ResultBase> {
|
||||
|
||||
public var success(default, null):Bool;
|
||||
public var error(default, null):Error;
|
||||
public var debug(default, null):DebugResponse;
|
||||
public var result(default, null):Result<T>;
|
||||
|
||||
public function new (core:NGLite, reply:String) {
|
||||
|
||||
var data:Dynamic;
|
||||
|
||||
try {
|
||||
data = Json.parse(reply);
|
||||
|
||||
} catch (e:Dynamic) {
|
||||
|
||||
data = Json.parse('{"success":false,"error":{"message":"${Std.string(reply)}","code":0}}');
|
||||
}
|
||||
|
||||
success = data.success;
|
||||
debug = data.debug;
|
||||
|
||||
if (!success) {
|
||||
error = new Error(data.error.message, data.error.code);
|
||||
core.logError('Call unseccessful: $error');
|
||||
return;
|
||||
}
|
||||
|
||||
result = new Result<T>(core, data.result);
|
||||
}
|
||||
}
|
109
source/io/newgrounds/objects/events/Result.hx
Normal file
109
source/io/newgrounds/objects/events/Result.hx
Normal file
|
@ -0,0 +1,109 @@
|
|||
package io.newgrounds.objects.events;
|
||||
|
||||
class Result<T:ResultBase> {
|
||||
|
||||
public var echo(default, null):String;
|
||||
public var component(default, null):String;
|
||||
|
||||
public var data(default, null):T;
|
||||
public var success(default, null):Bool;
|
||||
public var debug(default, null):Bool;
|
||||
public var error(default, null):Error;
|
||||
|
||||
public function new(core:NGLite, data:Dynamic) {
|
||||
|
||||
echo = data.echo;
|
||||
component = data.component;
|
||||
|
||||
data = data.data;
|
||||
success = data.success;
|
||||
debug = data.debug;
|
||||
|
||||
if(!data.success) {
|
||||
|
||||
error = new Error(data.error.message, data.error.code);
|
||||
core.logError('$component fail: $error');
|
||||
|
||||
} else
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
typedef ResultBase = { };
|
||||
|
||||
typedef SessionResult = {
|
||||
> ResultBase,
|
||||
|
||||
var session:Dynamic;
|
||||
}
|
||||
|
||||
typedef GetHostResult = {
|
||||
> ResultBase,
|
||||
|
||||
var host_approved:Bool;
|
||||
}
|
||||
|
||||
typedef GetCurrentVersionResult = {
|
||||
> ResultBase,
|
||||
|
||||
var current_version:String;
|
||||
var client_deprecated:Bool;
|
||||
}
|
||||
|
||||
typedef LogEventResult = {
|
||||
> ResultBase,
|
||||
|
||||
var event_name:String;
|
||||
}
|
||||
|
||||
typedef GetDateTimeResult = {
|
||||
> ResultBase,
|
||||
|
||||
var datetime:String;
|
||||
}
|
||||
|
||||
typedef GetVersionResult = {
|
||||
> ResultBase,
|
||||
|
||||
var version:String;
|
||||
}
|
||||
|
||||
typedef PingResult = {
|
||||
> ResultBase,
|
||||
|
||||
var pong:String;
|
||||
}
|
||||
|
||||
typedef MedalListResult = {
|
||||
> ResultBase,
|
||||
|
||||
var medals:Array<Dynamic>;
|
||||
}
|
||||
|
||||
typedef MedalUnlockResult = {
|
||||
> ResultBase,
|
||||
|
||||
var medal_score:String;
|
||||
var medal:Dynamic;
|
||||
}
|
||||
|
||||
typedef ScoreBoardResult = {
|
||||
> ResultBase,
|
||||
|
||||
var scoreboards:Array<Dynamic>;
|
||||
}
|
||||
|
||||
typedef ScoreResult = {
|
||||
> ResultBase,
|
||||
|
||||
var scores:Array<Score>;
|
||||
var scoreboard:Dynamic;
|
||||
}
|
||||
|
||||
typedef PostScoreResult = {
|
||||
> ResultBase,
|
||||
|
||||
var tag:String;
|
||||
var scoreboard:Dynamic;
|
||||
var score:Score;
|
||||
}
|
23
source/io/newgrounds/swf/LoadingBar.hx
Normal file
23
source/io/newgrounds/swf/LoadingBar.hx
Normal file
|
@ -0,0 +1,23 @@
|
|||
package io.newgrounds.swf;
|
||||
|
||||
import openfl.display.MovieClip;
|
||||
|
||||
class LoadingBar extends MovieClip {
|
||||
|
||||
public var bar(default, null):MovieClip;
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
|
||||
setProgress(0.0);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param value The ratio of bytes loaded to bytes total
|
||||
*/
|
||||
public function setProgress(value:Float):Void {
|
||||
|
||||
bar.gotoAndStop(1 + Std.int(value * (bar.totalFrames - 1)));
|
||||
}
|
||||
}
|
151
source/io/newgrounds/swf/MedalPopup.hx
Normal file
151
source/io/newgrounds/swf/MedalPopup.hx
Normal file
|
@ -0,0 +1,151 @@
|
|||
package io.newgrounds.swf;
|
||||
|
||||
import io.newgrounds.swf.common.BaseAsset;
|
||||
import io.newgrounds.objects.Medal;
|
||||
|
||||
import openfl.text.TextFieldAutoSize;
|
||||
import openfl.text.TextField;
|
||||
import openfl.display.DisplayObject;
|
||||
import openfl.display.Loader;
|
||||
import openfl.display.MovieClip;
|
||||
import openfl.net.URLRequest;
|
||||
import openfl.events.Event;
|
||||
|
||||
class MedalPopup extends BaseAsset {
|
||||
|
||||
static inline var FRAME_HIDDEN:String = "hidden";
|
||||
static inline var FRAME_MEDAL_UNLOCKED:String = "medalUnlocked";
|
||||
static inline var FRAME_INTRO_COMPLETE:String = "introComplete";
|
||||
static inline var FRAME_UNLOCK_COMPLETE:String = "unlockComplete";
|
||||
static inline var MIN_TEXT_SIZE:Int = 12;
|
||||
|
||||
public var medalIcon(default, null):MovieClip;
|
||||
public var medalName(default, null):MovieClip;
|
||||
public var medalPoints(default, null):MovieClip;
|
||||
|
||||
public var alwaysOnTop:Bool;
|
||||
#if !ng_lite
|
||||
public var requiresSession:Bool;
|
||||
#end
|
||||
|
||||
var _animQueue = new Array<Void->Void>();
|
||||
var _scrollSpeed:Float;
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
|
||||
mouseEnabled = false;
|
||||
mouseChildren = false;
|
||||
|
||||
hide();
|
||||
addFrameScript(totalFrames - 1, onUnlockAnimComplete);
|
||||
}
|
||||
|
||||
function hide():Void {
|
||||
|
||||
visible = false;
|
||||
gotoAndStop(FRAME_HIDDEN);
|
||||
}
|
||||
|
||||
#if !ng_lite
|
||||
override function onReady():Void {
|
||||
super.onReady();
|
||||
|
||||
if (NG.core.medals != null)
|
||||
onMedalsLoaded();
|
||||
else
|
||||
NG.core.onLogin.addOnce(NG.core.requestMedals.bind(onMedalsLoaded));
|
||||
}
|
||||
|
||||
function onMedalsLoaded():Void {
|
||||
|
||||
for (medal in NG.core.medals)
|
||||
medal.onUnlock.add(onMedalOnlock.bind(medal));
|
||||
}
|
||||
|
||||
function onMedalOnlock(medal:Medal):Void {
|
||||
|
||||
if (requiresSession && !NG.core.loggedIn)
|
||||
return;
|
||||
|
||||
var loader = new Loader();
|
||||
loader.load(new URLRequest(medal.icon));
|
||||
|
||||
playAnim(loader, medal.name, medal.value);
|
||||
}
|
||||
|
||||
#end
|
||||
|
||||
public function playAnim(icon:DisplayObject, name:String, value:Int):Void {
|
||||
|
||||
if (currentLabel == FRAME_HIDDEN)
|
||||
playNextAnim(icon, name, value);
|
||||
else
|
||||
_animQueue.push(playNextAnim.bind(icon, name, value));
|
||||
}
|
||||
|
||||
function playNextAnim(icon:DisplayObject, name:String, value:Int):Void {
|
||||
|
||||
visible = true;
|
||||
gotoAndPlay(FRAME_MEDAL_UNLOCKED);
|
||||
|
||||
if (alwaysOnTop && parent != null) {
|
||||
|
||||
parent.setChildIndex(this, parent.numChildren - 1);
|
||||
}
|
||||
|
||||
while(medalIcon.numChildren > 0)
|
||||
medalIcon.removeChildAt(0);
|
||||
|
||||
cast(medalPoints.getChildByName("field"), TextField).text = Std.string(value);
|
||||
|
||||
var field:TextField = cast medalName.getChildByName("field");
|
||||
field.autoSize = TextFieldAutoSize.LEFT;
|
||||
field.x = 0;
|
||||
field.text = "";
|
||||
var oldWidth = medalName.width;
|
||||
field.text = name;
|
||||
|
||||
_scrollSpeed = 0;
|
||||
if (field.width > oldWidth + 4) {
|
||||
|
||||
field.x = oldWidth + 4;
|
||||
initScroll(field);
|
||||
}
|
||||
|
||||
medalIcon.addChild(icon);
|
||||
}
|
||||
|
||||
function initScroll(field:TextField):Void {
|
||||
//TODO: Find out why scrollrect didn't work
|
||||
|
||||
var animDuration = 0;
|
||||
|
||||
for (frame in currentLabels){
|
||||
|
||||
if (frame.name == FRAME_INTRO_COMPLETE )
|
||||
animDuration -= frame.frame;
|
||||
else if (frame.name == FRAME_UNLOCK_COMPLETE)
|
||||
animDuration += frame.frame;
|
||||
}
|
||||
|
||||
_scrollSpeed = (field.width + field.x + 4) / animDuration;
|
||||
field.addEventListener(Event.ENTER_FRAME, updateScroll);
|
||||
}
|
||||
|
||||
function updateScroll(e:Event):Void{
|
||||
|
||||
if (currentLabel == FRAME_INTRO_COMPLETE)
|
||||
cast (e.currentTarget, TextField).x -= _scrollSpeed;
|
||||
}
|
||||
|
||||
function onUnlockAnimComplete():Void {
|
||||
|
||||
cast (medalName.getChildByName("field"), TextField).removeEventListener(Event.ENTER_FRAME, updateScroll);
|
||||
|
||||
if (_animQueue.length == 0)
|
||||
hide();
|
||||
else
|
||||
(_animQueue.shift())();
|
||||
}
|
||||
}
|
250
source/io/newgrounds/swf/ScoreBrowser.hx
Normal file
250
source/io/newgrounds/swf/ScoreBrowser.hx
Normal file
|
@ -0,0 +1,250 @@
|
|||
package io.newgrounds.swf;
|
||||
|
||||
import openfl.events.Event;
|
||||
import io.newgrounds.swf.common.DropDown;
|
||||
import io.newgrounds.objects.Score;
|
||||
import io.newgrounds.objects.events.Result.ScoreBoardResult;
|
||||
import io.newgrounds.objects.events.Result.ScoreResult;
|
||||
import io.newgrounds.objects.events.Response;
|
||||
import io.newgrounds.swf.common.BaseAsset;
|
||||
import io.newgrounds.swf.common.Button;
|
||||
import io.newgrounds.components.ScoreBoardComponent.Period;
|
||||
|
||||
import openfl.display.MovieClip;
|
||||
import openfl.text.TextField;
|
||||
|
||||
class ScoreBrowser extends BaseAsset {
|
||||
|
||||
public var prevButton (default, null):MovieClip;
|
||||
public var nextButton (default, null):MovieClip;
|
||||
public var reloadButton (default, null):MovieClip;
|
||||
public var listBox (default, null):MovieClip;
|
||||
public var loadingIcon (default, null):MovieClip;
|
||||
public var errorIcon (default, null):MovieClip;
|
||||
public var scoreContainer(default, null):MovieClip;
|
||||
public var titleField (default, null):TextField;
|
||||
public var pageField (default, null):TextField;
|
||||
|
||||
public var period(get, set):Period;
|
||||
function get_period():Period { return _periodDropDown.value; }
|
||||
function set_period(value:Period):Period { return _periodDropDown.value = value; }
|
||||
|
||||
public var title(get, set):String;
|
||||
function get_title():String { return titleField.text; }
|
||||
function set_title(value:String):String { return titleField.text = value; }
|
||||
|
||||
public var tag(default, set):String;
|
||||
function set_tag(value:String):String {
|
||||
|
||||
if (this.tag != value) {
|
||||
|
||||
this.tag = value;
|
||||
delayReload();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public var social(default, set):Bool;
|
||||
function set_social(value:Bool):Bool {
|
||||
|
||||
if (this.social != value) {
|
||||
|
||||
this.social = value;
|
||||
delayReload();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public var boardId(default, set):Int;
|
||||
function set_boardId(value:Int):Int {
|
||||
|
||||
_boardIDSet = true;
|
||||
|
||||
if (this.boardId != value) {
|
||||
|
||||
this.boardId = value;
|
||||
delayReload();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public var page(default, set):Int;
|
||||
function set_page(value:Int):Int {
|
||||
|
||||
if (this.page != value) {
|
||||
|
||||
this.page = value;
|
||||
delayReload();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
var _scores:Array<MovieClip>;
|
||||
var _limit:Int = 0;
|
||||
var _periodDropDown:DropDown;
|
||||
var _boardIDSet:Bool;
|
||||
|
||||
public function new() { super(); }
|
||||
|
||||
override function setDefaults():Void {
|
||||
super.setDefaults();
|
||||
|
||||
boardId = -1;
|
||||
_boardIDSet = false;
|
||||
|
||||
scoreContainer.visible = false;
|
||||
loadingIcon.visible = false;
|
||||
reloadButton.visible = false;
|
||||
errorIcon.visible = false;
|
||||
errorIcon.addFrameScript(errorIcon.totalFrames - 1, errorIcon.stop);
|
||||
|
||||
//TODO: prevent memory leaks?
|
||||
new Button(prevButton, onPrevClick);
|
||||
new Button(nextButton, onNextClick);
|
||||
new Button(reloadButton, reload);
|
||||
_periodDropDown = new DropDown(listBox, delayReload);
|
||||
_periodDropDown.addItem("Current day" , Period.DAY );
|
||||
_periodDropDown.addItem("Current week" , Period.WEEK );
|
||||
_periodDropDown.addItem("Current month", Period.MONTH);
|
||||
_periodDropDown.addItem("Current year" , Period.YEAR );
|
||||
_periodDropDown.addItem("All time" , Period.ALL );
|
||||
_periodDropDown.value = Period.ALL;
|
||||
|
||||
_scores = new Array<MovieClip>();
|
||||
while(true) {
|
||||
|
||||
var score:MovieClip = cast scoreContainer.getChildByName('score${_scores.length}');
|
||||
if (score == null)
|
||||
break;
|
||||
|
||||
new Button(score);
|
||||
_scores.push(score);
|
||||
}
|
||||
|
||||
_limit = _scores.length;
|
||||
}
|
||||
|
||||
override function onReady():Void {
|
||||
super.onReady();
|
||||
|
||||
if (boardId == -1 && !_boardIDSet) {
|
||||
|
||||
#if ng_lite
|
||||
NG.core.calls.scoreBoard.getBoards()
|
||||
.addDataHandler(onBoardsRecieved)
|
||||
.queue();
|
||||
#else
|
||||
if (NG.core.scoreBoards != null)
|
||||
onBoardsLoaded();
|
||||
else
|
||||
NG.core.requestScoreBoards(onBoardsLoaded);
|
||||
#end
|
||||
}
|
||||
|
||||
reload();
|
||||
}
|
||||
|
||||
#if ng_lite
|
||||
function onBoardsRecieved(response:Response<ScoreBoardResult>):Void {
|
||||
|
||||
if (response.success && response.result.success) {
|
||||
|
||||
for (board in response.result.data.scoreboards) {
|
||||
|
||||
NG.core.log('No boardId specified defaulting to ${board.name}');
|
||||
boardId = board.id;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
function onBoardsLoaded():Void {
|
||||
|
||||
for (board in NG.core.scoreBoards) {
|
||||
|
||||
NG.core.log('No boardId specified defaulting to ${board.name}');
|
||||
boardId = board.id;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#end
|
||||
|
||||
/** Used internally to avoid multiple server requests from various property changes in a small time-frame. **/
|
||||
function delayReload():Void {
|
||||
|
||||
addEventListener(Event.EXIT_FRAME, onDelayComplete);
|
||||
}
|
||||
|
||||
function onDelayComplete(e:Event):Void { reload(); }
|
||||
|
||||
public function reload():Void {
|
||||
removeEventListener(Event.EXIT_FRAME, onDelayComplete);
|
||||
|
||||
errorIcon.visible = false;
|
||||
scoreContainer.visible = false;
|
||||
pageField.text = 'page ${page + 1}';
|
||||
|
||||
if (_coreReady && boardId != -1 && _limit > 0 && period != null) {
|
||||
|
||||
loadingIcon.visible = true;
|
||||
|
||||
NG.core.calls.scoreBoard.getScores(boardId, _limit, _limit * page, period, social, tag)
|
||||
.addDataHandler(onScoresReceive)
|
||||
.send();
|
||||
}
|
||||
}
|
||||
|
||||
function onScoresReceive(response:Response<ScoreResult>):Void {
|
||||
|
||||
loadingIcon.visible = false;
|
||||
|
||||
if (response.success && response.result.success) {
|
||||
|
||||
scoreContainer.visible = true;
|
||||
|
||||
var i = _limit;
|
||||
while(i > 0) {
|
||||
i--;
|
||||
|
||||
if (i < response.result.data.scores.length)
|
||||
drawScore(i, response.result.data.scores[i], _scores[i]);
|
||||
else
|
||||
drawScore(i, null, _scores[i]);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
errorIcon.visible = true;
|
||||
errorIcon.gotoAndPlay(1);
|
||||
reloadButton.visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
inline function drawScore(rank:Int, score:Score, asset:MovieClip):Void {
|
||||
|
||||
if (score == null)
|
||||
asset.visible = false;
|
||||
else {
|
||||
|
||||
asset.visible = true;
|
||||
cast (asset.getChildByName("nameField" ), TextField).text = score.user.name;
|
||||
cast (asset.getChildByName("scoreField"), TextField).text = score.formatted_value;
|
||||
cast (asset.getChildByName("rankField" ), TextField).text = Std.string(rank + 1);
|
||||
}
|
||||
}
|
||||
|
||||
function onPrevClick():Void {
|
||||
|
||||
if (page > 0)
|
||||
page--;
|
||||
}
|
||||
|
||||
function onNextClick():Void {
|
||||
|
||||
page++;
|
||||
}
|
||||
}
|
35
source/io/newgrounds/swf/common/BaseAsset.hx
Normal file
35
source/io/newgrounds/swf/common/BaseAsset.hx
Normal file
|
@ -0,0 +1,35 @@
|
|||
package io.newgrounds.swf.common;
|
||||
|
||||
import openfl.events.Event;
|
||||
import openfl.display.MovieClip;
|
||||
|
||||
class BaseAsset extends MovieClip {
|
||||
|
||||
var _coreReady:Bool = false;
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
|
||||
setDefaults();
|
||||
|
||||
if (stage != null)
|
||||
onAdded(null);
|
||||
else
|
||||
addEventListener(Event.ADDED_TO_STAGE, onAdded);
|
||||
}
|
||||
|
||||
function setDefaults():Void { }
|
||||
|
||||
function onAdded(e:Event):Void {
|
||||
|
||||
if (NG.core != null)
|
||||
onReady();
|
||||
else
|
||||
NG.onCoreReady.add(onReady);
|
||||
}
|
||||
|
||||
function onReady():Void {
|
||||
|
||||
_coreReady = true;
|
||||
}
|
||||
}
|
151
source/io/newgrounds/swf/common/Button.hx
Normal file
151
source/io/newgrounds/swf/common/Button.hx
Normal file
|
@ -0,0 +1,151 @@
|
|||
package io.newgrounds.swf.common;
|
||||
|
||||
import openfl.display.Stage;
|
||||
import openfl.events.Event;
|
||||
import openfl.events.MouseEvent;
|
||||
import openfl.display.MovieClip;
|
||||
|
||||
class Button {
|
||||
|
||||
var _enabled:Bool;
|
||||
public var enabled(get, set):Bool;
|
||||
function get_enabled():Bool { return _enabled; }
|
||||
function set_enabled(value:Bool):Bool {
|
||||
|
||||
if (value != _enabled) {
|
||||
|
||||
_enabled = value;
|
||||
updateEnabled();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public var onClick:Void->Void;
|
||||
public var onOver:Void->Void;
|
||||
public var onOut:Void->Void;
|
||||
|
||||
var _target:MovieClip;
|
||||
var _down:Bool;
|
||||
var _over:Bool;
|
||||
var _foundLabels:Array<String>;
|
||||
|
||||
public function new(target:MovieClip, onClick:Void->Void = null, onOver:Void->Void = null, onOut:Void->Void = null) {
|
||||
|
||||
_target = target;
|
||||
this.onClick = onClick;
|
||||
this.onOver = onOver;
|
||||
this.onOut = onOut;
|
||||
|
||||
_foundLabels = new Array<String>();
|
||||
for (label in _target.currentLabels)
|
||||
_foundLabels.push(label.name);
|
||||
|
||||
_target.stop();
|
||||
_target.addEventListener(Event.ADDED_TO_STAGE, onAdded);
|
||||
if (target.stage != null)
|
||||
onAdded(null);
|
||||
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
function onAdded(e:Event):Void {
|
||||
|
||||
var stage = _target.stage;
|
||||
stage.addEventListener(MouseEvent.MOUSE_UP, mouseHandler);
|
||||
_target.addEventListener(MouseEvent.MOUSE_OVER, mouseHandler);
|
||||
_target.addEventListener(MouseEvent.MOUSE_OUT, mouseHandler);
|
||||
_target.addEventListener(MouseEvent.MOUSE_DOWN, mouseHandler);
|
||||
_target.addEventListener(MouseEvent.CLICK, mouseHandler);
|
||||
|
||||
function selfRemoveEvent(e:Event):Void {
|
||||
|
||||
_target.removeEventListener(Event.REMOVED_FROM_STAGE, selfRemoveEvent);
|
||||
onRemove(e, stage);
|
||||
}
|
||||
_target.addEventListener(Event.REMOVED_FROM_STAGE, selfRemoveEvent);
|
||||
}
|
||||
|
||||
function onRemove(e:Event, stage:Stage):Void {
|
||||
|
||||
stage.removeEventListener(MouseEvent.MOUSE_UP, mouseHandler);
|
||||
_target.removeEventListener(MouseEvent.MOUSE_OVER, mouseHandler);
|
||||
_target.removeEventListener(MouseEvent.MOUSE_OUT, mouseHandler);
|
||||
_target.removeEventListener(MouseEvent.MOUSE_DOWN, mouseHandler);
|
||||
_target.removeEventListener(MouseEvent.CLICK, mouseHandler);
|
||||
}
|
||||
|
||||
function mouseHandler(event:MouseEvent):Void {
|
||||
|
||||
switch(event.type) {
|
||||
|
||||
case MouseEvent.MOUSE_OVER:
|
||||
|
||||
_over = true;
|
||||
|
||||
if (onOver != null)
|
||||
onOver();
|
||||
|
||||
case MouseEvent.MOUSE_OUT:
|
||||
|
||||
_over = false;
|
||||
|
||||
if (onOut != null)
|
||||
onOut();
|
||||
|
||||
case MouseEvent.MOUSE_DOWN:
|
||||
|
||||
_down = true;
|
||||
|
||||
case MouseEvent.MOUSE_UP:
|
||||
|
||||
_down = false;
|
||||
|
||||
case MouseEvent.CLICK:
|
||||
|
||||
if (enabled && onClick != null)
|
||||
onClick();
|
||||
}
|
||||
updateState();
|
||||
}
|
||||
|
||||
function updateEnabled():Void {
|
||||
|
||||
updateState();
|
||||
|
||||
_target.useHandCursor = enabled;
|
||||
_target.buttonMode = enabled;
|
||||
}
|
||||
|
||||
function updateState():Void {
|
||||
|
||||
var state = determineState();
|
||||
|
||||
if (_target.currentLabel != state && _foundLabels.indexOf(state) != -1)
|
||||
_target.gotoAndStop(state);
|
||||
}
|
||||
|
||||
function determineState():String {
|
||||
|
||||
if (enabled) {
|
||||
|
||||
if (_over)
|
||||
return _down ? "down" : "over";
|
||||
|
||||
return "up";
|
||||
|
||||
}
|
||||
return "disabled";
|
||||
}
|
||||
|
||||
public function destroy():Void {
|
||||
|
||||
_target.removeEventListener(Event.ADDED_TO_STAGE, onAdded);
|
||||
|
||||
_target = null;
|
||||
onClick = null;
|
||||
onOver = null;
|
||||
onOut = null;
|
||||
_foundLabels = null;
|
||||
}
|
||||
}
|
88
source/io/newgrounds/swf/common/DropDown.hx
Normal file
88
source/io/newgrounds/swf/common/DropDown.hx
Normal file
|
@ -0,0 +1,88 @@
|
|||
package io.newgrounds.swf.common;
|
||||
|
||||
|
||||
import haxe.ds.StringMap;
|
||||
|
||||
import openfl.display.MovieClip;
|
||||
import openfl.display.Sprite;
|
||||
import openfl.text.TextField;
|
||||
|
||||
class DropDown {
|
||||
|
||||
public var value(default, set):String;
|
||||
function set_value(v:String):String {
|
||||
|
||||
if (this.value == v)
|
||||
return v;
|
||||
|
||||
this.value = v;
|
||||
_selectedLabel.text = _values.get(v);
|
||||
|
||||
if (_onChange != null)
|
||||
_onChange();
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
var _choiceContainer:Sprite;
|
||||
var _selectedLabel:TextField;
|
||||
var _onChange:Void->Void;
|
||||
var _values:StringMap<String>;
|
||||
var _unusedChoices:Array<MovieClip>;
|
||||
|
||||
public function new(target:MovieClip, label:String = "", onChange:Void->Void = null) {
|
||||
|
||||
_onChange = onChange;
|
||||
|
||||
_selectedLabel = cast cast(target.getChildByName("currentItem"), MovieClip).getChildByName("label");
|
||||
_selectedLabel.text = label;
|
||||
|
||||
_values = new StringMap<String>();
|
||||
|
||||
new Button(cast target.getChildByName("button"), onClickExpand);
|
||||
new Button(cast target.getChildByName("currentItem"), onClickExpand);
|
||||
_choiceContainer = new Sprite();
|
||||
_choiceContainer.visible = false;
|
||||
target.addChild(_choiceContainer);
|
||||
|
||||
_unusedChoices = new Array<MovieClip>();
|
||||
while(true) {
|
||||
|
||||
var item:MovieClip = cast target.getChildByName('item${_unusedChoices.length}');
|
||||
if (item == null)
|
||||
break;
|
||||
|
||||
target.removeChild(item);
|
||||
_unusedChoices.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
public function addItem(name:String, value:String):Void {
|
||||
|
||||
_values.set(value, name);
|
||||
|
||||
if (_unusedChoices.length == 0) {
|
||||
|
||||
NG.core.logError('cannot create another dropBox item max=${_choiceContainer.numChildren}');
|
||||
return;
|
||||
}
|
||||
|
||||
var button = _unusedChoices.shift();
|
||||
cast(button.getChildByName("label"), TextField).text = name;
|
||||
_choiceContainer.addChild(button);
|
||||
|
||||
new Button(button, onChoiceClick.bind(value));
|
||||
}
|
||||
|
||||
function onClickExpand():Void {
|
||||
|
||||
_choiceContainer.visible = !_choiceContainer.visible;
|
||||
}
|
||||
|
||||
function onChoiceClick(name:String):Void {
|
||||
|
||||
value = name;
|
||||
|
||||
_choiceContainer.visible = false;
|
||||
}
|
||||
}
|
203
source/io/newgrounds/utils/AsyncHttp.hx
Normal file
203
source/io/newgrounds/utils/AsyncHttp.hx
Normal file
|
@ -0,0 +1,203 @@
|
|||
package io.newgrounds.utils;
|
||||
|
||||
import io.newgrounds.NGLite;
|
||||
|
||||
import haxe.Http;
|
||||
import haxe.Timer;
|
||||
|
||||
#if neko
|
||||
import neko.vm.Thread;
|
||||
#elseif java
|
||||
import java.vm.Thread;
|
||||
#elseif cpp
|
||||
import cpp.vm.Thread;
|
||||
#end
|
||||
|
||||
/**
|
||||
* Uses Threading to turn hxcpp's synchronous http requests into asynchronous processes
|
||||
*
|
||||
* @author GeoKureli
|
||||
*/
|
||||
class AsyncHttp {
|
||||
|
||||
inline static var PATH:String = "https://newgrounds.io/gateway_v3.php";
|
||||
|
||||
static public function send
|
||||
( core:NGLite
|
||||
, data:String
|
||||
, onData:String->Void
|
||||
, onError:String->Void
|
||||
, onStatus:Int->Void
|
||||
) {
|
||||
|
||||
core.logVerbose('sending: $data');
|
||||
|
||||
#if (neko || java || cpp)
|
||||
sendAsync(core, data, onData, onError, onStatus);
|
||||
#else
|
||||
sendSync(core, data, onData, onError, onStatus);
|
||||
#end
|
||||
}
|
||||
|
||||
static function sendSync
|
||||
( core:NGLite
|
||||
, data:String
|
||||
, onData:String->Void
|
||||
, onError:String->Void
|
||||
, onStatus:Int->Void
|
||||
):Void {
|
||||
|
||||
var http = new Http(PATH);
|
||||
http.setParameter("input", data);
|
||||
http.onData = onData;
|
||||
http.onError = onError;
|
||||
http.onStatus = onStatus;
|
||||
// #if js http.async = async; #end
|
||||
http.request(true);
|
||||
}
|
||||
|
||||
#if (neko || java || cpp)
|
||||
static var _deadPool:Array<AsyncHttp> = [];
|
||||
static var _livePool:Array<AsyncHttp> = [];
|
||||
static var _map:Map<Int, AsyncHttp> = new Map();
|
||||
static var _timer:Timer;
|
||||
|
||||
static var _count:Int = 0;
|
||||
|
||||
var _core:NGLite;
|
||||
var _key:Int;
|
||||
var _onData:String->Void;
|
||||
var _onError:String->Void;
|
||||
var _onStatus:Int->Void;
|
||||
var _worker:Thread;
|
||||
|
||||
public function new (core:NGLite) {
|
||||
|
||||
_core = core;
|
||||
_worker = Thread.create(sendThreaded);
|
||||
_key = _count++;
|
||||
_map[_key] = this;
|
||||
_core.logVerbose('async http created: $_key');
|
||||
}
|
||||
|
||||
function start(data:String, onData:String->Void, onError:String->Void, onStatus:Int->Void) {
|
||||
|
||||
_core.logVerbose('async http started: $_key');
|
||||
|
||||
if (_livePool.length == 0)
|
||||
startTimer();
|
||||
|
||||
_deadPool.remove(this);
|
||||
_livePool.push(this);
|
||||
|
||||
_onData = onData;
|
||||
_onError = onError;
|
||||
_onStatus = onStatus;
|
||||
_worker.sendMessage({ source:Thread.current(), args:data, key:_key, core:_core });
|
||||
}
|
||||
|
||||
function handleMessage(data:ReplyData):Void {
|
||||
|
||||
_core.logVerbose('handling message: $_key');
|
||||
|
||||
if (data.status != null) {
|
||||
|
||||
_core.logVerbose('\t- status: ${data.status}');
|
||||
_onStatus(cast data.status);
|
||||
return;
|
||||
}
|
||||
|
||||
var tempFunc:Void->Void;
|
||||
if (data.data != null) {
|
||||
|
||||
_core.logVerbose('\t- data');
|
||||
tempFunc = _onData.bind(data.data);
|
||||
|
||||
} else {
|
||||
|
||||
_core.logVerbose('\t- error');
|
||||
tempFunc = _onError.bind(data.error);
|
||||
}
|
||||
|
||||
cleanUp();
|
||||
// Delay the call until destroy so that we're more likely to use a single
|
||||
// thread on daisy-chained calls
|
||||
tempFunc();
|
||||
}
|
||||
|
||||
inline function cleanUp():Void {
|
||||
|
||||
_onData = null;
|
||||
_onError = null;
|
||||
|
||||
_deadPool.push(this);
|
||||
_livePool.remove(this);
|
||||
|
||||
if (_livePool.length == 0)
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
static function sendAsync
|
||||
( core:NGLite
|
||||
, data:String
|
||||
, onData:String->Void
|
||||
, onError:String->Void
|
||||
, onStatus:Int->Void
|
||||
):Void {
|
||||
|
||||
var http:AsyncHttp;
|
||||
if (_deadPool.length == 0)
|
||||
http = new AsyncHttp(core);
|
||||
else
|
||||
http = _deadPool[0];
|
||||
|
||||
http.start(data, onData, onError, onStatus);
|
||||
}
|
||||
|
||||
static function startTimer():Void {
|
||||
|
||||
if (_timer != null)
|
||||
return;
|
||||
|
||||
_timer = new Timer(1000 / 60.0);
|
||||
_timer.run = update;
|
||||
}
|
||||
|
||||
static function stopTimer():Void {
|
||||
|
||||
_timer.stop();
|
||||
_timer = null;
|
||||
}
|
||||
|
||||
static public function update():Void {
|
||||
|
||||
var message:ReplyData = cast Thread.readMessage(false);
|
||||
if (message != null)
|
||||
_map[message.key].handleMessage(message);
|
||||
}
|
||||
|
||||
static function sendThreaded():Void {
|
||||
|
||||
while(true) {
|
||||
|
||||
var data:LoaderData = cast Thread.readMessage(true);
|
||||
data.core.logVerbose('start message received: ${data.key}');
|
||||
|
||||
sendSync
|
||||
( data.core
|
||||
, data.args
|
||||
, function(reply ) { data.source.sendMessage({ key:data.key, data :reply }); }
|
||||
, function(error ) { data.source.sendMessage({ key:data.key, error :error }); }
|
||||
, function(status) { data.source.sendMessage({ key:data.key, status:status }); }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#end
|
||||
}
|
||||
|
||||
|
||||
#if (neko || java || cpp)
|
||||
typedef LoaderData = { source:Thread, key:Int, args:String, core:NGLite };
|
||||
typedef ReplyData = { key:Int, ?data:String, ?error:String, ?status:Null<Int> };
|
||||
#end
|
118
source/io/newgrounds/utils/Dispatcher.hx
Normal file
118
source/io/newgrounds/utils/Dispatcher.hx
Normal file
|
@ -0,0 +1,118 @@
|
|||
package io.newgrounds.utils;
|
||||
|
||||
/**
|
||||
* Basically shitty signals, but I didn't want to have external references.
|
||||
**/
|
||||
class Dispatcher {
|
||||
|
||||
var _list:Array<Void->Void>;
|
||||
var _once:Array<Void->Void>;
|
||||
|
||||
public function new() {
|
||||
|
||||
_list = new Array<Void->Void>();
|
||||
_once = new Array<Void->Void>();
|
||||
}
|
||||
|
||||
public function add(handler:Void->Void, once:Bool = false):Bool {
|
||||
|
||||
if (_list.indexOf(handler) != -1) {
|
||||
|
||||
// ---- REMOVE ONCE
|
||||
if (!once && _once.indexOf(handler) != -1)
|
||||
_once.remove(handler);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
_list.unshift(handler);
|
||||
if (once)
|
||||
_once.unshift(handler);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline public function addOnce(handler:Void->Void):Bool {
|
||||
|
||||
return add(handler, true);
|
||||
}
|
||||
|
||||
public function remove(handler:Void->Void):Bool {
|
||||
|
||||
_once.remove(handler);
|
||||
return _list.remove(handler);
|
||||
}
|
||||
|
||||
public function dispatch():Void {
|
||||
|
||||
var i = _list.length - 1;
|
||||
while(i >= 0) {
|
||||
|
||||
var handler = _list[i];
|
||||
|
||||
if (_once.remove(handler))
|
||||
_list.remove(handler);
|
||||
|
||||
handler();
|
||||
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TypedDispatcher<T> {
|
||||
|
||||
var _list:Array<T->Void>;
|
||||
var _once:Array<T->Void>;
|
||||
|
||||
public function new() {
|
||||
|
||||
_list = new Array<T->Void>();
|
||||
_once = new Array<T->Void>();
|
||||
}
|
||||
|
||||
public function add(handler:T->Void, once:Bool = false):Bool {
|
||||
|
||||
if (_list.indexOf(handler) != -1) {
|
||||
|
||||
// ---- REMOVE ONCE
|
||||
if (!once && _once.indexOf(handler) != -1)
|
||||
_once.remove(handler);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
_list.unshift(handler);
|
||||
if (once)
|
||||
_once.unshift(handler);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline public function addOnce(handler:T->Void):Bool {
|
||||
|
||||
return add(handler, true);
|
||||
}
|
||||
|
||||
public function remove(handler:T->Void):Bool {
|
||||
|
||||
_once.remove(handler);
|
||||
return _list.remove(handler);
|
||||
}
|
||||
|
||||
public function dispatch(arg:T):Void {
|
||||
|
||||
var i = _list.length - 1;
|
||||
while(i >= 0) {
|
||||
|
||||
var handler = _list[i];
|
||||
|
||||
if (_once.remove(handler))
|
||||
_list.remove(handler);
|
||||
|
||||
handler(arg);
|
||||
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue