dubai Posted Saturday at 08:31 AM Share Posted Saturday at 08:31 AM 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(); } } } 2 Quote Link to comment Share on other sites More sharing options...