Greasy Fork is available in English.

RBDB

RocketBall Decoder Buffer

このスクリプトは単体で利用できません。右のようなメタデータを含むスクリプトから、ライブラリとして読み込まれます: // @require https://update.greasyfork.org/scripts/448028/1071803/RBDB.js

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==UserScript==
// @name         RBDB
// @version      1.0
// @author       0vC4
// @description  RocketBall Decoder Buffer
// @namespace    https://greasyfork.org/users/670183-exnonull
// @license      MIT
// ==/UserScript==

const RBScheme = {
    pos: [
        [13, 'x', 'f32'],
        [21, 'y', 'f32'],
    ],
    object: [
        [10, 'pos', 'pos'],
        [16, 'type', 'objectType'],
        [24, 'playerId', 'int7'],
        [37, 'angle', 'f32'],
        [42, 'speed', 'pos'],
    ],
    entity: [
        [8, 'objectId', 'int7'],
        [18, 'data', 'object'],
    ],
    entities: [
        [10, 'item', 'entity'],
    ],
    user: [
        [8, 'playerId', 'int7'],
        [18, 'username', 'string'],
        [24, 'goals', 'int7'],
        [32, 'assists', 'int7'],
        [40, 'team', 'teamName'],
        [48, 'car', 'int7'],
        [56, 'bot', 'int7'],
        [64, 'exp', 'int7'],
    ],
    users: [
        [10, 'item', 'user'],
    ],

    team: [
        [8, 'name', 'teamName'],
        [16, 'goals', 'int7'],
    ],
    round: [
        [13, 'time', 'f32'],
        [18, 'red', 'team'],
        [18, 'blue', 'team'],
        [18, 'spectator', 'team'],
        [24, 'magic', 'int7'],
    ],

    finish: [
        [10, 'authToken', 'string'],
        [34, 'username', 'string'],
        [40, 'goals', 'int7'],
        [48, 'assists', 'int7'],
        [56, 'wins', 'int7'],
        [72, 'lost', 'int7'],
        [80, 'spareExp', 'int7'],
        [88, 'exp', 'int7'],
    ],
};

class ReadBuffer {
    offset = 0
    buffer = []
    finished = false
    constructor(packet) {
        this.buffer = [...packet];
        this.offset = 0
    }

    next(size=1) {
        let offset = this.offset;
        this.offset += size;
        if (this.offset == this.buffer.length) this.finished = true;
        return offset;
    }

    get skip8() {
        this.u8;
        return this;
    }
    get skip16() {
        this.u16;
        return this;
    }
    get skip32() {
        this.u32;
        return this;
    }
    get skip64() {
        this.u64;
        return this;
    }
    get skip7() {
        this.int7;
        return this;
    }
    get skipString() {
        this.string;
        return this;
    }

    get u8() {
        return new Uint8Array([this.buffer[this.next()]])[0];
    }
    get u16() {
        return new Uint16Array(new Uint8Array(this.buffer.slice(this.next(2), this.offset)).buffer)[0];
    }
    get u32() {
        return new Uint32Array(new Uint8Array(this.buffer.slice(this.next(4), this.offset)).buffer)[0];
    }
    get u64() {
        return new BigUint64Array(new Uint8Array(this.buffer.slice(this.next(8), this.offset)).buffer)[0];
    }

    get s8() {
        return new Int8Array([this.buffer[this.next()]])[0];
    }
    get s16() {
        return new Int16Array(new Uint8Array(this.buffer.slice(this.next(2), this.offset)).buffer)[0];
    }
    get s32() {
        return new Int32Array(new Uint8Array(this.buffer.slice(this.next(4), this.offset)).buffer)[0];
    }
    get s64() {
        return new BigInt64Array(new Uint8Array(this.buffer.slice(this.next(8), this.offset)).buffer)[0];
    }

    get f32() {
        return new Float32Array(new Uint8Array(this.buffer.slice(this.next(4), this.offset)).buffer)[0];
    }
    get f64() {
        return new Float64Array(new Uint8Array(this.buffer.slice(this.next(8), this.offset)).buffer)[0];
    }

    get int7() {
        let offset = 0;
        let num = 0;
        while (offset != 0b100011) {
            let byte = this.buffer[this.offset + offset/7];
            num |= (byte&0x7f)<<offset;
            offset += 7;
            if ((byte&0x80) == 0) {
                this.next(offset/7);
                return num;
            }
        }
        this.next(offset/7);
        return num;
    }
    get array() {
        return new Uint8Array(this.buffer.slice(this.next(this.buffer.length-this.offset), this.offset));
    }
    get array7() {
        const offset = this.next(this.int7);
        return new Uint8Array(this.buffer.slice(offset, this.offset));
    }
    get string() {
        return new TextDecoder().decode(this.array7);
    }



    // custom types

    read(scheme) {
        const buffer = new ReadBuffer(this.array7);
        if (buffer.buffer.length == 0) return {};

        const data = {};
        const skip = [];
        while (!buffer.finished) {
            const slotId = buffer.u8;
            try {
                const [id, key, type] = RBScheme[scheme].find(([id], i) => {
                    if (id == slotId && !skip.includes(i)) {
                        skip.push(i);
                        return true;
                    }
                });
                data[key] = buffer[type];
            } catch (e) {
                console.log(scheme, buffer.buffer+'', buffer.offset, slotId, RBScheme[scheme]);
                throw e;
            }
        }

        return data;
    }
    readArray(scheme) {
        const buffer = new ReadBuffer(this.array7);
        if (buffer.buffer.length == 0) return [];

        const data = [];
        while (!buffer.finished)
            data.push(buffer.skip8.read(RBScheme[scheme][0][2]));

        return data;
    }

    get pos() {
        const data = this.read('pos');
        if (!data.x) data.x = 0;
        if (!data.y) data.y = 0;
        return data;
    }
    get object() {
        return this.read('object');
    }
    get entity() {
        return this.read('entity');
    }
    get entities() {
        return this.readArray('entities');
    }

    get user() {
        return this.read('user');
    }
    get users() {
        return this.readArray('users');
    }


    get objectType() {
        return ['player', 'ball', 'magic', 'magic2', 'boost'][this.int7];
    }
    get teamName() {
        return ['red', 'blue', 'spectator'][this.int7];
    }
    get team() {
        const data = this.read('team');
        if (!data.name) data.name = 'red';
        if (!data.goals) data.goals = 0;
        return data;
    }
    get round() {
        return this.read('round');
    }

    get finish() {
        return this.read('finish');
    }
}

class WriteBuffer {
    offset = 0
    buffer = []
    constructor(buffer = []) {
        this.buffer = [...buffer];
        this.offset = buffer.length;
    }

    u8(...nums) {
        for (let byte of new Uint8Array([...nums]))
            this.buffer[this.offset++] = byte;
        return this;
    }
    u16(...nums) {
        for (let byte of new Uint8Array(new Uint16Array([...nums]).buffer))
            this.buffer[this.offset++] = byte;
        return this;
    }
    u32(...nums) {
        for (let byte of new Uint8Array(new Uint32Array([...nums]).buffer))
            this.buffer[this.offset++] = byte;
        return this;
    }
    u64(...nums) {
        for (let byte of new Uint8Array(new BigUint64Array([...nums]).buffer))
            this.buffer[this.offset++] = byte;
        return this;
    }

    s8(...nums) {
        for (let byte of new Uint8Array([...nums]))
            this.buffer[this.offset++] = byte;
        return this;
    }
    s16(...nums) {
        for (let byte of new Uint8Array(new Int16Array([...nums]).buffer))
            this.buffer[this.offset++] = byte;
        return this;
    }
    s32(...nums) {
        for (let byte of new Uint8Array(new Int32Array([...nums]).buffer))
            this.buffer[this.offset++] = byte;
        return this;
    }
    s64(...nums) {
        for (let byte of new Uint8Array(new BigInt64Array([...nums]).buffer))
            this.buffer[this.offset++] = byte;
        return this;
    }

    f32(...nums) {
        for (let byte of new Uint8Array(new Float32Array([...nums]).buffer))
            this.buffer[this.offset++] = byte;
        return this;
    }
    f64(...nums) {
        for (let byte of new Uint8Array(new Float64Array([...nums]).buffer))
            this.buffer[this.offset++] = byte;
        return this;
    }

    int7(...nums) {
        const bytes = [];

        for (let num of nums) {
            while(num >= 0x80) {
                bytes.push((num|0x80)%0x100);
                num >>= 7;
            }
            bytes.push(num%0x100);
        }

        for (let byte of bytes)
            this.buffer[this.offset++] = byte;

        return this;
    }
    array(...arrays) {
        for (let array of arrays)
            for (let byte of array)
                this.buffer[this.offset++] = new Uint8Array([byte])[0];
        return this;
    }
    array7(...arrays) {
        for (let array of arrays) {
            this.int7(array.length);
            for (let byte of array)
                this.buffer[this.offset++] = new Uint8Array([byte])[0];
        }
        return this;
    }
    string(...strs) {
        for (let str of strs) this.array7(new TextEncoder().encode(str));
        return this;
    }



    // custom types

    write(scheme, ...items) {

        for (let item of items) {
            const buffer = new WriteBuffer();

            for (let k in item) {
                try {
                    const [id, key, type] = RBScheme[scheme].find(([id, key]) => key == k);
                    buffer.u8(id);
                    buffer[type](item[k]);
                } catch (e) {
                    console.log(scheme, items, item, k, RBScheme[scheme], buffer.buffer+'');
                    throw e;
                }
            }

            this.int7(buffer.buffer.length).array(buffer.buffer);
        }

        return this;
    }
    writeArray(scheme, ...itemLists) {

        for (let itemList of itemLists) {
            const buffer = new WriteBuffer();
            for (let items of itemList)
                buffer.int7(10).write(RBScheme[scheme][0][2], items);
            this.int7(10, buffer.buffer.length).array(buffer.buffer);
        }

        return this;
    }

    pos(...items) {
        return this.write('pos', ...items);
    }
    object(...items) {
        return this.write('object', ...items);
    }
    entity(...items) {
        return this.write('entity', ...items);
    }
    entities(...itemLists) {
        return this.writeArray('entities', ...itemLists);
    }

    user(...items) {
        return this.write('user', ...items);
    }
    users(...itemLists) {
        return this.writeArray('users', ...itemLists);
    }

    objectType(name) {
        this.buffer[this.offset++] = ['player', 'ball', 'magic', 'magic2', 'boost'].indexOf(name);
        return this;
    }
    teamName(name) {
        this.buffer[this.offset++] = ['red', 'blue', 'spectator'].indexOf(name);
        return this;
    }
    team(...items) {
        return this.write('team', ...items);
    }
    round(...items) {
        return this.write('round', ...items);
    }

    finish(...items) {
        return this.write('finish', ...items);
    }
}
// 0vC4#7152