Open Mennez opened 1 year ago
Can you share the Swf or source code (or at least the code that creates the URLLoader)?
We did explicitly set 'scoreLoader.dataFormat = URLLoaderDataFormat.VARIABLES', to use the url vars. This did work perfectly for the old flash player, but does not work for the RUFFLE player in AS3 games. Hereby the class we use with the URLLoader:
package clashflash.site {
import clashflash.site.SessionEvent;
import clashflash.site.ClashDataEvent;
import flash.events.*;
import flash.utils.*;
import flash.display.*;
import flash.net.*;
import flash.system.Security;
import flash.events.IOErrorEvent;
import flash.events.HTTPStatusEvent;
public class SiteCommunicator extends EventDispatcher {
// config vars
private var millisecondsBeforeDie = 5000; // millisecond before cancel communication attempt
private var statusText:String = "Verifying, please wait..."; // use getStatusText()
// vars that we will get with receiving data
private var flashToken:String;
private var submissionURL:String;
private var saveURL:String;
public var newPosition:String = "";
public var totalPlayers:String = "";
public var yourHighest:String = "";
public var worldRecord:String = "";
public var errorMsg:String;
// private vars
private var domainLocation:String;
private var loaderInf:LoaderInfo;
private var documentURL:String;
private var scoreLoader:URLLoader = new URLLoader();
private var sessionLoader:URLLoader = new URLLoader();
private var loginLoader:URLLoader = new URLLoader();
private var profileLoader:URLLoader = new URLLoader();
private var scoresLoader:URLLoader = new URLLoader();
private var statusEvent:Event;
private var localHost:Boolean = true;
private var isMobile:Boolean = false;
private var hasResponse:Boolean = false;
private var gameIdCode:String;
// dispatches after verify has been called and all goes well
public static const STATUSTEXT_UPDATE:String = "SiteCommunicator.StatusTextUpdate";
// dispatches after submitClashScore has been called and all goes well
public static const SCORE_SUBMITTED:String = "SiteCommunicator.DoneSendingScore";
public static const SESSION_CREATED:String = "SiteCommunicator.SessionCreated";
public static const LOGGED_IN:String = "SiteCommunicator.LoggedIn";
public static const PROFILE_RETRIEVED:String = "SiteCommunicator.ProfileRetrieved";
public static const SCORES_LISTED:String = "SiteCommunicator.ScoresListed";
public static const LOGIN_FAILED:String = "SiteCommunicator.LoginFailed";
/*
Constructor
*/
public function SiteCommunicator(documentURL:String, gameIdCode:String, isMobile:Boolean = false):void {
if (documentURL.indexOf('file://') === -1 && documentURL.indexOf('localhost') === -1) this.localHost = false;
else this.documentURL = documentURL;
this.gameIdCode = gameIdCode;
scoreLoader.addEventListener(Event.COMPLETE, doneSendingAndReceiving);
scoreLoader.addEventListener(IOErrorEvent.IO_ERROR, handleIOError);
scoreLoader.addEventListener(HTTPStatusEvent.HTTP_STATUS, handleHTTPStatus);
addEventListener(SiteCommunicator.SCORE_SUBMITTED, dispatchDoneSendingAndReceiving);
// interpreter loader as Variables
scoreLoader.dataFormat = URLLoaderDataFormat.VARIABLES;
loginLoader.addEventListener(Event.COMPLETE, doneLoginRequest);
loginLoader.addEventListener(IOErrorEvent.IO_ERROR, handleIOError);
loginLoader.addEventListener(HTTPStatusEvent.HTTP_STATUS, handleHTTPStatus);
sessionLoader.addEventListener(Event.COMPLETE, doneSessionRequest);
sessionLoader.addEventListener(IOErrorEvent.IO_ERROR, handleIOError);
sessionLoader.addEventListener(HTTPStatusEvent.HTTP_STATUS, handleHTTPStatus);
profileLoader.addEventListener(Event.COMPLETE, doneProfileRequest);
profileLoader.addEventListener(IOErrorEvent.IO_ERROR, handleIOError);
profileLoader.addEventListener(HTTPStatusEvent.HTTP_STATUS, handleHTTPStatus);
scoresLoader.addEventListener(Event.COMPLETE, doneScoresRequest);
scoresLoader.addEventListener(IOErrorEvent.IO_ERROR, handleIOError);
scoresLoader.addEventListener(HTTPStatusEvent.HTTP_STATUS, handleHTTPStatus);
this.isMobile = isMobile;
}
/*
let outside classes get event names
*/
public function getScoreSubmitted():String { return SiteCommunicator.SCORE_SUBMITTED; }
public function getStatusTextUpdate():String { return SiteCommunicator.STATUSTEXT_UPDATE; }
/*
Verify with domain and get token and urls
*/
public function verify():Boolean {
if (this.localHost || isMobile || Security.pageDomain == null) return true;
else if (Security.pageDomain.indexOf('clashflash.com') != -1) {
return true;
}
return false;
}
/*
get statusText
*/
public function getStatusText():String {
return this.statusText;
}
/*
Dispatch status update event
*/
public function statusTextUpdate(str:String):void {
this.statusText = str;
dispatchEvent(new Event(SiteCommunicator.STATUSTEXT_UPDATE, true));
}
public function login(usernameOrEmail:String, password:String):void {
statusTextUpdate("Attempting to login on clashflash.com");
hasResponse = false;
var loginRequest:URLRequest = new URLRequest((this.localHost ? 'http://localhost:3000' : 'https://www.clashflash.com') + '/users/login/');
loginRequest.method = URLRequestMethod.POST;
var loginVariables:URLVariables = new URLVariables();
var emailCheck:RegExp = /\w+@\w+\./;
usernameOrEmail = usernameOrEmail.toLowerCase(); // always lower case
if (emailCheck.test(usernameOrEmail)) loginVariables.email = usernameOrEmail;
else loginVariables.username = usernameOrEmail;
loginVariables.password = password;
loginRequest.data = loginVariables;
try {
this.loginLoader.load(loginRequest);
} catch (e:Error) {
trace("Loader error during login request");
statusTextUpdate("Unable to login on clashflash server");
}
}
public function getSession(authToken:String):void {
statusTextUpdate("Requesting game session");
var sessionRequest:URLRequest = new URLRequest((this.localHost ? 'http://localhost:3000' : 'https://www.clashflash.com') + '/methods/game-sessions.session/');
sessionRequest.method = URLRequestMethod.POST;
var sessionVariables:URLVariables = new URLVariables();
sessionVariables.gameId = gameIdCode;
sessionRequest.data = sessionVariables;
try {
sessionRequest.requestHeaders = [
new URLRequestHeader("pragma", "no-cache"),
new URLRequestHeader("Authorization", "Bearer "+authToken)
];
this.sessionLoader.load(sessionRequest);
} catch (e:Error) {
trace("Loader error during the requesting of a game session");
statusTextUpdate("Unable to request a game session");
}
}
public function getProfile(userId:String):void {
statusTextUpdate("Requesting user profile");
var request:URLRequest = new URLRequest((this.localHost ? 'http://localhost:3000' : 'https://www.clashflash.com') + '/methods/users.get.profile/');
request.method = URLRequestMethod.POST;
var variables:URLVariables = new URLVariables();
variables.userId = userId;
request.data = variables;
try {
request.requestHeaders = [
new URLRequestHeader("pragma", "no-cache")
];
this.profileLoader.load(request);
} catch (e:Error) {
trace("Loader error during the requesting the user profile");
statusTextUpdate("Unable to request user profile");
}
}
public function getHighscores():void {
statusTextUpdate("Requesting highscore listing");
var request:URLRequest = new URLRequest((this.localHost ? 'http://localhost:3000' : 'https://www.clashflash.com') + '/methods/game-sessions.highscores/');
request.method = URLRequestMethod.POST;
var variables:URLVariables = new URLVariables();
variables.gameId = gameIdCode;
request.data = variables;
try {
request.requestHeaders = [
new URLRequestHeader("pragma", "no-cache")
];
this.scoresLoader.load(request);
} catch (e:Error) {
trace("Loader error during the requesting the highscore listing");
statusTextUpdate("Unable to request the highscore listing");
}
}
/*
submit score to server
*/
public function submitClashScore(score:int, gameKey:int, userId:String, token:String):String {
hasResponse = false;
var statusMessage = "Submitting score to "+(this.localHost ? "localhost" : "clashflash.com")+", please wait...";
statusTextUpdate(statusMessage);
// send score to php
var scoreRequest:URLRequest = new URLRequest((this.localHost ? 'http://localhost:3000' : 'https://www.clashflash.com') + '/methods/game-sessions.score/');
var scoreVariables:URLVariables = new URLVariables();
// initialize vars
if (gameIdCode) {
scoreVariables.score = score;
scoreVariables.gameId = gameIdCode;
scoreVariables.token = token;
scoreVariables.userId = userId;
} else scoreVariables.gameId = 'test';
// prepare to post data
scoreRequest.method = URLRequestMethod.POST;
scoreRequest.data = scoreVariables;
// send and load vars
try {
this.scoreLoader.load(scoreRequest);
} catch (e:Error) {
trace("Loader error during result send request");
statusTextUpdate("Unable to communicate with server");
}
// scoreLoader has event listener on complete: function DoneSendingAndReceiving
return statusMessage;
}
/*
Done sending score
*/
private function dispatchDoneSendingAndReceiving(e:Event):void {
trace("done sending score and receiving data: (see below)");
trace("New position: "+this.newPosition);
trace("Total players: "+this.totalPlayers);
trace("Your highest: "+this.yourHighest);
trace("Message: "+this.errorMsg);
trace("WorldRecord: "+this.worldRecord);
}
/*
Call function after sending and receiving
*/
private function doneSendingAndReceiving(e:Event):void {
var loader:URLLoader = URLLoader(e.target);
var urlVars:URLVariables = loader.data;
if (urlVars["message"] != null) {
this.errorMsg = urlVars["message"];
statusTextUpdate(urlVars["message"]);
}
if (urlVars["newPosition"] != null) this.newPosition = urlVars["newPosition"];
else this.newPosition = "";
if (urlVars["totalPlayers"] != null) this.totalPlayers = urlVars["totalPlayers"];
else this.totalPlayers = "";
if (urlVars["yourHighest"] != null) this.yourHighest = urlVars["yourHighest"];
else this.yourHighest = "";
if (urlVars["worldRecord"] != null) this.worldRecord = urlVars["worldRecord"];
else this.worldRecord = "";
dispatchEvent(new Event(SiteCommunicator.SCORE_SUBMITTED, true));
}
private function doneSessionRequest(e:Event):void {
statusTextUpdate("Game session started successfully");
var loader:URLLoader = URLLoader(e.target);
var result:Object = JSON.parse(loader.data);
dispatchEvent(new SessionEvent(SESSION_CREATED, result.token, result.tokenExpires, result.key));
}
private function doneProfileRequest(e:Event):void {
statusTextUpdate("Profile loaded");
var loader:URLLoader = URLLoader(e.target);
var result:Object = JSON.parse(loader.data);
dispatchEvent(new ClashDataEvent(PROFILE_RETRIEVED, result));
}
private function doneScoresRequest(e:Event):void {
statusTextUpdate("Highscores retrieved");
var loader:URLLoader = URLLoader(e.target);
var result:Object = JSON.parse(loader.data);
dispatchEvent(new ClashDataEvent(SCORES_LISTED, result));
}
private function doneLoginRequest(e:Event):void {
statusTextUpdate("Authentication completed");
var loader:URLLoader = URLLoader(e.target);
var result:Object = JSON.parse(loader.data);
dispatchEvent(new SessionEvent(LOGGED_IN, result.token, result.tokenExpires, result.id));
}
private function handleHTTPStatus(e:HTTPStatusEvent):void {
hasResponse = true;
trace(e);
if (e.status == 400) {
dispatchEvent(new Event(LOGIN_FAILED, true));
statusTextUpdate('Incorrect user or password');
} else if (e.status != 200) statusTextUpdate('An unexpected error ('+e.status+') occurred');
}
private function handleIOError(e:IOErrorEvent):void {
if (!hasResponse) statusTextUpdate('No connection');
}
}
}
@Aaron1011 is this enough to reproduce the issue?
Any update? Is this noted for one of missing features in the AS3 api?
Describe the bug
We are trying to communicate with our server to submit high scores in our AS3 games. We noticed that the data returned from the server is not correctly processed. Consider the following two lines of code:
In this case loader.data should contain an object with several variables. As the server has returned the following string:
"newPosition=1&totalPlayers=13&yourHighest=173744&worldRecord=193596&message=Your score has been submitted successfully!"
We expected loader.data to contain:
We trying to access the vars using the urlVars['message'] and we receive the following error in our console:
ERROR core/src/avm2/events.rs:419 Error dispatching event EventObject(EventObject { type: "complete", class: flash.events::Event, ptr: 0x1169d9d4 }) to handler FunctionObject(FunctionObject { ptr: 0xf05fdc }) : TypeError: Error #1009: Cannot access a property or method of a null object reference. (accessing field: message)
This exact code worked in the original AS3 processor and we have also noticed that it works perfectly for AS2 games. Is this a known issue or is it on the roadmap? This is the only critical issue currently preventing us from publishing our AS3 games to our website.
Expected behavior
Expect it to work exactly like it does with AS2.
Affected platform
Desktop app
Operating system
macOS Ventura 13.2.1
Browser
Google Chrome 114.0.5735.106
Additional information
No response