Jump to content

[OPEN SOURCE] Death Recorder


dubai

Recommended Posts

Ever been testing a script for a long time and you come back to the

bot dead and you're wondering how the hell did it die?
------------------------------------------

Open Source Death Recorder to the rescue!

Using a rolling cache of jpgs and ffmpeg to record the last 10 seconds leading up to when the bot dies for debugging/testing scripts while you're not watching.

This is still under development/testing so I'm releasing it essentially as a concept for now and welcome to anyone who wants to use/build onto this project.

(REQUIRES ffmpeg installed and in system PATH for video conversion)

Still building/testing this thing and adding some more error handling so I might update this thread in the very near future.

 

Source:

Spoiler
import org.osbot.rs07.script.Script;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.LinkedList;
import java.util.Queue;
import javax.imageio.ImageIO;

public class DeathRecorder {
    
    private static final int MAX_FRAMES = 300; // 10 seconds @ 30 FPS
    private static final int CAPTURE_INTERVAL = 33; // ~30 FPS
    private static final int IMAGE_SCALE = 2; // Reduce image size for performance
    
    private final Script script;
    private final Rectangle captureArea;
    private final Queue<File> frameQueue = new LinkedList<>();
    private volatile boolean recording = false;
    private Thread captureThread;
    private Robot robot;

    public DeathRecorder(Script script) throws AWTException {
        this.script = script;
        this.robot = new Robot();
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        this.captureArea = new Rectangle(0, 0, 
            screenSize.width/IMAGE_SCALE, 
            screenSize.height/IMAGE_SCALE);
    }

    public void startRecording() {
        if (recording) return;
        
        recording = true;
        captureThread = new Thread(this::captureLoop);
        captureThread.start();
        script.log("Death recording started");
    }

    private void captureLoop() {
        try {
            File tempDir = new File(System.getProperty("java.io.tmpdir"), "osbot_records");
            tempDir.mkdirs();

            while (recording) {
                BufferedImage screen = robot.createScreenCapture(captureArea);
                BufferedImage scaled = scaleImage(screen);
                File frameFile = saveTempFrame(scaled, tempDir);
                
                synchronized(frameQueue) {
                    if (frameQueue.size() >= MAX_FRAMES) {
                        File oldFrame = frameQueue.poll();
                        if (oldFrame != null) oldFrame.delete();
                    }
                    frameQueue.add(frameFile);
                }
                
                Thread.sleep(CAPTURE_INTERVAL);
            }
        } catch (Exception e) {
            script.log("Recording error: " + e.getMessage());
        }
    }

    private BufferedImage scaleImage(BufferedImage original) {
        int width = original.getWidth() / IMAGE_SCALE;
        int height = original.getHeight() / IMAGE_SCALE;
        BufferedImage scaled = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        scaled.getGraphics().drawImage(original, 0, 0, width, height, null);
        return scaled;
    }

    private File saveTempFrame(BufferedImage image, File dir) throws Exception {
        File tempFile = File.createTempFile("frame_", ".jpg", dir);
        ImageIO.write(image, "jpg", tempFile);
        return tempFile;
    }

    public void saveLastMoments() {
        recording = false;
        try {
            if (captureThread != null) {
                captureThread.join();
            }
            
            File output = new File(System.getProperty("user.home"), 
                "Death_" + System.currentTimeMillis() + ".avi");
            
            // Convert frames to video using built-in Java features
            createVideoFromFrames(output);
            
            cleanupTempFiles();
            script.log("Saved death recording to: " + output.getAbsolutePath());
            
        } catch (Exception e) {
            script.log("Failed to save recording: " + e.getMessage());
        }
    }

    private void createVideoFromFrames(File output) throws Exception {
        // Simple AVI creation using JPG frames sequence
        // Note: This creates a playable but large AVI file
        File tempDir = new File(System.getProperty("java.io.tmpdir"), "osbot_records");
        ProcessBuilder pb = new ProcessBuilder(
            "ffmpeg",
            "-framerate", "30",
            "-i", tempDir.getAbsolutePath() + "/frame_%08d.jpg",
            "-c:v", "mjpeg",
            "-q:v", "3",
            output.getAbsolutePath()
        );
        
        pb.redirectErrorStream(true);
        Process process = pb.start();
        process.waitFor();
    }

    private void cleanupTempFiles() {
        File tempDir = new File(System.getProperty("java.io.tmpdir"), "osbot_records");
        for (File file : tempDir.listFiles()) {
            if (file.getName().startsWith("frame_")) {
                file.delete();
            }
        }
    }
}

Something like this to integrate it into your scripts and trigger recording on death:
 

Spoiler
public class MyBot extends Script {
    private DeathRecorder deathRecorder;
    private long lastCheck;

    @Override
    public void onStart() {
        try {
            deathRecorder = new DeathRecorder(this);
            deathRecorder.startRecording();
        } catch (AWTException e) {
            log("Failed to initialize death recorder: " + e);
        }
    }

    @Override
    public int onLoop() throws InterruptedException {
        if (System.currentTimeMillis() - lastCheck > 1000) {
            if (isDead()) {
                deathRecorder.saveLastMoments();
                // Handle death logic
            }
            lastCheck = System.currentTimeMillis();
        }
        return random(200, 300);
    }

    private boolean isDead() {
               getSkills().getDynamic(Skill.HITPOINTS) <= 0;
    }

    @Override
    public void onExit() {
        if (deathRecorder != null) {
            deathRecorder.saveLastMoments();
        }
    }
}

 

  • Like 3
  • Heart 1
Link to comment
Share on other sites

  • 3 weeks later...
10 hours ago, BloodyNoah said:

I’ve definitely had times when I came back to a dead bot, and it’s a nightmare trying to figure out what went wrong. The idea of having a last-10-seconds recording sounds perfect for debugging.

It is actually really useful, let me know if you decide to work with the snippets, I ended up tweaking it further I might post what I ended up with later today

Link to comment
Share on other sites

  • 2 weeks later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...