How to detect Adobe Flash crash reason? - actionscript-3

Edit02: The reason why the flash crash because the wrong distance cause the programm to divide something by 0 - well I found the error on my own, but my question still is, is there something like a crashreport that you can add / edit?
Edit: I found the bug in the script its located here
if(baseVerticies[vertX]-boneObj.x < 0){
distance = distance*-1;
}
at one point it generates the wrong distance, but this is not what the program causes to crash, I know how to solve the bug but it would still be interesting how to add functions to detect flash crash reasons
OLD:
Hey I am currently testing the CS6 Flash demo but one function always kills flash and I dont get any error messages, so my question is ... how can I hunt the bug down?
The only things I know: it crashes when I call a certain function (the one which I posted below) - it doesnt crash on the first call ... more like on the third or second
is there any way to add debugEvents or something als that would be usefull to track the error?
thx in advance =)
public function rotateBone(boneObj : Bone, point : Point){
//rotates the boneGrafik
boneObj.rotation = (point.x+boneObj.old_rotation)/2;
if(axis == "horizontal"){
var firstV : int = selected_bone*(squares+1)*2;
var lastV : int = selected_bone*(squares+1)*2 + squares*2;
var radius : Number = Math.sqrt((verticies[lastV]-verticies[firstV])*(verticies[lastV]-verticies[firstV])+
(verticies[lastV+1]-verticies[firstV+1])*(verticies[lastV+1]-verticies[firstV+1]));
//will be exectued for every single vertex
for(var s = 0; s<=squares; s++){
var vertX : int = selected_bone * (squares+1) * 2 + s*2;
var distance : Number = Math.sqrt((verticies[vertX]-boneObj.x)*(verticies[vertX]-boneObj.x)+
(verticies[vertX+1]-boneObj.y)*(verticies[vertX+1]-boneObj.y));
//calculates Vector
var rads:Number = boneObj.rotation / 180 * Math.PI;
var p:Point = new Point();
p.x=Math.cos(rads);
p.y=Math.sin(rads);
//baseMesh is used in order to check if the vertex pos is positiv / negative
if(baseVerticies[vertX]-boneObj.x < 0){
distance = distance*-1;
}
verticies[vertX] = boneObj.x + p.x * radius * (distance/radius);
verticies[vertX+1] = boneObj.y + p.y * radius * (distance/radius);
}
}else if (axis == "vertical"){
for(var r = 0; r<=rows; r++){
vertX = r * (squares+1) * 2 + selected_bone * 2;
}
}
updateMesh();
}

I'm not sure what you mean it crashes without exception. Are you testing in the browser? If so, then you could download and install the appropriate Flash debugger player from the list here: http://www.adobe.com/support/flashplayer/downloads.html
This should give you an error window whenever FlashPlayer crashes, which will contain debugging information. It will also write crashes and debugging data to a log file. There's more info on that here: http://helpx.adobe.com/flash-player/kb/configure-debugger-version-flash-player.html
Finally, if you want to catch and handle otherwise unhandled exceptions, you can do that in FP10.1 and later. Here are the Adobe docs about it: http://help.adobe.com/en_US/FlashPlatform/beta/reference/actionscript/3/flash/events/UncaughtErrorEvent.html, and here is the sample code from that page:
package
{
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.ErrorEvent;
import flash.events.UncaughtErrorEvent;
import flash.net.URLRequest;
public class LoaderUncaughtErrorEventExample extends Sprite
{
private var ldr:Loader;
public function LoaderUncaughtErrorEventExample()
{
ldr = new Loader();
ldr.load(new URLRequest("child.swf"));
ldr.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, uncaughtErrorHandler);
}
private function uncaughtErrorHandler(event:UncaughtErrorEvent):void
{
if (event.error is Error)
{
var error:Error = event.error as Error;
// do something with the error
}
else if (event.error is ErrorEvent)
{
var errorEvent:ErrorEvent = event.error as ErrorEvent;
// do something with the error
}
else
{
// a non-Error, non-ErrorEvent type was thrown and uncaught
}
}
}
}

Related

TouchEvent.TOUCH_BEGIN, onTouchBegin Freezes after several Reloads

I am building an Adobe Air AS3 IOS and Android App, in which i have a movie clip in the center of the stage. When you start touching this movie clip, you can move it all around the stage.
This is how i'm doing so :
Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
MC_M1.alpha = 1;
MC_M1.addEventListener(Event.ENTER_FRAME, ifHitAct);
MC_M1.addEventListener(TouchEvent.TOUCH_BEGIN, onTouchBegin);
MC_M1.x = 0.516 * gameIntro.stageWidthToUse;
MC_M1.y = 0.75 * gameIntro.stageHeightToUse;
MC_M1.height = 0.2 * gameIntro.stageHeightToUse;
MC_M1.width = MC_M1.height / 1.4;
gameIntro.STAGE.stage.addChildAt(MC_M1,1);
function onTouchBegin(event:TouchEvent)
{
trace("TouchBegin");
if (touchMoveID != 0)
{
trace("It Did Not");
return;
}
touchMoveID = event.touchPointID;
gameIntro.STAGE.stage.addEventListener(TouchEvent.TOUCH_MOVE, onTouchMove);
gameIntro.STAGE.stage.addEventListener(TouchEvent.TOUCH_END, onTouchEnd);
}
function onTouchMove(event:TouchEvent)
{
if (event.touchPointID != touchMoveID)
{
return;
}
//trace("Moving")
MC_M1.x = event.stageX;
MC_M1.y = event.stageY;
}
function onTouchEnd(event:TouchEvent)
{
if (event.touchPointID != touchMoveID)
{
return;
}
//trace("Ending");
touchMoveID = 0;
gameIntro.STAGE.stage.removeEventListener(TouchEvent.TOUCH_MOVE, onTouchMove);
gameIntro.STAGE.stage.removeEventListener(TouchEvent.TOUCH_END, onTouchEnd);
}
When the player actually looses the game, what i am actually doing is the following :
MC_M1.removeEventListener(Event.ENTER_FRAME , ifHitAct);
MC_M1.removeEventListener(TouchEvent.TOUCH_BEGIN , onTouchBegin);
gameIntro.STAGE.stage.removeChild(MC_M1);
MC_M1.alpha = 0;
isDead = 1;
replayButToUse.x = 0.127 * gameIntro.stageWidthToUse;
replayButToUse.y = 0.91 * gameIntro.stageHeightToUse;
replayButToUse.addEventListener(MouseEvent.CLICK, gotoIntro);
This is all happening in a class called : introClassToUse.
So when the users looses, he will get a replay button, and when he clicks it, he will go back to the same class and reload everything, using the following code :
function gotoIntro(event:MouseEvent):void
{
replayButToUse.removeEventListener(MouseEvent.CLICK, gotoIntro);
replayButToUse.alpha = 0;
replayButToUse.removeEventListener(MouseEvent.CLICK, gotoIntro);
stop();
var reload:introClassToUse = new introClassToUse();
}
And so everything loads back up and the game restarts. My problem is, i'm facing a very weird behavior when i tend to replay the game more than 2-3 times. The MC_M1 just stops listening to any touch event, but keeps on listening to ENTER_FRAME events, in which i keep touching the MC_M1 but it seems to not respond to it. I even debugged it remotely from my iPhone, for the first couple of replays, i can see the trace("TouchBegin"); with it's outcome, it was showing me TouchBegin, but after a few replays, the touch events just froze. What am i missing?
Any help is really appreciated, i'm new in AS3, i need to learn so i could manage more
Edit 1 :
I have no code on any frame, i just have lots of AS Classes.
The fla file is linked to an AS Class called gameIntro. In this class, i have linked the following :
- STAGE is an object of type Stage.
- gameIntro.STAGE = stage
Later on, when the user clicks a play button, i call the class introClassToUse. This class has all the game functionalities. All the code present above is in introClassToUse. When the user looses and clicks the replay button, he will go to "goToIntro" function, im which i recall the introClassToUse.
It's all working fine, with several other timers implemented and all, the only problem is that after several replays, the MC_M1 just freezes over
I am removing the MC_M1 each time the user looses and re-add them when i call back the introClassToUse, because i tried to use the .visible property, it didn't work at all ( this is why i am using the gameIntro.STAGE.stage.removeChild(MC_M1)
I know the question is old but maybe someone is still wondering what is going on here (like me).
There are lot of problems in you code but I thing the root of your problem starts here:
function gotoIntro(event:MouseEvent):void{
//...
var reload:introClassToUse = new introClassToUse();
}
It is usually unwanted behavior if simply creating an instance does more than nothing to your program and you don't even need to assign it to variable in this case.
You mentioned this code is located in your introClassToUse class. This basically means that you are creating new instance of your game inside old one and this seem to be completely awry.
You should consider using only instance properties in your class definition and create new introClassToUse() in external classes;
You didn't include many important details about your code like
How the whole class structures look like - for example you can't place line like MC_M1.addEventListener(Event.ENTER_FRAME, ifHitAct);in the scope of your class so obviously you have this in some function and we don't know when and from where it is called.
Where and how your variables are declared, and assigned. It's hard to tell if your MC_M1 is property of an instance or a class, is it internal/public/private/...
Do you link library symbols to your classes or acquire it from stage.
There could be many things that could give you such result. Based on what you wrote I've reproduced behavior similar to what you've describe but using mouse event and a dummy loose condition. This ends the game each time you drop the mc partially outside right edge of the sage, show restart button and starts again if you click it (basically it's mostly your code). It works fine for about 10s and than suddely you can't move the mc anymore. The frame event is still tracing out but touch/mouse is not.
How can it be? I suspect that you could remove only listeners somewhere and have invisible mc stuck on the new one. And this could be easy overlooked, especially if you using static properties. Again we don't even know where is your movie clip coming from so we can only guess what is happening whit your code but I've tried to take the example simple this is how I did it. The problem may lay in some completely different place but you can guess for all scenarios.
Document class of the project - GameIntro.as
package
{
import flash.display.Sprite;
public class GameIntro extends Sprite
{
//Document class. this need to be compiled with strict mode off.
public function GameIntro() {
GameIntro.STAGE = stage;
GameIntro.stageWidthToUse = stage.stageWidth;
GameIntro.stageHeightToUse = stage.stageHeight;
var intro:IntroClassToUse = new IntroClassToUse();
stage.addChild(intro);
}
}
}
IntroClassToUse.as
package
{
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.TimerEvent;
import flash.utils.Timer;
/**
* You need to have library symbol linked to this class in .fla with two mcs -
* mcFromLibrarySymbol (dragable) and repButton (reapatButton)
*/
public class IntroClassToUse extends MovieClip
{
var t = 0; //timer ticks
var fc:uint = 0; //frames counter
var isDead = 0;
var mc;
static var repButton;
var logicContex:Timer = new Timer(30);
public function IntroClassToUse() {
trace("toUse", GameIntro.stageWidthToUse);
mc = mcFromLibrarySymbol;
if(!repButton) repButton = repButtonX;
logicContex.addEventListener(TimerEvent.TIMER, logicInterval);
logicContex.start();
init();
}
internal function init() {
trace("init");
mc.alpha = 1;
mc.addEventListener(Event.ENTER_FRAME, onFrame);
mc.addEventListener(MouseEvent.MOUSE_DOWN, onMDown);
mc.x = 0.516 * GameIntro.stageWidthToUse;
mc.y = 0.75 * GameIntro.stageHeightToUse;
mc.height = 0.2 * GameIntro.stageHeightToUse;
mc.width = mc.height / 1.4;
GameIntro.STAGE.stage.addChildAt(mc, 1);
}
internal function onLoose() {
trace("onLoose");
mc.removeEventListener(Event.ENTER_FRAME , onFrame);
mc.removeEventListener(MouseEvent.MOUSE_DOWN, onMDown);
GameIntro.STAGE.stage.removeChild(mc);
mc.alpha = 0;
isDead = 1;
repButton.x = 0.127 * GameIntro.stageWidthToUse;
repButton.y = 0.91 * GameIntro.stageHeightToUse;
repButton.addEventListener(MouseEvent.CLICK, onReplay);
repButton.alpha = 1;
}
internal function onReplay(e:MouseEvent):void {
trace("onReplay");
repButton.removeEventListener(MouseEvent.CLICK, onReplay);
repButton.alpha = 0;
stop();
new IntroClassToUse();
}
internal function onMDown(e:MouseEvent):void {
trace("mouseDow");
GameIntro.STAGE.stage.addEventListener(MouseEvent.MOUSE_MOVE, onMMove);
GameIntro.STAGE.stage.addEventListener(MouseEvent.MOUSE_UP, onMUp);
}
internal function onMMove(e:MouseEvent):void {
mc.x = e.stageX;
mc.y = e.stageY;
}
//you loose the game if you release you mc with part of it over rigth stage edge.
internal function onMUp(e:MouseEvent):void {
trace("mouseUp");
GameIntro.STAGE.stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMMove);
GameIntro.STAGE.stage.removeEventListener(MouseEvent.MOUSE_UP, onMUp);
trace("Stage:", GameIntro.STAGE.numChildren);
if (mc.x + mc.width > GameIntro.STAGE.stageWidth) onLoose();
}
internal function onFrame(e:Event):void {
trace("frames", fc++);
}
internal function logicInterval(e:TimerEvent):void {
if (t++ < 300 || !isDead) return;
init();
mc.alpha = 0;
mc.removeEventListener(MouseEvent.MOUSE_DOWN, onMDown);
isDead = 0;
}
}
}

as3 1119: Access of possibly undefined property getters/setters

It would be amazing if someone could expand on the current answer, Thanks.
full error
Line 22 1119: Access of possibly undefined property CharacterX through a reference with static type flash.display:DisplayObject.
I'm trying to set a variable for the object shark, that is already defined in the object character
First time using setters in flash, so I might not be doing this right.
code I'm using to set the variable I tried to comment out the stuff I thought was irrelevant to this issue, not actually commented out in real code.
var character:Character;
//var bullet:Bullet=null;
//var bullets:Array = new Array();
//var enemies:Array = new Array();
//character=new Character(bullets);
addChild(character);
var shark:Shark=new Shark();
addChild(shark);
//var enemy:Enemy=null;
////var i:int;
//for (i=0; i<10; i++) {
//enemy = new Enemy(Math.random()*stage.stageWidth, Math.random()*stage.stageHeight);
//addChild(enemy);
// enemies.push(enemy);
//}
//stage.addEventListener(Event.ENTER_FRAME, colTest);
//function colTest(e:Event ):void {
// if(character.hitTestObject(turtle)){
// character.gotoAndStop("Turtle");
// }
//}
shark.setT(character.x, character.y)
class in which I'm attempting to define a variable using the function above.
package
{
import flash.display.*;
import flash.events.*;
public class Shark extends MovieClip
{
var CharacterX:Number = 0;
var CharacterY:Number = 0;
public function Shark()
{
this.x = 300;
this.y = 200;
addEventListener(Event.ENTER_FRAME,playGame);
}
public function setT(characterx:Number,charactery:Number){
CharacterX = characterx - this.x;
CharacterY = charactery - this.y;
}
function playGame(event:Event):void
{
var ease:int = 20;
var speed:int = 10;
var targetX:int = root.CharacterX - this.x;
var targetY:int = root.CharacterY - this.y;
var rotation = Math.atan2(targetY,targetX) * 180 / Math.PI;
cut code off here, didn't want to make a code dump can get you anything that might be relevant just ask.
Here is a pastebin of all of the code if it might help,
Shark class:
Actions on Frame 1:
Character class
Let me start by saying I can't spot the exact problem here, but I have some ideas. Your error 1999 says that something of the type display object is trying to change your variable. This happens a lot when you use parent.myMethod() because parent is typed as display object. You would fix this by typecasting like (parent as MovieClip).myMethod
In your case I don't see the exact source of this problem. But you could try using this.characterX in your setT function

Prevent Movie Clip Overlapsing

I'm creating a screen saver which requires a movie clip to be loaded to the stage at a random position based on the size of the screen, then fade out (which I have all of the animation within a movie clip as tweens)
I've hit a road block and can't figure out how to prevent the movie clips from overlapsing on top of each other. If anything, I'd like for them to appear at another random spot that does not cause the overlapse.
here is all of the code for my project:
stop();
import flash.display.MovieClip;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.events.Event;
import flash.display.Stage;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
var greystoneLogos:Array = new Array ;
var countTimeArray:Array = new Array ;
var previousLogos:Array = new Array ;
var xpoint:int;
var ypoint:int;
function getNewSymbols()
{
previousLogos = new Array ;
greystoneLogos = new Array ;
var i:int;
for (i=0; i < 3; i++)
{
greystoneLogos[i] = new GreystoneLogo1();
greystoneLogos[i].width = 100;
greystoneLogos[i].height = 60;
addSymbolToStage(greystoneLogos[i],i*2000);
}
}
getNewSymbols();
function addSymbolToStage(currentLogo:MovieClip,waitTime:int)
{
var i3:int;
var i4:int;
var logoBoundaries:Array = new Array()
var XandY:Array = new Array()
for (i3=0; i3 < greystoneLogos.length; i3++)
{
if (greystoneLogos[i3] !== currentLogo)
{
xpoint = randomRange(this.stage.stageWidth - (currentLogo.width * 4.8));
ypoint = randomRange(this.stage.stageHeight - (currentLogo.height * 6.9));
logoBoundaries = getOffDimensions(currentLogo)
for (i4=0; i4 < logoBoundaries.length; i4++)
{
XandY = logoBoundaries[i4].split(":")
while ((xpoint <= (Number(XandY[0]) + Number(currentLogo.width * 4.8)) && xpoint >= (Number(XandY[0]) - Number(currentLogo.width * 4.8))) && (ypoint <= (Number(XandY[1]) + Number(currentLogo.height * 6.9)) && ypoint >= (Number(XandY[1]) - Number(currentLogo.height * 6.9)))){
xpoint = randomRange(this.stage.stageWidth - (currentLogo.width * 4.8));
trace(XandY[0] + " And " + (Number(currentLogo.width * 4.8)))
trace(xpoint + " And " + (Number(XandY[0]) + Number(currentLogo.width * 4.8)))
ypoint = randomRange(this.stage.stageHeight - (currentLogo.height * 6.9));
}
}
}
else
{
continue;
}
}
previousLogos.push(currentLogo);
currentLogo.x = xpoint;
currentLogo.y = ypoint;
stage.addChild(currentLogo);
currentLogo.gotoAndStop(1);
var countTime:Timer = new Timer(waitTime,1);
countTime.addEventListener(TimerEvent.TIMER, function(){
currentLogo.gotoAndPlay(1);
currentLogo.addFrameScript ( currentLogo.totalFrames - 1 , function(){
currentLogo.stop()
stage.removeChild(currentLogo)
if(stage.numChildren <= 1){
getNewSymbols();
}
}) ;
});
countTime.start();
}
function getOffDimensions(currentLogo:MovieClip){
var i3:int;
var tempArr:Array = new Array()
for (i3=0; i3 < greystoneLogos.length; i3++)
{
if (greystoneLogos[i3] !== currentLogo){
tempArr[i3]=greystoneLogos[i3].x +":"+ greystoneLogos[i3].y
}
}
return tempArr
}
function randomRange(max:Number, min:Number = 0):Number
{
return Math.random() * (max - min) + min;
}
There are also may be a handful of unused variables from multiple things I've been trying out.
The code that I posted, will make the movie clip appear at a random spot based on the last movie clip that came up. So let's say we have 3 movie clips (the user will be able to change how many of the clips get displayed) 1 appears at 0,0 the other at 400,400 and the last one appears at 10,10 because I have no way of saving the previous values to compare in the while loop.
I hope this clarifies it a tad more
EDIT:
Based on a function shown below, I've added this:
for (i3=0; i3 < greystoneLogos.length; i3++)
{
if (greystoneLogos[i3] !== currentLogo && greystoneLogos[i3] != null)
{
while(currentLogo.hitTestObject(greystoneLogos[i3]) == true){
xpoint = randomRange(this.stage.stageWidth - (currentLogo.width));
ypoint = randomRange(this.stage.stageHeight - (currentLogo.height));
i3 = 0
}
}else{
continue;
}
}
Which results in a rather bad loop as well as the logo's still overlap above each other
The quickest way I can think to solve this is to do a hit test (http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/DisplayObject.html#hitTestObject()) on the new MovieClip and if it returns true, run the placement code again
Ok, here's the implementation of this quickest way.
You can track your present displayObject by the .numChildren property. You don't even need an array of your logos or things. Let's say you have a function that adds new movieClip to your screen. It knows how many clips you can have (MovieClipDummy is just my testing class - it draws a circle with a specified radius. It should be replaced by your objects).
private function addAnotherMovieClip(): void {
if (_currentDummmiesCount < _dummmiesCount) {
var newDummy: MovieClipDummy = new MovieClipDummy(90);
addChild(newDummy);
//If we get a stackoverflow error (see below)
//we just remove our object from the screen as it will most likely just won't fit
if (checkForEmptySpace(newDummy) != "") {
removeChild(newDummy);
return;
}
} else {
//Do nothing or do something
}
}
It calls another function, checkForEmptySpace, which tries to place your movieclip so it won't overlap with other objects. Here it is:
private function checkForEmptySpace(newClip: Sprite): String {
//you should store your screen width somewhere.
//you can also call stage.stageWidth instead, but first make sure that you
//always have a link to the stage or it will throw an error
newClip.x = Math.random() * _screenWidth;
newClip.y = Math.random() * _screenHeight;
//===========================
//Important part - we try to check all the present children of our container
//numChildren is a property of the DisplayObjectContainer
for (var i: int; i < numChildren; i++) {
//We need a try here because we will get a StackOverflow error if there's no empty space left
try {
//We need to check if our current display object, received with getChildAt()
//is not the same as the one we've just added to the screen
//And if our new object intersects with ANY other object on the stage - we
//call THIS function once again.
//We do recursion because we can easily catch an error and remove this object from
//the screen
if (newClip != getChildAt(i) && newClip.hitTestObject(getChildAt(i))) {
//If our recursive function returns an error - we should pass it further
if (checkForEmptySpace(newClip) != "") {
return "error";
}
}
//The only error that can go here is stackoverflow error. So when we get one
//we return this error string
} catch (error: Error) {
trace(error);
return "error";
}
}
//We only return this empty string if we don't have stackoverflow error
//so there's possibly no space left for another movieclip
return "";
}
You can do it without recursion, but you will have to check for empty space with another logic.
And, you can do it more "professionally" by using Minkowski addition. You should consider storing an array of "boundary" points of your movieclips (let's say every movieclip is a rectangle) and when you add a new object to the screen, you calculate this Minkowsky addition. It will have some "free" spots on your screen which represent any possible coordinates of your new movieclip. It's pretty interesting to implement something like that because the accuracy will be phenomenal. But if you don't have time - just use that recursive placement function

Handle VerifyError: Error #1014 when loading swfs using AS3

We are making a system that has a main swf for the application, and loads separate tools from separate swfs -- there will be versioning issues in the future since the separate swfs are coming from a cms (especially now since we're still developing, but also in the future when other developers might create an incompatible tool). I'm trying hard to prevent them as much as possible but I'd really like to be able to display a message to the user of the system when an incompatible swf is loaded.
This would mean that we need to catch that VerifyError or at least determine the loading failed for some reason - I've got no idea how to handle that at the moment. I suspect it might be possible using 10.1 and the uncaughtError system, but we are currently targeting flash player 10. Does anyone have a good idea? (we are already handling IOErrorEvent.IO_ERROR)
UPDATE: I've built a solution that scans the bytecode before importing, looks like that will work. I'll post the solution later.
The best way to do this is by using one of the libraries bhups suggested. I used senocular's for the next example.
Also, because the senocular's library provides only basic operations for the parsed SWF you may need the SWF Format Spec (adobe.com/devnet/swf/pdf/swf_file_format_spec_v10.pdf) to get the info you want out of the loaded SWF.
The next example lists all the class names from a loaded SWF:
package swf
{
import flash.events.Event;
import flash.net.URLRequest;
import flash.net.URLStream;
import flash.utils.ByteArray;
import flash.utils.Endian;
import swf.SWFReader;
public class GetSWFInfo
{
private var swfInfo:SWFReader;
public function GetSWFInfo()
{
var urlRequest:URLRequest = new URLRequest("theswf.swf");
var loader:URLStream = new URLStream();
loader.load(urlRequest);
loader.addEventListener(Event.COMPLETE, onComplete);
}
public function onComplete(e:Event):void {
var recivedByteArray :ByteArray = new ByteArray();
URLStream(e.currentTarget).readBytes(recivedByteArray);
//create a new instance of SWFReader
swfInfo = new SWFReader();
//readTag it's a callback function that will be called when a tag is read during the SWF parse process.
//read more on tags in the SWF specification document
swfInfo.tagCallback = readTag;
//start parsing
swfInfo.parse(recivedByteArray);
}
public function readTag(tag:uint, bytes:ByteArray):void {
//76 it's the tag type for SymbolClass tag
//read more in the SWF specification document
if (76 == tag) {
var classesArray:Array = new Array();
var symbolsNumber:uint = 0;
var currentId:uint = 0;
bytes.endian = Endian.LITTLE_ENDIAN;
//read the symbols Number
//again read more in the SWF specification document
symbolsNumber = bytes.readShort();
bytes.position = 4;
while (true) {
var i:uint = bytes.position;
//every string name ends with a null byte
//again read more in the SWF specification document
while(bytes[i] != 0) i++;
var readAmount:uint = i - bytes.position;
classesArray.push(bytes.readUTFBytes(readAmount));
//the last ID is always the base class Id, and it's 0
currentId=bytes.readUnsignedShort();
bytes.position++;
if (currentId==0) {
break;
}
}
//this two should be equal
trace(classesArray.length + 1);//the number of elements in the classesArray
trace(symbolsNumber);//the number of classes retrived from the SWF
//list the names
var name:String;
for each (name in classesArray) {
trace(name);
}
//now you have an array with all the class names that you can use to compare
}
}
}
}
I did misunderstand what you are trying to do.
Well, actually, I guess there is no handler for verify error and to detect it, you have to fight with byte-codes.
By the way, I have and idea which is not the very answer for your question but may helps you.
a 3rd party swf is depending on a class that should be in my swf -- if that class is missing I get the VerifyError.
From this point, I can advice that if you link the 'missing class' into your swf and load the 3rd party swf into ApplicationDomain.currentDomain or new ApplicationDomain(ApplicationDomain.currentDomain), you can avoid the 'Verify Error'.
(This is because the flash player will find the diffinition of the missing class in the parent swf.)
Here is my sample code which loads a swf with verify error(http://teionclub.com/test/xml/main.swf).
Avoiding VerifyError - wonderfl build flash online
I think there is a way to workaround this issue.
load the swf using a URLLoader or
URLStream into a ByteArray.
Use any open source library for parsing SWF binary like this or
this.
check if it verifies that the whole byte array represents valid
SWF file.
If above test succeeds then load this ByteArray into loader using
loadBytes method.
Else show user that this is not working.
Disclaimer: A binary file can be a valid SWF still might not be render-able, but with this you can discard all the invalid SWFs or any other formats whose extension are changed to swf.
I've worked with that kind of application in the past but I think it would be better to fix the SWF loaded rather than handling VerifyError. VeriyError indicates that the SWF loaded is corrupted or malformed.
And it's natural that the SWF itself is malformed rather than that the SWF is corrupted during the transfer. I guess you are trying to load png or other format named ".swf" or the SWF is generated by some software other than Flex compiler or Flash such as swfmill(In the latter case, there would be a bug in that software).
To finally answer my own question, this is the utility class I've been using to detect possible errors. I load the SWF as a bytearray and scan the contents before loading it as an actual MovieClip.
As you can see my code heavily depends on the com.segfaultlabs.swfutils package
Important: I've stopped using this method of preventing errors, opting for the more manual approach of checking the files by actually trying to load them and see if they work. This is because the utility is not complete, and my current knowledge of the ABC format is not good enough to make sure I can develop a check that will always be correct.
Posting my code here as starting point for others who want to take a stab at it :-)
package nl.ijsfontein.utils
{
import com.segfaultlabs.swfutils.ABC.ABCCPool;
import com.segfaultlabs.swfutils.ABC.ABCClass;
import com.segfaultlabs.swfutils.ABC.ABCInstance;
import com.segfaultlabs.swfutils.ABC.ABCMethodInfo;
import com.segfaultlabs.swfutils.ABC.ABCMultiname;
import com.segfaultlabs.swfutils.ABC.ABCParser;
import com.segfaultlabs.swfutils.ABC.ABCTraitConstSlot;
import com.segfaultlabs.swfutils.ABC.ABCTraitsInfo;
import com.segfaultlabs.swfutils.ABC.ABCinfo;
import com.segfaultlabs.swfutils.SWFDataInput;
import com.segfaultlabs.swfutils.SWFFile;
import flash.system.ApplicationDomain;
import flash.utils.ByteArray;
/**
* utility to see which classes a swf uses, but doesn't contain itself
* - this can be used to detect possible VerifyErrors before they happen.
*/
public class SwfDependencyUtil
{
public function SwfDependencyUtil()
{
}
// return null if ok, or name of needed class if external depencendy
private static function resolveSuper(abc:ABCinfo, superClass:String):String
{
//if (superClass.indexOf("flash.") == 0 || superClass.indexOf("*") == 0 || superClass.indexOf("Object") == 0)
if (superClass.indexOf("*") == 0)
{
trace(' super: ' + superClass + " (ignore)");
}
else
{
var superClassClass:ABCClass = null;
for each ( var c:ABCClass in abc.classes )
{
if (c.name == superClass)
{
superClassClass = c;
}
}
if (superClassClass)
{
trace(' super: ' + superClass + " (resolved internally)");
return resolveSuper(abc, superClassClass.iref.base);
}
else
{
trace(' super: ' + superClass + " (NOTFOUND)");
return superClass;
}
}
return null;
}
/*
* checks: classes, superclasses, static variables, member variables
* TODO: function arguments
* won't check: method bodies
*
* TODO: refactor to multiple methods
*/
public static function getDependencies(swfBytes:ByteArray):Array /* of String */
{
var result:Array = [];
swfBytes.position = 0;
var swfr:SWFFile = new SWFFile(swfBytes);
var arr:Array;
if ( swfr.compressed )
{
swfr.dataInput = swfr.uncompress();
swfr.readHeader();
};
arr = swfr.parseTags();
if ( arr[82] != null )
{
var abc:ABCinfo = new ABCinfo();
var cpool:ABCCPool = new ABCCPool();
var abcparse:ABCParser = new ABCParser();
abcparse.readMethodBytes = true;
abcparse.readExceptions = false;
for ( var j:int = 0; j < arr[82].length; j += 1 )
{
swfr.dataInstance.position = arr[82][j].position;
try
{
abcparse.parse( swfr.dataInput as SWFDataInput, abc, cpool, new FakeLogger() );
for each ( var c:ABCClass in abc.classes )
{
trace('class:', c.name);
var superClass:String = c.iref.base;
var dependency:String = resolveSuper(abc, superClass);
if (dependency)
{
result.push(dependency);
}
for each (var mn:ABCMultiname in c.iref.interfaces)
{
var interfaceName:String = mn.nsset[0] != "" ? mn.nsset[0] + "::" + mn.name : mn.name;
var interfaceDependency:String = resolveSuper(abc, interfaceName);
if (interfaceDependency)
{
result.push(interfaceDependency);
}
}
for each (var ti:ABCTraitsInfo in c.traits)
{
if (ti is ABCTraitConstSlot)
{
var constName:String
if (QName(ABCTraitConstSlot(ti).type).uri)
{
constName = QName(ABCTraitConstSlot(ti).type).uri + "::" + QName(ABCTraitConstSlot(ti).type).localName
}
else
{
constName = QName(ABCTraitConstSlot(ti).type).localName
}
var constDependency:String = resolveSuper(abc, constName);
if (constDependency)
{
result.push(constDependency);
}
}
else if (ti is ABCMethodInfo)
{
trace('method', ABCMethodInfo(ti).name);
} else
{
trace(ti);
}
// trace(ti.type.localName);
}
// const (static?) members: c.traits
}
for each ( var i:ABCInstance in abc.instances )
{
// trace(i);
for each (var instanceTi:ABCTraitsInfo in i.traits)
{
if (instanceTi is ABCTraitConstSlot)
{
trace('instance:', createClassNameFromQname(ABCTraitConstSlot(instanceTi).type));
var csdep:String = resolveSuper(abc, createClassNameFromQname(ABCTraitConstSlot(instanceTi).type));
if (csdep)
{
result.push(csdep);
}
}
else if (instanceTi is ABCMethodInfo)
{
}
else
{
trace('unexpected trait type');
}
}
}
abc.dispose();
}
catch ( e:Error )
{
trace( " Error ",e.getStackTrace() );
};
};
cpool.dispose();
}
else
{
trace("No DoABC block... ;(");
}
return result;
}
private static function createClassNameFromQname(qn:QName):String
{
var result:String
if (qn.uri)
{
result = qn.uri + "::" + qn.localName
}
else
{
result = qn.localName
}
return result;
}
public static function getUnsatisfiedDependencies(swfBytes:ByteArray):Array /* of String */
{
var result:Array = [];
var dependencies:Array = SwfDependencyUtil.getDependencies(swfBytes)
for each (var dependency:String in dependencies)
{
if (ApplicationDomain.currentDomain.hasDefinition(dependency))
{
trace('ok: ', dependency);
}
else
{
trace('ERROR: unsatisfied dependency: ', dependency);
result.push(dependency);
}
}
return result;
}
}
}

Debugging a release only flash issue

I've got an Adobe Flash 10 program that freezes in certain cases, however only when running under a release version of the flash player. With the debug version, the application works fine.
What are the best approaches to debugging such issues? I considered installing the release player on my computer and trying to set some kind of non-graphical method of output up (I guess there's some way to write a log file or similar?), however I see no way to have both the release and debug versions installed anyway :( .
EDIT: Ok I managed to replace my version of flash player with the release version, and no freeze...so what I know so far is:
Flash: Debug Release
Vista 32: works works
XP PRO 32: works* freeze
I gave them the debug players I had to test this
Hmm, seeming less and less like an error in my code and more like a bug in the player (10.0.45.2 in all cases)... At the very least id like to see the callstack at the point it freezes. Is there some way to do that without requiring them to install various bits and pieces, e.g. by letting flash write out a log.txt or something with a "trace" like function I can insert in the code in question?
EDIT2: I just gave the swf to another person with XP 32bit, same results :(
EDIT3:
Ok, through extensive use of flash.external.ExternalInterface.call("alert", "..."); I managed to find the exact line causing the problem (I also improved exception handling code so rather than freeze it told me there was an "unhandled" exception). The problem now is what on earth is flashes problem with this with the release player on some machines...
particles.push(p);
Which causes a TypeError #1034 on said platforms. Particles is a Vector.<Particle>, p is a Particle. I tested with getQualifiedClassName and got:
getQualifiedClassName(p) = ::Particle
getQualifiedClassName(particles) = __AS3__.vec::Vector.<::Particle>
Any ideas why this is a problem and what to do to make it work?
EDIT4:
Ok I seem to have solved this. The Particle class is just a simple internal class located after the package {...} in the action script file using it. I moved this into its own file (particle.as) and made it a proper public class in my package, and problem solved.
Maybe its a flash bug or maybe I missed the memo about not using internal classes in vectors or something, although if that's the case I would have expected something or other (either at compile time or with debug runtimes) to disallow it explicitly, e.g. some error on the "private var particles:Vector.<Particle>;" line. If I get a chance I guess I'll take a look at contacting the Adobe flash team concerning this or something.
Thanks for help giving debugging tips which I guess is more along the original questions lines :)
This is a long shot, but are the Particles the objects that you are clicking? If so then catching the event in the wrong phase of bubbling, and pushing event.target (assuming it to be a Particle) could cause that problem.
Whatever the problem, I have something that should help you debug. A class that creates a pseudo trace window in your SWF, much nicer than extinterfacing to javascript. I've forgotten who wrote it, but I feel like it's Senocular. I use it any time I need to get traces back from end users.
Just drop it in the default package for your project, call stage.addChild(new Output());, and then to trace call Output.trace("A message");
package {
import flash.display.Shape;
import flash.display.Sprite;
import flash.display.Stage;
import flash.display.GradientType;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Matrix;
import flash.text.TextField;
import flash.text.TextFieldType;
import flash.text.TextFormat;
import flash.text.TextFormatAlign;
import flash.text.TextFieldAutoSize;
/**
* Creates a pseudo Output panel in a publish
* swf for displaying trace statements.
* For the output panel to capture trace
* statements, you must use Output.trace()
* and add an instance to the stage:
* stage.addChild(new Output());
*
*/
public class Output extends Sprite {
private var output_txt:TextField;
private var titleBar:Sprite;
private static var instance:Output;
private static var autoExpand:Boolean = false;
private static var maxLength:int = 1000;
public function Output(outputHeight:uint = 400){
if (instance && instance.parent){
instance.parent.removeChild(this);
}
instance = this;
addChild(newOutputField(outputHeight));
addChild(newTitleBar());
addEventListener(Event.ADDED, added);
addEventListener(Event.REMOVED, removed);
}
// public methods
public static function trace(str:*):void {
if (!instance) return;
instance.output_txt.appendText(str+"\n");
if (instance.output_txt.length > maxLength) {
instance.output_txt.text = instance.output_txt.text.slice(-maxLength);
}
instance.output_txt.scrollV = instance.output_txt.maxScrollV;
if (autoExpand && !instance.output_txt.visible) instance.toggleCollapse();
}
public static function clear():void {
if (!instance) return;
instance.output_txt.text = "";
}
private function newOutputField(outputHeight:uint):TextField {
output_txt = new TextField();
//output_txt.type = TextFieldType.INPUT;
output_txt.border = true;
output_txt.borderColor = 0;
output_txt.background = true;
output_txt.backgroundColor = 0xFFFFFF;
output_txt.height = outputHeight;
var format:TextFormat = output_txt.getTextFormat();
format.font = "_sans";
output_txt.setTextFormat(format);
output_txt.defaultTextFormat = format;
return output_txt;
}
private function newTitleBar():Sprite {
var barGraphics:Shape = new Shape();
barGraphics.name = "bar";
var colors:Array = new Array(0xE0E0F0, 0xB0C0D0, 0xE0E0F0);
var alphas:Array = new Array(1, 1, 1);
var ratios:Array = new Array(0, 50, 255);
var gradientMatrix:Matrix = new Matrix();
gradientMatrix.createGradientBox(18, 18, Math.PI/2, 0, 0);
barGraphics.graphics.lineStyle(0);
barGraphics.graphics.beginGradientFill(GradientType.LINEAR, colors, alphas, ratios, gradientMatrix);
barGraphics.graphics.drawRect(0, 0, 18, 18);
var barLabel:TextField = new TextField();
barLabel.autoSize = TextFieldAutoSize.LEFT;
barLabel.selectable = false;
barLabel.text = "Output";
var format:TextFormat = barLabel.getTextFormat();
format.font = "_sans";
barLabel.setTextFormat(format);
titleBar = new Sprite();
titleBar.addChild(barGraphics);
titleBar.addChild(barLabel);
return titleBar;
}
// Event handlers
private function added(evt:Event):void {
stage.addEventListener(Event.RESIZE, fitToStage);
titleBar.addEventListener(MouseEvent.CLICK, toggleCollapse);
fitToStage();
toggleCollapse();
}
private function removed(evt:Event):void {
stage.removeEventListener(Event.RESIZE, fitToStage);
titleBar.removeEventListener(MouseEvent.CLICK, toggleCollapse);
}
private function toggleCollapse(evt:Event = null):void {
if (!instance) return;
output_txt.visible = !output_txt.visible;
fitToStage(evt);
}
private function fitToStage(evt:Event = null):void {
if (!stage) return;
output_txt.width = stage.stageWidth;
output_txt.y = stage.stageHeight - output_txt.height;
titleBar.y = (output_txt.visible) ? output_txt.y - titleBar.height : stage.stageHeight - titleBar.height;
titleBar.getChildByName("bar").width = stage.stageWidth;
}
}
}
Judging by when the freeze occurs, try to pinpoint some possibilities for what the offending code may be, and use De MonsterDebugger to check variables etc.
EDIT:
I'm pretty certain that the actual call stack is only available to you in the debug versions of the Flash Player / AIR. Still, it may be useful in the debug player to trace the stack from within the handler for the button to see if anything is out of place:
var err:Error = new Error(“An Error”);
trace(err.getStackTrace());
FYI the getStackTrace method is only available in the debug player, so there is no way to write it to a log.txt in production.