RSC in HTML5 using THREE.js

I’ve been playing around with the idea of rewriting the rsc client in html5 just for shit n giggles, I have started writing an OB3 loader for THREE.js you can view the, the loader does not support colors or textures at this time because I forgot how they work and have to research it. I’ve also noticed a few models are drawing weird it may be the way I am reducing the polys into triangle so I will have to look into it. At this time I have dumped the straight ob3 models but plan to write a cache loader at some point. Anyone that is interested playing around with it can feel free.

example here:
code here:

Just what we need more silab items. :stuck_out_tongue_closed_eyes:

Good luck with your HTML5 client. Hopefully all goes well with it.

Looking cool so far, how are you finding three.js? Nice to see you getting into more web shit too.

What no ES6? Tsk tsk. :stuck_out_tongue:

I thought you were hosting your own git repository for a second then I checked out getgud. What the fuck lol.

me too lol, gitgud just sounds like a silab product kek

Doesn’t work with my graphics card. Feels bad.

really does your card not support openGL?

Three.js is pretty easy to work with so far, using Javascript has been fun but a bit of a challenge all at the time time, just for example when loading from the cache Jagex used a filename hashing method where they would overflow an integer, in javascript the overflow wouldn’t occur due to the size difference of Number and Java’s Integer. It took me a solid hour to figure out where the error was as I had thought I had something wrong with the way I was opening the files.

I don’t own but its a pretty creative name, I decided to use this git host because they claim to be pretty lax on take down requests, and the ability to freely create private repos.

Interesting. I was thinking about migrating my research documents because if I lost my wiki for my server source I’d kill someone.

Yes it does but WebGL is weird.

you probably need to enable webgl in your browser settings sometimes its disabled.

Visual studio online offers git, continuous integration, Kanban, etc in private repositories for free.

Continued progress:

I wanted to jump straight into getting textures loaded for models but had to take a crash course in that which can be called RSC fucked cache system. Having only really played with the rs2 cache you can really come to appreciate the progress they made with the organization of the cache system from the upgrade. The way that files are read and packed have not changed much between rsc and the initial release of rs2 but the organization is a bit fucked in RSC. I needed the texture count but found it was packed in this giant configuration file which is basically every important definition file for RSC. in RS2 they had the sense to separate this.

Also I removed the bzip2 compression on cache files simply because my conversion of the java class didn’t really function and alternatives would need to be modifed to fit with the rsc standard (removing the header and hard setting some options) I didn’t really feel like messing with it so just opted for just repacking the cache with no bzip compression. I wrote this quick pile of silab to get the job done.

 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.

import java.util.logging.Level;
import java.util.logging.Logger;

 * a shitty bypass for my BZIP2 issues in javascript this will repack the cache with no bzipped data.
 * @author Silabsoft
public class NoBZip {

    public NoBZip() {

    public void unBzip(String file) throws Exception {
        FileOutputStream out = new FileOutputStream("data204_no_bzip/" + file+"t");
        byte[] f = readDataFile(file);
        Entry[] e = unpackData(f);
        byte[] payload = this.writeEntries(e);
        ByteArrayOutputStream bo = new ByteArrayOutputStream();
        this.putTri(bo, payload.length);
        this.putTri(bo, payload.length);

    public static void main(String[] args)  {
        try {
            NoBZip ub = new NoBZip();
            // ub.unBzip("textures" + Version.TEXTURES + ".jag");
        } catch (Exception ex) {
            Logger.getLogger(NoBZip.class.getName()).log(Level.SEVERE, null, ex);

    public byte[] readDataFile(String file) {
        //System.out.println("Using default load");
        file = "./data204_no_bzip/" + file;
        int archiveSize = 0;
        int archiveSizeCompressed = 0;
        byte archiveData[] = null;
        byte header[] = new byte[6];
        try {

   inputstream = Utility.openFile(file);
            DataInputStream datainputstream = new DataInputStream(inputstream);

            datainputstream.readFully(header, 0, 6);
            archiveSize = ((header[0] & 0xff) << 16) + ((header[1] & 0xff) << 8) + (header[2] & 0xff);

            archiveSizeCompressed = ((header[3] & 0xff) << 16) + ((header[4] & 0xff) << 8) + (header[5] & 0xff);
            int read = 0;
            archiveData = new byte[archiveSizeCompressed];
            while (read < archiveSizeCompressed) {
                int length = archiveSizeCompressed - read;
                if (length > 1000) {
                    length = 1000;
                datainputstream.readFully(archiveData, read, length);
                read += length;

        } catch (IOException ignored) {

        if (archiveSizeCompressed != archiveSize) {
            byte decompressed[] = new byte[archiveSize];
            BZLib.decompress(decompressed, archiveSize, archiveData, archiveSizeCompressed, 0);
            return decompressed;
        } else {
            return archiveData;

    public byte[] writeEntries(Entry[] entry) throws IOException {
        ByteArrayOutputStream bb = new ByteArrayOutputStream();
        putShort(bb, entry.length);
        for (int i = 0; i < entry.length; i++) {
            putInt(bb, entry[i].getFileHash());
            putTri(bb, entry[i].getData().length);
            putTri(bb, entry[i].getData().length);

        for (int i = 0; i < entry.length; i++) {
            for(int x = 0; x < entry[i].getData().length; x++){
        return bb.toByteArray();

    public void putShort(ByteArrayOutputStream out, int i) {
        out.write((byte) (i >> 8));
        out.write((byte) i);

    public void putInt(ByteArrayOutputStream out, int i) {
        out.write((byte) (i >> 24));
        out.write((byte) (i >> 16));
        out.write((byte) (i >> 8));
        out.write((byte) i);


    public void putTri(ByteArrayOutputStream out, int i) {
        out.write((byte) (i >> 16));
        out.write((byte) (i >> 8));
        out.write((byte) i);

    public Entry[] unpackData(byte archiveData[]) {
        int numEntries = (archiveData[0] & 0xff) * 256 + (archiveData[1] & 0xff);
        int offset = 2 + numEntries * 10;
        Entry[] ent = new Entry[numEntries];
        for (int entry = 0; entry < numEntries; entry++) {
            int fileHash = (archiveData[entry * 10 + 2] & 0xff) * 0x1000000 + (archiveData[entry * 10 + 3] & 0xff) * 0x10000 + (archiveData[entry * 10 + 4] & 0xff) * 256 + (archiveData[entry * 10 + 5] & 0xff);
            int fileSize = (archiveData[entry * 10 + 6] & 0xff) * 0x10000 + (archiveData[entry * 10 + 7] & 0xff) * 256 + (archiveData[entry * 10 + 8] & 0xff);
            int fileSizeCompressed = (archiveData[entry * 10 + 9] & 0xff) * 0x10000 + (archiveData[entry * 10 + 10] & 0xff) * 256 + (archiveData[entry * 10 + 11] & 0xff);
            System.out.println(fileHash+" "+fileSize+" "+fileSizeCompressed+" "+offset);
            byte[] fileData = new byte[fileSize];
            if (fileSize != fileSizeCompressed) {
                BZLib.decompress(fileData, fileSize, archiveData, fileSizeCompressed, offset);
            } else {
                for (int j = 0; j < fileSize; j++) {
                    fileData[j] = archiveData[offset + j];


            ent[entry] = new Entry(fileHash, fileData);

            offset += fileSizeCompressed;

        return ent;

    public class Entry {

        private final byte[] data;
        private final int fileHash;

        public Entry(int fileHash, byte[] data) {
   = data;
            this.fileHash = fileHash;

        public byte[] getData() {
            return data;

        public int getFileHash() {
            return fileHash;



You can view the config data dumped in real time here:

but does it have a cool name like gitgud?

Not really, no. But I still think that it’s pretty decent if you’ve got something you want to keep private/semi-private.
Put it this way, doing continuous integration and continuous deployment with Github requires you to tie a half dozen different services together. Doing it with TFS online, it’s all integrated.

inb4 the anti-Micro$oft brigade shows up. XD

Lothy you come in here always proposing ways to make things professional but unless you need it, it just seems excessive

On a more serious note, your little example isn’t clearing the screen between model changes and seems to be rendering upside down on Chrome Version 52.0.2743.116 m (Windows 10 x64). Just curious if it’s a bug or just your pile of silab.