ruffle-rs / ruffle

A Flash Player emulator written in Rust
https://ruffle.rs
Other
15.55k stars 806 forks source link

Gray screen on Transformice #8937

Open Lord-McSweeney opened 1 year ago

Lord-McSweeney commented 1 year ago

Describe the bug

https://www.transformice.com/Transformice.swf fails to load, instead displaying a gray screen. The obfuscated game uses the unimplemented flash.profiler.showRedrawRegions (flash.profiler has not been implemented at all yet).

Expected behavior

The game can load and run.

Affected platform

Browser's extension

Operating system

Linux

Browser

Chrome 108

Additional information

This game is also multiplayer, so until Socket is implemented, the core functionality will not run.

Lord-McSweeney commented 1 year ago

This issue will probably be fixed even if flash.profiler and flash.profiler.showRedrawRegions are stubbed.

Lord-McSweeney commented 1 year ago

This also needs flash.text.StyleSheet.

Lord-McSweeney commented 1 year ago

The topmost SWF contains an obfuscated script that concatenates multiple binaryDatas into a single SWF, which makes up the main game. Here's the SWF created by them: RESULT.swf.zip

The SWF is also obfuscated (though not as much).

Lord-McSweeney commented 1 year ago

With #10410 this needs Socket.

Hadaward commented 1 year ago

Transformice is almost playable despite having a few things missing. Testing along with PR https://github.com/ruffle-rs/ruffle/pull/12047 I noticed that some things are not yet implemented.

Nine-Slice-Scaling create account ui Nine-Slice-Scaling options ui

Some modern game interfaces do not render properly, the reason being that apparently 9-slice scaling has not yet been implemented in Ruffle.

Another point is that external game resources that are hosted at transformice.com/images by the game developer and images.atelier801.com by the minigames development team are not being loaded, and there is no warning about this in the Ruffle logs so I assume the function is stubbed without warning or not implemented correctly.

game logs

Ruffle also doesn't have tab navigation yet and it looks like the flash.text.TextField.restrict property is missing, so it's not possible to log into a game account as the input is ignored and the server only receives the player's nickname, configuring it as a guest account.

I also commented on pr https://github.com/ruffle-rs/ruffle/pull/12047#issuecomment-1646515961 about the delay in receiving data from Ruffle, I don't know the reason for this but the ping and the screenshot of the jump prove it.

One bug that I also don't know the exact cause of is that the player list is not rendering in ruffle. Ruffle: image Flash: image

There's also this error:

ERROR ruffle_core::avm2::events: Error dispatching event EventObject(EventObject { type: "timer", class: flash.events::TimerEvent, ptr: 0x55c63997fbb0 }) to handler FunctionObject(FunctionObject { ptr: 0x55c637c85ea0 }) : RustError("TypeError: Cannot coerce argument of type __AS3__.vec::Vector.<flash.display::DisplayObject> to argument of type __AS3__.vec::Vector.<\u{6}\u{1}\u{1}\u{1}\u{5}\u{2}\u{8}\u{3}\u{8}>")

But this seems to be fixed by another pr so it's not too worth mentioning it.

Without being able to log into a real account due to the password input being ignored it is not possible to test other game interfaces like the player profile and the cheese hall, so this is the report for now.

Lord-McSweeney commented 1 year ago

Vector error has been fixed by #12132.

Lord-McSweeney commented 1 year ago

For anyone who needs it, here's the deobfuscated version of the main class (that loads binaryDatas) in Transformice.swf:

package {
    import flash.display.Loader;
    import flash.display.Sprite;
    import flash.system.LoaderContext;
    import flash.utils.ByteArray;
    import flash.utils.getDefinitionByName;

    public dynamic class qjjPRySbAZ extends Sprite {
        private var chunk5:Class;

        private var chunk7:Class;

        private var chunk11:Class;

        private var chunk12:Class;

        private var chunk9:Class;

        private var chunk3:Class;

        private var chunk2:Class;

        private var chunk1:Class;

        private var chunk8:Class;

        private var chunk10:Class;

        private var chunk4:Class;

        private var chunk6:Class;

        public function qjjPRySbAZ() {
            this.chunk5 = qjjPRySbAZ_chunk5;
            this.chunk7 = qjjPRySbAZ_chunk7;
            this.chunk11 = qjjPRySbAZ_chunk11;
            this.chunk12 = qjjPRySbAZ_chunk12;
            this.chunk9 = qjjPRySbAZ_chunk9;
            this.chunk3 = qjjPRySbAZ_chunk3;
            this.chunk2 = qjjPRySbAZ_chunk2;
            this.chunk1 = qjjPRySbAZ_chunk1;
            this.chunk8 = qjjPRySbAZ_chunk8;
            this.chunk10 = qjjPRySbAZ_chunk10;
            this.chunk4 = qjjPRySbAZ_chunk4;
            this.chunk6 = qjjPRySbAZ_chunk6;
            super();

            this.__qguva__ = String.fromCharCode;
            this.resultBytes = new ByteArray();
            this.resultBytes.writeBytes(new this.chunk1());
            this.resultBytes.writeBytes(new this.chunk2());
            this.resultBytes.writeBytes(new this.chunk3());
            this.resultBytes.writeBytes(new this.chunk4());
            this.resultBytes.writeBytes(new this.chunk5());
            this.resultBytes.writeBytes(new this.chunk6());
            this.resultBytes.writeBytes(new this.chunk7());
            this.resultBytes.writeBytes(new this.chunk8());
            this.resultBytes.writeBytes(new this.chunk9());
            this.resultBytes.writeBytes(new this.chunk10());
            this.resultBytes.writeBytes(new this.chunk11());
            this.resultBytes.writeBytes(new this.chunk12());

            this.loader = new Loader();
            this.addChild(this.loader);

            this.loader.contentLoaderInfo.addeventlistener('complete', this.completeHandler);
            this.loadingContext = new LoaderContext();
            this.loadingContext.allowCodeimport = true;
            this.loader.loadBytes(this.resultBytes, this.loadingContext);
            this.resultBytes.clear();
        }

        private function completeHandler(... rest) : void {
            this.__tiwpwvfnsi__ = 0;
            this.__bjkq__ = 10000;
            this.__hzxCx__ = 3000;
            this.__eubtvzobqok__ = this.loader.content;
            this.stage.scaleMode = 'noScale';
        }
    }
}