Emberveil source

tests/OnlineSmoke.java

import java.net.InetAddress;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;

/** External VPS acceptance check; temporary QA accounts only, never owner credentials. */
public class OnlineSmoke extends Game {
    static String host, username, peer;
    static int targetTicks=Integer.getInteger("emberveil.qaTicks",1800);
    int ticks, startX, startY;
    boolean attempted, moved, sawPeer, sawChat, welcome;
    boolean cyanHeader,cyanPvm,cyanPvp,chestVisible,chestOpened,chestDeposited,chestWithdrew;
    int depositSlot=-1,depositId,depositCount;
    boolean goblinListed,goblinJournal,staffDenied,monkListed,monkJournal,romeoListed,romeoJournal,fishingListed,fishingJournal;
    boolean clockListed,clockJournal,totemListed,totemJournal,ownerSlayerDenied;
    void bankClick(int widget,int slot,int id){stream.createFrame(145);stream.method432(widget);stream.method432(slot);stream.method432(id);}
    boolean testWalking=true, testChat=true;
    @Override public void resetLogout() {
        if(attempted && ticks>0) {
            System.err.println("FAIL unexpected logout at test tick "+ticks+" packet="+pktType);
            new Exception("Logout call site").printStackTrace();
            System.exit(3);
        }
        super.resetLogout();
    }
    @Override public void dropClient() {
        System.err.println("FAIL dropped connection at test tick "+ticks+" packet="+pktType);
        new Exception("Connection drop call site").printStackTrace();
        System.exit(4);
    }
    void chat() {
        stream.createFrame(4); stream.writeWordBigEndian(0);
        int offset=stream.currentOffset;
        stream.method425(0); stream.method425(0);
        aStream_834.currentOffset=0;
        TextInput.method526("emberveil online test", aStream_834);
        stream.method441(0,aStream_834.buffer,aStream_834.currentOffset);
        stream.writeBytes(stream.currentOffset-offset);
    }
    @Override public void processGameLoop() {
        if(!attempted) {
            attempted=true; myUsername=username; myPassword=System.getProperty("emberveil.qaPassword");
            if(myPassword==null) throw new IllegalArgumentException("QA password required");
            loginScreenState=2; login(myUsername,myPassword,false);
            if(!loggedIn) { System.err.println("FAIL login: "+loginMessage1+" "+loginMessage2); System.exit(1); }
            System.out.println("Encrypted public VPS login succeeded: "+username);
        }
        super.processGameLoop();
        if(!loggedIn || loadingStage!=2) return;
        ticks++;
        if(ticks==20 || (Boolean.getBoolean("emberveil.checkGreenUpdate")&&openInterfaceID==3559&&ticks%40==0)) {
            for(RSInterface widget: RSInterface.interfaceCache) {
                if(widget!=null && widget.anInt214==326) { System.out.println("Accepting character design: tick="+ticks+" interface="+openInterfaceID+" button="+widget.id);if(promptUserForInput(widget)){stream.createFrame(185);stream.writeWord(widget.id);} break; }
            }
        }
        for(Player player:playerArray) if(player!=null && player.name!=null && player.name.equalsIgnoreCase(peer)) sawPeer=true;
        for(String message:chatMessages) if(message!=null) {
            if(message.contains("Slayer teleport is only available to owner bonds"))ownerSlayerDenied=true;
            if(message.contains("teleport is for Emberveil's owner and co-owner"))staffDenied=true;
            if(message.contains("Welcome to") && message.contains("bonds")) welcome=true;
            if(message.startsWith("@cya@")&&message.contains("Welcome to")&&message.contains("bonds"))cyanHeader=true;
            if(message.startsWith("@cya@")&&message.contains("PvM")&&message.contains("Slayer"))cyanPvm=true;
            if(message.startsWith("@cya@")&&message.contains("Wilderness")&&message.contains("PvP"))cyanPvp=true;
            if(message.toLowerCase().contains("emberveil online test")) sawChat=true;
        }
        if(ticks==100 && testWalking) {
            startX=myPlayer.smallX[0]; startY=myPlayer.smallY[0];
            doWalkTo(0,0,0,0,startY,0,0,startY,startX,true,startX+1);
        }
        if(ticks>100 && (myPlayer.smallX[0]!=startX || myPlayer.smallY[0]!=startY)) moved=true;
        if(testChat && (ticks==200 || ticks==400)) chat();
        if(Boolean.getBoolean("emberveil.checkDarrenUpdate")) {
            if(ticks==300)goblinListed=RSInterface.interfaceCache[7338]!=null&&RSInterface.interfaceCache[7338].disabledText.contains("Goblin Diplomacy");
            if(ticks==800){String command="staffzone";stream.createFrame(103);stream.writeWordBigEndian(command.length()+1);stream.writeString(command);}
            if(ticks==850){stream.createFrame(185);stream.writeWord(7338);}
            if(ticks==910){goblinJournal=openInterfaceID==8134&&RSInterface.interfaceCache[8144].disabledText.contains("Goblin Diplomacy");closeOpenInterfaces();}
        }
        if(Boolean.getBoolean("emberveil.checkShortQuest")) {
            if(ticks==940){monkListed=RSInterface.interfaceCache[7369]!=null&&RSInterface.interfaceCache[7369].disabledText.contains("Monk's Friend");stream.createFrame(185);stream.writeWord(7369);}
            if(ticks==1010){monkJournal=openInterfaceID==8134&&RSInterface.interfaceCache[8144].disabledText.contains("Monk's Friend");closeOpenInterfaces();}
        }
        if(Boolean.getBoolean("emberveil.checkRomeoQuest")) {
            if(ticks==1060){romeoListed=RSInterface.interfaceCache[7343]!=null&&RSInterface.interfaceCache[7343].disabledText.contains("Romeo");stream.createFrame(185);stream.writeWord(7343);}
            if(ticks==1120){romeoJournal=openInterfaceID==8134&&RSInterface.interfaceCache[8144].disabledText.contains("Romeo");closeOpenInterfaces();}
        }
        if(Boolean.getBoolean("emberveil.checkFishingQuest")) {
            if(ticks==1180){fishingListed=RSInterface.interfaceCache[7359]!=null&&RSInterface.interfaceCache[7359].disabledText.contains("Fishing Contest");stream.createFrame(185);stream.writeWord(7359);}
            if(ticks==1240){fishingJournal=openInterfaceID==8134&&RSInterface.interfaceCache[8144].disabledText.contains("Fishing Contest");closeOpenInterfaces();}
        }
        if(ticks%1000==0) System.out.println("Still connected: "+username+" ticks="+ticks);
        if(Boolean.getBoolean("emberveil.checkClockQuest")) {
            if(ticks==1280){clockListed=RSInterface.interfaceCache[7353]!=null&&RSInterface.interfaceCache[7353].disabledText.contains("Clock Tower");stream.createFrame(185);stream.writeWord(7353);}
            if(ticks==1360){clockJournal=openInterfaceID==8134&&RSInterface.interfaceCache[8144].disabledText.contains("Clock Tower");closeOpenInterfaces();}
        }
        if(Boolean.getBoolean("emberveil.checkTotemQuest")) {
            if(ticks==1400){totemListed=RSInterface.interfaceCache[7381]!=null&&RSInterface.interfaceCache[7381].disabledText.contains("Tribal Totem");stream.createFrame(185);stream.writeWord(7381);}
            if(ticks==1480){totemJournal=openInterfaceID==8134&&RSInterface.interfaceCache[8144].disabledText.contains("Tribal Totem");closeOpenInterfaces();}
        }
        if(ticks==500 && Boolean.getBoolean("emberveil.checkNewClient")){tabID=7;needDrawTabArea=true;tabAreaAltered=true;}
        if(Boolean.getBoolean("emberveil.checkGreenUpdate")) {
            if(ticks==300)chestVisible=(worldController.method302(plane,3231-baseX,3227-baseY)>>14&0x7fff)==4483;
            if(ticks==450)doWalkTo(0,0,0,0,myPlayer.smallY[0],0,0,3227-baseY,myPlayer.smallX[0],true,3232-baseX);
            if(ticks==520){stream.createFrame(132);stream.method433(3231);stream.writeWord(4483);stream.method432(3227);}
            if(ticks==600){
                chestOpened=openInterfaceID==5292;
                if(!chestOpened){System.err.println("FAIL spawn chest did not open; visible="+chestVisible+" position="+(baseX+myPlayer.smallX[0])+","+(baseY+myPlayer.smallY[0])+","+plane+" interface="+openInterfaceID);for(String line:chatMessages)if(line!=null)System.err.println("QA chat: "+line);System.exit(1);}
                RSInterface inv=RSInterface.interfaceCache[5064];
                for(int i=0;i<inv.inv.length;i++)if(inv.inv[i]==996){depositSlot=i;depositId=995;depositCount=inv.invStackSizes[i];break;}
                if(depositSlot<0)for(int i=0;i<inv.inv.length;i++)if(inv.inv[i]>0){depositSlot=i;depositId=inv.inv[i]-1;depositCount=inv.invStackSizes[i];break;}
                if(depositSlot<0)throw new AssertionError("No starter item for deposit test");
                bankClick(5064,depositSlot,depositId);
            }
            if(ticks==660){RSInterface bank=RSInterface.interfaceCache[5382];for(int i=0;i<bank.inv.length;i++)if(bank.inv[i]==depositId+1&&bank.invStackSizes[i]>=1){chestDeposited=true;bankClick(5382,i,depositId);break;}}
            if(ticks==730){RSInterface inv=RSInterface.interfaceCache[5064];for(int i=0;i<inv.inv.length;i++)if(inv.inv[i]==depositId+1&&inv.invStackSizes[i]>=depositCount)chestWithdrew=true;closeOpenInterfaces();}
        }
        if(Boolean.getBoolean("emberveil.checkOwnerSlayer") && ticks==850){String command="slayertask";stream.createFrame(103);stream.writeWordBigEndian(command.length()+1);stream.writeString(command);}
        if(Boolean.getBoolean("emberveil.checkOwnerSlayer") && ticks==880){String command="slayzone";stream.createFrame(103);stream.writeWordBigEndian(command.length()+1);stream.writeString(command);}
        // Visual fixture only: never changes the QA account on the server.
        if(Boolean.getBoolean("emberveil.checkFireHat") && ticks==900){
            myPlayer.name="Bonds";myPlayer.equipment[0]=14999+512;
            try{java.lang.reflect.Field f=Player.class.getDeclaredField("aLong1718");f.setAccessible(true);f.setLong(myPlayer,789010380512L);}catch(Exception e){throw new RuntimeException(e);}
            System.out.println("Client-only equipped fire-partyhat visual fixture enabled");
        }
        if(ticks==targetTicks) {
            try {
                BufferedImage screenshot=new BufferedImage(512,334,BufferedImage.TYPE_INT_RGB);
                screenshot.setRGB(0,0,512,334,aRSImageProducer_1165.anIntArray315,0,512);
                ImageIO.write(screenshot,"png",new File("runtime/online-"+username+".png"));
                boolean pass=moved && sawPeer && sawChat && welcome;
                if(Boolean.getBoolean("emberveil.checkOwnerSlayer")){pass &= ownerSlayerDenied;System.out.println("Owner Slayer teleport denied to QA account: "+ownerSlayerDenied);}
                if(Boolean.getBoolean("emberveil.checkFireHat")){boolean hat=FirePartyhat.wearing(myPlayer)&&myPlayer.getRotatedModel()!=null;pass &= hat;System.out.println("Client-only equipped fire-partyhat render fixture: "+hat);}
                if(Boolean.getBoolean("emberveil.checkTotemQuest")){pass &= totemListed&&totemJournal;System.out.println("Tribal Totem live journal: listed="+totemListed+" journal="+totemJournal);}
                if(Boolean.getBoolean("emberveil.checkClockQuest")){pass &= clockListed&&clockJournal;System.out.println("Clock Tower live journal: listed="+clockListed+" journal="+clockJournal);}
                if(Boolean.getBoolean("emberveil.checkFishingQuest")){pass &= fishingListed&&fishingJournal;System.out.println("Fishing Contest live journal: listed="+fishingListed+" journal="+fishingJournal);}
                if(Boolean.getBoolean("emberveil.checkRomeoQuest")){pass &= romeoListed&&romeoJournal;System.out.println("Romeo and Juliet live journal: listed="+romeoListed+" journal="+romeoJournal);}

                if(Boolean.getBoolean("emberveil.checkShortQuest")){pass &= monkListed&&monkJournal;System.out.println("Monks Friend live journal: listed="+monkListed+" journal="+monkJournal);}

                if(Boolean.getBoolean("emberveil.checkGreenUpdate")) {
                    int green=0;for(int pixel:aRSImageProducer_1163.anIntArray315)if((pixel>>8&255)>200&&(pixel>>8&255)>(pixel>>16&255))green++;
                    pass &= cyanHeader&&cyanPvm&&cyanPvp&&chestVisible&&chestOpened&&chestDeposited&&chestWithdrew&&green>200;
                    BufferedImage panel=new BufferedImage(190,261,BufferedImage.TYPE_INT_RGB);panel.setRGB(0,0,190,261,aRSImageProducer_1163.anIntArray315,0,190);ImageIO.write(panel,"png",new File("runtime/green-tab-live-"+username+".png"));
                    System.out.println("Green update verified: cyan="+(cyanHeader&&cyanPvm&&cyanPvp)+" chestVisible="+chestVisible+" chestBank="+chestOpened+" deposited="+chestDeposited+" withdrew="+chestWithdrew+" greenPixels="+green);
                }
                if(Boolean.getBoolean("emberveil.checkNewClient")) {
                    if(Boolean.getBoolean("emberveil.checkDarrenUpdate")) {
                        pass &= goblinListed&&goblinJournal&&staffDenied&&baseY+myPlayer.smallY[0]<4000;
                        System.out.println("Darren/free quest verified: goblinListed="+goblinListed+" journal="+goblinJournal+" staffTeleportDenied="+staffDenied);
                    }
                    java.lang.reflect.Field iconField=Game.class.getDeclaredField("slayerTabIcon");iconField.setAccessible(true);Sprite icon=(Sprite)iconField.get(this);
                    pass &= icon!=null && icon.width==24 && icon.height==28 && variousSettings[1907]==1;
                    java.lang.reflect.Field barField=Game.class.getDeclaredField("aRSImageProducer_1124");barField.setAccessible(true);RSImageProducer bar=(RSImageProducer)barField.get(this);
                    BufferedImage tabs=new BufferedImage(269,37,BufferedImage.TYPE_INT_RGB);tabs.setRGB(0,0,269,37,bar.anIntArray315,0,269);ImageIO.write(tabs,"png",new File("runtime/slayer-tab-live.png"));
                    System.out.println("New client icon and player information verified: "+pass);
                }
                System.out.println((pass?"PASS":"FAIL")+" "+username+" world=rendered movement="+moved+" peer="+sawPeer+" chat="+sawChat+" ownerWelcome="+welcome+" position="+(baseX+myPlayer.smallX[0])+","+(baseY+myPlayer.smallY[0])+","+plane);
                if(pass && Boolean.getBoolean("emberveil.checkGreenUpdate")) {java.awt.EventQueue.invokeLater(()->windowClosing(new java.awt.event.WindowEvent(gameFrame,java.awt.event.WindowEvent.WINDOW_CLOSING)));return;}
                System.exit(pass?0:1);
            } catch(Exception error) { error.printStackTrace(); System.exit(1); }
        }
    }
    public static void main(String[] args)throws Exception {
        host=args[0]; username=args[1]; peer=args[2];
        if(!username.matches("evnet[0-9]{1,6}[ab]") || !peer.matches("evnet[0-9]{1,6}[ab]")) throw new IllegalArgumentException("Temporary QA accounts only");
        ClientSettings.SERVER_IP=host; ClientSettings.SERVER_WORLD=1; ClientSettings.CHECK_CRC=false;
        ClientSettings.SHOW_NAVBAR=false; ClientSettings.HIDE_ROOFS=true;
        ClientSettings.CUSTOM_SETTINGS_TAB=Boolean.getBoolean("emberveil.checkNewClient");
        Game.nodeID=10; Game.portOff=0; Game.setHighMem(); Game.isMembers=true;
        Signlink.storeid=32; Signlink.startpriv(InetAddress.getLocalHost());
        OnlineSmoke client=new OnlineSmoke();
        client.createClientFrame(503,765);
        client.gameFrame.setVisible(false);
        Thread timeout=new Thread(()->{try{Thread.sleep(targetTicks*20L+60000);}catch(Exception ignored){} System.err.println("FAIL: online test timeout"); System.exit(2);});
        timeout.setDaemon(true); timeout.start();
    }
}