Greasy Fork is available in English.

Box3++超级版

别喷,只是想复活

// ==UserScript==
// @name         Box3++超级版
// @version      1.0.0
// @description  别喷,只是想复活
// @namespace    https://pgaot.com/
// @author       创作喵
// @license      MIT
// @match        https://box3.fun/*
// @match        https://box3.codemao.cn/*
// @match        https://static.box3.codemao.cn/block/*
// @icon         https://box3.fun/favicon.ico
// @require      https://cdn.jsdelivr.net/npm/lil-gui@0.16
// @require      https://cdn.jsdelivr.net/npm/three@0.142.0/examples/js/libs/stats.min.js
// @require      https://unpkg.com/mdui@1.0.2/dist/js/mdui.min.js
// @grant        GM_xmlhttpRequest
// @grant        GM_log
// @grant        GM_getValue
// @grant        GM_setValue
// @connect      *
// @connect      static.box3.codemao.cn
// ==/UserScript==
let link = document.createElement("link");
link.rel = "stylesheet";
link.type = "text/css";
link.href = "https://unpkg.com/mdui@1.0.2/dist/css/mdui.min.css";
document.head.appendChild(link);
function prettierUI() {
    requestAnimationFrame(prettierUI);
    mdui.$(".a-F8dFt2hg1W-lIbThLbO").css("backdrop-filter", "blur(3px)");
    [
        "a",
        ".cursor-pointer",
        "._15lJaNwokhrvBITYpCdZBg, ._28PX7t6sYZjpgdkp56cQY_, .gHO6bo4JP-z80tTWRgOld",
        "._1lO8S3Fo0FZ4CNWHI0vDlY",
        "._210V1pgtcsKM0CS3q9Mzoq, ._2IYl3aDKtjEN6yelK6ZQJB,._2VsAXkoKr_drDxi4aXIhgr",
        ".wEySfY7uVCnTptMngztm4 div",
    ].map((s) => mdui.$(s).addClass("mdui-ripple"));
    [
        "button",
        "._1fXgmyCy8qEkTugSc5XVFf ._2byugNOjsV5_rSPJmXvyDO",
        "._1xZbWt8b7cKJ8PPcK2Ho81.-y0as17U00f3yUjy_bm6K",
        "_31l4xpmllyrTDCG_n-rOf6",
    ].map((s) => mdui.$(s).addClass("mdui-ripple mdui-btn mdui-btn-rasied"));
    [
        "._1Bk3sWdrdhP8JPXYytQ240",
        "._2sJ_JP3UNnDIbP2h46Jgsz>*",
        "._1ReegQnUK6jKlZOvd0KIgZ",
        ".KUzAqHalGLCvvU9mhNy2u, ._8iMJ9PRMtIpTQPsCqB-C_",
        "._2K6VILBkECu3h5ocjWmmb5",
    ].map((s) => mdui.$(s).addClass("mdui-ripple mdui-menu-item"));
    [
        "._1U-H613uST925WwgUSVsPx._31uKzmO2fxXo6KwO_-RETK",
        "._3OZUDfwlADkB_meRCgyvWm ._31_zQUYv2JwOcKqu2yk0Vv",
    ].map((s) => mdui.$(s).addClass("mdui-ripple mdui-btn mdui-btn-icon"));
    [].map((s) => mdui.$(s).addClass("mdui-slider"));
    ["._2kWmDVfvQZvUKLSQONkaIr"].map((s) =>
                                     mdui.$(s).addClass("mdui-color-orange")
                                    );
}
window.creathashmap=(hash,name)=>{
    axios({
        method: 'post',
        url: 'https://backend.box3.fun/container/create-game-edit',
        data: JSON.parse(`{"image":"https://static.box3.codemao.cn/img/QmNpHbMFiubdXZVPmannCHsbh3zmpPJkakhSy23Zpw6ijm_121_121_cover.webp","name":"${name}","describe":"","hash":"${hash}","resourceId":0}`),
        withCredentials: true
    })
        .then(({
        request
    }) => {
        console.log(JSON.parse(request.responseText)['data']['value'].slice(5));
    });
}
prettierUI();
var dialogzindex = 99999;
const setv=((html,name,value)=>{html.setAttribute(name,value)});
const setn=((html,value)=>{html.setAttribute(value,"")});
const seth=((html,html5)=>{html.innerHTML=html5});
const sett=((html,text)=>{html.innerText=text});
const addhtml=((position,localName,data,html)=>{var newHtml=document.createElement(localName);for(var name in data){newHtml.setAttribute(name,data[name])};newHtml.innerHTML=html;var newElement=position.appendChild(newHtml);return(newElement)})
const addwindow=(name,content,width,img=null,closeyes=true,position=document.body)=>{
    var dialog = addhtml(position,"div",{class:"box3editmessagediv box3messagedialog dongtai kaishizhuangtai",show:"true",oncontextmenu:"return false;",style:`top:10px;left:10px;width:${width}px;z-index:${dialogzindex}}`},"");
    var dialogdb = addhtml(dialog,"div",{class:"db"},"");
    setTimeout(()=>{dialog.classList.remove("kaishizhuangtai")},10)
    setTimeout(()=>{dialog.classList.remove("dongtai")},260);
    if(img){
        addhtml(dialogdb,"img",{width:"100",height:"100%",src:img,style:"margin-right: 10px;"},``)
    }
    var dialogydtzd = addhtml(dialog,"div",{class:"ydtzd",title:"拖动"},"");
    var a = addhtml(dialogdb,"div",{style:"display: flex;flex-direction: column;"},`<span style='font-weight: bold;font-size:20px'>${name}</span><font style='font-size:12px;color:#aaa;display: flex;flex-direction: row;'></font>`)
    sett(a.getElementsByTagName("font")[0],content)
    var dialogclose = addhtml(dialogdb,"button",{title:"关闭",zdy:"",jy:!closeyes},"×");
    if(closeyes){
        dialogclose.onclick=()=>{setTimeout(()=>{dialog.classList.add("dongtai","kaishizhuangtai");},10);setTimeout(()=>{dialog.remove();},260)}
    }
    dialog.onmousedown=()=>{dialogzindex+=1;dialog.style.zIndex=dialogzindex}
    dialogydtzd.onmousedown=(en)=>{
        setv(dialogydtzd,"ox",en.offsetX)
        setv(dialogydtzd,"oy",en.offsetY)
        document.onmousemove=(e)=>{
            dialog.style.top=(e.clientY-en.offsetY)+"px"
            dialog.style.left=(e.clientX-en.offsetX)+"px"
        }
    }
    dialogydtzd.onmouseup=()=>{document.onmousemove=null;}
    return({close:()=>{dialog.remove()},dialog})
}
var box3messageboxsettings = {
    width:245,
    showmonitor:true,
    monitorposition:0,
    monitortm:90,
}
addhtml(document.body,"style",{type:"text/css"},`

.box3editmessagediv {
    position: fixed;
    background: #383838;
    color: #fff;
    padding: 20px;
    right: 0px;
    bottom: 0px;
    box-shadow: 0px 0px 30px -10px #000;
    margin: 8px 20px;
    z-index:999999;
    display: flex;
    flex-direction: column;
    border-radius: 10px;
    -webkit-border-radius: 10px;
    -moz-border-radius: 10px;
}
.box3editmessagediv .div{
    display: flex;
    align-items: center;
    align-content: center;
    flex-direction: row;
    margin-top: 5px;
}
.box3editmessagediv[show=false] {
    display: none
}
.box3editmessagedivycxx {
    position: fixed;
    right: 0px;
    bottom: -16.75px;
    box-shadow: 0px 0px 30px -10px #000;
    z-index:999999;
    transition: .5s;
}
.box3editmessagedivycxx:hover {
    bottom: 0px;
}
.box3editmessagedivycxx[show=false] {
    display: none
}
.box3editmessagediv .db{
    color: #fff;
    margin-bottom: 10px;
    width: 100%;
    display: flex;
    align-items: center;
    align-content: center;
    flex-direction: row;
}
.box3editmessagediv .db div{
    margin-right: 20px;
}
.box3editmessagediv .div button{margin-right: 5px;}
.box3editmessagediv button[zdy]{background:#0000;color:#fff;position: absolute;top: 0;right: 0;z-index:99999999999999999999999999999999999999999999999999999999999999999}
.box3editmessagediv .ahref{color:currentColor}
.box3editmessagediv .ahref:hover{text-decoration: underline;color:#fff}
.box3editmessagediv .ahref:active{color:#f44747}
.box3editmessagediv .db button{
    font-size:30px;
    padding: 0px 10px;
    border-top-right-radius: 10px;
    transition: .1s;
}
.box3editmessagediv .db button:hover{
    background:#a00a
}
.box3messagedialog {
    top: inherit;
    left: inherit;
    bottom: inherit;
    right: inherit;
    margin: 0;
}
.box3messagedialog .db button[jy=true] {
    opacity: .5;
}
.box3messagedialog .ydtzd {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 40px;
    cursor: all-scroll;
}
.box3editmessagediv .db div font{
    max-height: 100px;
    overflow: auto;
    padding:5px 5px 0px 0px
}
.box3editmessagediv .db div font::-webkit-scrollbar {
    height: 5px;
    width: 5px;
}
.box3editmessagediv .db div font::-webkit-scrollbar-thumb {
    background: #aaa5;
    border-radius: 50px
}
.box3editmessagediv .db div font::-webkit-scrollbar-thumb:hover {
    background: #aaa;
}
.box3editmessagediv .db div font::-webkit-scrollbar-thumb:active {
    background: #aaa3;
}
.box3editmessagediv .db div font::-webkit-scrollbar-track {
    background: #0000;
    border-radius: 50px
}

.box3editmessagediv .div textarea::-webkit-scrollbar {
    height: 5px;
    width: 5px;
}
.box3editmessagediv .div textarea::-webkit-scrollbar-thumb {
    background: #aaa5;
    border-radius: 50px
}
.box3editmessagediv .div textarea::-webkit-scrollbar-thumb:hover {
    background: #aaa;
}
.box3editmessagediv .div textarea::-webkit-scrollbar-thumb:active {
    background: #aaa3;
}
.box3editmessagediv .div textarea::-webkit-scrollbar-track {
    background: #0000;
    border-radius: 50px
}

.box3messagedialog.dongtai{
    transition: .25s cubic-bezier(0, 0, 0, 0.9);
}
.box3messagedialog.kaishizhuangtai{
    transform: scale(0.75);
    opacity: 0;
}


`);
const VOXEL_NAME_TO_ID = {
    A: 37,
    B: 39,
    C: 41,
    D: 43,
    E: 45,
    F: 47,
    G: 49,
    H: 51,
    I: 53,
    J: 55,
    K: 57,
    L: 59,
    M: 61,
    N: 63,
    O: 65,
    P: 67,
    Q: 69,
    R: 71,
    S: 73,
    T: 75,
    U: 77,
    V: 79,
    W: 81,
    X: 83,
    Y: 85,
    Z: 87,
    acacia: 133,
    add: 3,
    air: 0,
    air_duct: 585,
    ampersand: 485,
    asterisk: 487,
    at: 489,
    backslash: 491,
    bamboo: 574,
    bat_window: 546,
    bear_footprint: 553,
    biscuit: 341,
    black: 175,
    black_glass: 302,
    blue: 363,
    blue_decorative_light: 566,
    blue_gift: 557,
    blue_glass: 276,
    blue_light: 291,
    blue_surface_01: 349,
    blue_surface_02: 351,
    blueberry_juice: 416,
    board0: 433,
    board1: 435,
    board10: 453,
    board11: 455,
    board12: 457,
    board13: 459,
    board14: 461,
    board15: 463,
    board2: 437,
    board3: 439,
    board4: 441,
    board5: 443,
    board6: 445,
    board7: 447,
    board8: 449,
    board9: 451,
    board_01: 181,
    board_02: 183,
    board_03: 309,
    board_04: 311,
    board_05: 313,
    board_06: 315,
    board_07: 635,
    bookshelf: 483,
    bounce_pad: 631,
    bracket_close: 493,
    bracket_open: 495,
    brick_01: 637,
    brick_02: 639,
    brick_red: 109,
    button: 587,
    cadet_blue: 89,
    candy: 551,
    caret: 497,
    carpet_01: 195,
    carpet_02: 197,
    carpet_03: 199,
    carpet_04: 201,
    carpet_05: 203,
    carpet_06: 205,
    carpet_07: 207,
    carpet_08: 235,
    carpet_09: 237,
    carpet_10: 239,
    carpet_11: 241,
    carpet_12: 243,
    carpet_13: 245,
    coffee: 428,
    coffee_gray: 379,
    colon: 499,
    color_glass: 172,
    comma: 501,
    conveyor: 471,
    crane_lantern: 405,
    crane_roof_01: 401,
    crane_roof_02: 403,
    cross_window: 162,
    dark_brick_00: 329,
    dark_brick_01: 331,
    dark_brick_02: 333,
    dark_grass: 317,
    dark_gray: 95,
    dark_orchid: 369,
    dark_red: 107,
    dark_salmon: 383,
    dark_slate_blue: 113,
    dark_stone: 327,
    dark_surface: 357,
    dirt: 125,
    divide: 9,
    dollar: 503,
    eight: 33,
    equal: 11,
    exclamation_mark: 13,
    express_box: 479,
    fan: 589,
    firecracker: 582,
    five: 27,
    four: 25,
    fu: 577,
    geometric_window_01: 164,
    geometric_window_02: 166,
    glass: 170,
    gold_trim_brick: 151,
    grape_juice: 420,
    grass: 127,
    greater_than: 505,
    green_decorative_light: 568,
    green_glass: 278,
    green_leaf: 131,
    green_light: 287,
    greenbelt_L: 319,
    greenbelt_L1: 321,
    grey_stone_brick: 149,
    honeycomb_01: 535,
    honeycomb_02: 537,
    ice: 398,
    ice_brick: 145,
    ice_wall: 249,
    indigo_light: 289,
    lab_lamp_01: 591,
    lab_lamp_02: 593,
    lab_lamp_03: 595,
    lab_material_01: 597,
    lab_material_02: 599,
    lab_material_03: 601,
    lab_material_04: 603,
    lab_material_05: 605,
    lab_material_06: 607,
    lab_material_07: 609,
    lab_material_08: 611,
    lab_material_09: 613,
    lab_material_10: 615,
    lab_material_11: 617,
    lab_material_12: 619,
    lab_material_13: 621,
    lab_material_14: 622,
    lab_material_15: 624,
    lab_screen: 627,
    lab_wire: 629,
    lantern_01: 157,
    lantern_02: 159,
    lava01: 465,
    lava02: 467,
    leaf_01: 251,
    leaf_02: 253,
    leaf_03: 529,
    leaf_04: 531,
    leaf_05: 533,
    leaf_06: 633,
    ledfloor01: 473,
    ledfloor02: 475,
    lemon: 121,
    lemon_juice: 418,
    less_than: 507,
    light_gray: 97,
    light_grey_stone_brick: 147,
    lime_juice: 414,
    macaroon: 339,
    maroon: 377,
    medium_gray: 111,
    medium_green: 391,
    medium_orchid: 371,
    medium_purple: 373,
    medium_spring_green: 397,
    medium_violet_red: 375,
    medium_yellow: 389,
    milk: 424,
    mint_green: 395,
    mint_green_light: 297,
    multiply: 7,
    navajo_white: 385,
    nine: 35,
    olive_green: 99,
    one: 19,
    orange: 119,
    orange_juice: 422,
    orange_light: 283,
    orange_red: 387,
    palace_carving: 264,
    palace_cloud: 361,
    palace_eaves_01: 209,
    palace_eaves_02: 211,
    palace_eaves_03: 213,
    palace_eaves_04: 215,
    palace_eaves_05: 217,
    palace_eaves_06: 219,
    palace_eaves_07: 221,
    palace_eaves_08: 223,
    palace_floor: 263,
    palace_lamp: 307,
    palace_roof: 255,
    palace_window: 408,
    pale_green: 103,
    palm: 541,
    paren_close: 511,
    paren_open: 509,
    peach_juice: 430,
    percent: 513,
    period: 515,
    peru: 381,
    pink: 115,
    pink_cake: 337,
    pink_light: 295,
    plank_01: 137,
    plank_02: 139,
    plank_03: 141,
    plank_04: 143,
    plank_05: 641,
    plank_06: 643,
    plank_07: 645,
    polar_ice: 347,
    polar_region: 345,
    pound: 517,
    powder_blue: 93,
    pumpkin: 543,
    pumpkin_lantern: 549,
    purple: 293,
    purple_surface_01: 353,
    purple_surface_02: 355,
    quartz_brick: 155,
    question_mark: 15,
    quotation_mark: 519,
    rainbow_cube: 581,
    red: 105,
    red_brick: 153,
    red_brick_floor: 259,
    red_brick_wall: 261,
    red_decorative_light: 570,
    red_gift: 555,
    red_glass: 304,
    red_light: 281,
    rock: 359,
    roof_blue_04: 231,
    roof_green: 229,
    roof_grey: 407,
    roof_purple: 227,
    roof_red: 225,
    roof_yellow: 233,
    sakura_pink: 117,
    sand: 135,
    semicolon: 521,
    seven: 31,
    sienna: 393,
    six: 29,
    sky_blue: 91,
    slash: 523,
    snow: 169,
    snowflake_lamp: 565,
    snowland: 343,
    snowman_body: 561,
    snowman_head: 559,
    soy_sauce: 426,
    spiderweb: 544,
    stained_glass: 123,
    stainless_steel: 247,
    star_lamp: 562,
    stone: 129,
    stone_brick_01: 323,
    stone_brick_02: 325,
    stone_pillar_03: 267,
    stone_pillar_04: 269,
    stone_pillar_05: 271,
    stone_pillar_06: 273,
    stone_wall: 275,
    stone_wall_01: 335,
    strawberry_juice: 412,
    stripe_01: 185,
    stripe_02: 187,
    stripe_03: 189,
    stripe_04: 191,
    stripe_05: 193,
    subtract: 5,
    television: 481,
    three: 23,
    tilde: 525,
    toolbox: 647,
    traditional_window: 578,
    treasure_chest: 649,
    turquoise: 367,
    two: 21,
    warm_yellow_light: 301,
    water: 364,
    white: 177,
    white_grass: 539,
    white_light: 299,
    window: 160,
    windygrass: 469,
    winter_leaf: 527,
    wood: 257,
    wooden_box: 179,
    woodstone_12: 411,
    yellow_decorative_light: 572,
    yellow_grass: 477,
    yellow_green: 101,
    yellow_light: 285,
    zero: 17,
};
(async function () {
    "use strict";
    let mode, lastUrl;
    const stats = new Stats();
    document.body.append(stats.domElement);
    function log(...m) {
        GM_log("[Box3++]", m.join(" "));
    }
    function checkMode() {
        let url = location.href;
        mode = null;
        const matches = {
            "^https://box3.+/maas": "maas",
            "^https://box3.+/e/.+": "editor",
            "^https://box3.+/p/.+": "play",
            "^https://box3.+/me/content": "map-create",
            "^https://box3.+/g/.+": "game-view",
            "^https://box3.+/p/.+": "game-play",
        };
        for (let e of Object.keys(matches)) {
            if (new RegExp(e).test(url)) mode = matches[e];
        }
        log("ModeChecked", mode || "<NONE " + url + ">");
    }
    function getWebsiteCore() {
        return document.querySelector(".desktop")._reactRootContainer._internalRoot
            .current.updateQueue.baseState.element.props.children.props.website;
    }
    async function g2etWebsiteCore() {
        return await document.querySelector(".desktop")._reactRootContainer._internalRoot
            .current.updateQueue.baseState.element.props.children.props.website;
    }
    function getGameplayCore() {
        return document.querySelector(".desktop")._reactRootContainer._internalRoot
            .current.updateQueue.baseState.element.props.children.props.children
            .props;
    }
    function getEditorCore() {
        return document.querySelector(".desktop")._reactRootContainer._internalRoot
            .current.updateQueue.baseState.element.props.children.props.children
            .props;
    }
    async function waitElement(selector) {
        let el;
        while (!el) {
            el = document.querySelector(selector);
            await new Promise(requestAnimationFrame);
        }
        return el;
    }
    Object.assign(document, { getWebsiteCore, getGameplayCore, getEditorCore });
    let gui;
    async function setupGameMode() {
        gui.title("   Box3++ 工具箱 (GameplayMode)");
        let loadingLabel = gui.add({ a() {} }, "a").disable();
        function setLoading(s) {
            if (!!s) loadingLabel.name("⏳ " + s + "...");
            else loadingLabel.name("✅ GameplayMode 准备就绪");
        }

        setLoading("正在进入地图");
        const state = getGameplayCore().state;
        const core= getGameplayCore()
        const box3CoreElement = document.querySelector("#react-container");
        const reactNodeName = Object.keys(box3CoreElement).filter((v) =>
                                                                  v.includes("reactContain")
                                                                 )[0];
        const core2 =
              box3CoreElement[reactNodeName].updateQueue.baseState.element.props.children
        .props.children.props;console.log(core2)
        document.func = document.querySelector('.desktop')._reactRootContainer._internalRoot.current.updateQueue.baseState.element.props.children.props.children.props.state;
        console.dir(core2)
        Object.assign(document, { state });
        await new Promise(requestAnimationFrame);
        await getGameplayCore().start();
        state.brpc.skin.api
            .getAll()
            .then(
            (a) => (state.box3.state.secret.availableSkin = a.map((o) => o.name))
        );
        while (state.appState !== 3) {
            await new Promise(requestAnimationFrame);
        }
        while (true) {
            if (document.func.box3.loading.appLoaded) break;
            await sleep(100);
        };
        console.dir(core2.brpc.content.api.get({
            type: "id",
            data: {
                contentId: state.box3.state.config.contentId,
                isPublic: true,
                meshHash: true,
                type: 1,
                userId: 0,
                Hash: true,
            },
        }))
        confirm('本次启动用时' + document.func.box3.loading.pending + 'ms');
        var box3plus = {
            flying: false,
            // saveblocks: [],
            playerIndex: 0,
            cameraFovY: 0.25,
            scale: 0.5,
            "t":()=>{box3plus.scale=10;},
            "e":()=>{box3plus.scale=1;}
        };
        console.log("state.box3.state:\n" + state.box3.state);
        setInterval(() => {
            state.state.uiState.user.displayname=""
            core2.state.box3.state.camera.distance=500;
            state.state.uiState.userName=""
            state.state.box3.state.physics.gravity=0;
            state.state.box3.state.secret.skin.head="";
            state.state.box3.client.running=false;
            Object.keys(state.state.box3.state.animations).forEach((x) => {
                x.position[0]=0;
                x.position[1]=0;
                x.position[2]=0;
                //state.box3.state.replica.damage[x].hp=state.box3.state.replica.damage[x].maxHp;
            })
        }, 0.5);
        console.dir(state.box3.state.replica.damage)
        console.log(core2.uiState);
        console.log("core:")
        console.log(core2)

        setLoading(null);
        const options = {
            transparentSkin() {
                core.setSkin({
                    head: "none",
                    hips: "none",
                    leftFoot: "none",
                    leftHand: "none",
                    leftLowerArm: "none",
                    leftLowerLeg: "none",
                    leftShoulder: "none",
                    leftUpperArm: "none",
                    leftUpperLeg: "none",
                    neck: "none",
                    rightFoot: "none",
                    rightHand: "none",
                    rightLowerArm: "none",
                    rightLowerLeg: "none",
                    rightShoulder: "none",
                    rightUpperArm: "none",
                    rightUpperLeg: "none",
                    torso: "none",
                });
                Object.keys(state.box3.uiState.playerSkin).forEach((x)=>{state.box3.uiState.playerSkin[x]=""});
                state.box3.uiState.isFlying=true;
                state.box3.user.displayname=true;
            },
            cameraMode: state.box3.state.secret.replica.camera.mode,
            inhtml(){
                const contentId=state.box3.state.config.contentId;
                alert("获取到链接!正在进入...")
                window.open("https://box3.codemao.cn/g/" + contentId);
                alert("成功进入!")
            }
        };
        const skinFolder = gui.addFolder("皮肤");
        core2.state.box3.state.camera.distance=500;
        skinFolder.add(options, "transparentSkin").name("切换为透明皮肤");
        const mapData = gui.addFolder("地图信息");
        mapData.add(options, "inhtml").name("打开此地图主页");
        const cameraFolder = gui.addFolder("   摄像机视角");
        cameraFolder
            .add(state.box3.state.secret.replica.camera, "mode", {
            第三人称: 0,
            "固定(FIXED)": 1,
            第一人称: 2,
            固定方向: 3,
            锁定: 4,
        })
            .name("视角模式")
            .onChange((value) =>
                      value === 0
                      ? cameraDistanceSlider.enable()
                      : cameraDistanceSlider.disable()
                     );
        cameraFolder
            .add(state.box3.state.secret.replica.camera, "fovY", 0, 1, 0.01)
            .name("视场角(FOV)");
        const cameraDistanceSlider = cameraFolder
        .add(state.box3.state.secret.replica.camera, "distance", 0.1, 100)
        .name("摄像机距离");
        const videoEffectsFolder = gui.addFolder("   显示");
        videoEffectsFolder
            .add(state.box3.state.secret.replica, "enableCursor")
            .name("启用3D光标");

        videoEffectsFolder.add(state.box3.state, "hideUI").name("  隐藏界面");
        const networkFolder = gui.addFolder("   网络");
        networkFolder.add(state.box3.state.secret, "netPaused").name("网络暂停");
        const settingsFolder = gui.addFolder("⚙ 画质&音频 设置").close();
        const voxelEditorObj = {
            sx: 0,
            sy: 0,
            sz: 0,
            ex: 128,
            ey: 128,
            ez: 128,
            v: 364,
            replaceMode: false,
            replaceTarget: 0,
            d: 2048,
            blockFillAbort: false,
            async fill() {
                fillButton.disable().name("⌛ 正在填充...");
                if (this.ex < this.sx || this.ey < this.sy || this.ez < this.sz) {
                    alert("输入信息错误 (结束坐标不能大于起始坐标)");
                    fillButton.enable().name("开始填充");
                    abortFillButton.disable();
                    return;
                }
                let total =
                    (this.ex + 1 - this.sx) *
                    (this.ey + 1 - this.sy) *
                    (this.ez + 1 - this.sz);
                let c = 0;
                for (let x = this.sx; x <= this.ex; x++) {
                    for (let y = this.sy; y <= this.ey; y++) {
                        for (let z = this.sz; z <= this.ez; z++) {
                            if (this.blockFillAbort) break;
                            try {
                                if (this.replaceMode) {
                                    if (state.box3.voxel.getVoxel(x, y, z) === this.replaceTarget){
                                        state.box3.voxel._setVoxel(x, y, z, this.v);}
                                } else if (state.box3.voxel.getVoxel(x, y, z) !== this.v) {
                                    state.box3.voxel._setVoxel(x, y, z, this.v);
                                }
                            } catch (e) {
                                console.log("Fill block error", e);
                            }

                            c++;
                            if (this.d > 0 && c % this.d === 0) {
                                fillButton.name(
                                    `⌛ 正在填充 ${((c / total) * 100).toFixed(2)}%`
                                );
                                await new Promise(requestAnimationFrame);
                            }
                        }
                    }
                }
                this.blockFillAbort = false;
                fillButton.enable().name("开始填充");
                abortFillButton.disable();
            },
            start() {
                abortFillButton.enable();
                this.fill();
            },
            abort() {
                this.blockFillAbort = true;
            },
            swithBlocks() {
                const temp = Number(this.v);
                this.v = Number(this.replaceTarget);
                this.replaceTarget = temp;
                voxelsFolder.controllers.forEach((i) => i.updateDisplay());
            },
        };
        var datas = {
            "清空聊天框":()=>{state.box3.state.chat.log=[];},
            "发送长消息":()=>{
                var dialogs = addwindow("发送长消息","可绕过禁言,在下方输入消息,回车换行,Shift+回车发送。\n另外,发送的内容请自行打开“聊天区”查看",500);
                var div1 = addhtml(dialogs.dialog,"div",{class:"div"},"");
                var div2 = addhtml(dialogs.dialog,"div",{class:"div"},"");
                var input = addhtml(div2,"textarea",{style:"width:100%;height:250px;background:#0000;color:#fff;outline: none;resize: none;padding:10px;margin-top:10px"},"")
                var fjcg=()=>{var a = addhtml(dialogs.dialog,"div",{class:"div"},"发送成功!");setTimeout(()=>{a.remove()},2000)}
                var send = ()=>{
                    state.box3.chat.sendMessage(input.value);
                    setTimeout(()=>{input.value=""},100);fjcg()
                }
                var sendwhile=async()=>{
                    while(1){
                        state.box3.chat.sendMessage(input.value);
                        await sleep(100);
                    }
                }
                addhtml(div1,"button",{},"粘贴文本").onclick=async()=>{input.value=await navigator.clipboard.readText();}
                addhtml(div1,"button",{},"一键发送").onclick=send
                addhtml(div1,"button",{},"刷屏").onclick=sendwhile
                input.onkeydown=(e)=>{if(e.key=="Enter"&&e.shiftKey){send()}}
            },
        }
        var p25 = gui.addFolder('聊天框设置');
        p25.add(datas, '清空聊天框').name('清空聊天框');
        p25.add(datas, '发送长消息').name('发送长消息');
        const voxelsFolder = gui.addFolder("   客户端世界编辑").close();

        voxelsFolder.add(voxelEditorObj, "sx", 0, 256, 1).name("起始X");
        voxelsFolder.add(voxelEditorObj, "sy", 0, 256, 1).name("起始Y");
        voxelsFolder.add(voxelEditorObj, "sz", 0, 704, 1).name("起始Z");
        voxelsFolder.add(voxelEditorObj, "ex", 0, 256, 1).name("结束X");
        voxelsFolder.add(voxelEditorObj, "ey", 0, 256, 1).name("结束Y");
        voxelsFolder.add(voxelEditorObj, "ez", 0, 704, 1).name("结束Z");
        voxelsFolder.add(voxelEditorObj, "v", VOXEL_NAME_TO_ID).name("方块");
        voxelsFolder
            .add(voxelEditorObj, "replaceMode")
            .name("替换模式")
            .onChange((v) =>
                      v ? replaceTargetSelect.enable() : replaceTargetSelect.disable()
                     );
        const replaceTargetSelect = voxelsFolder
        .add(voxelEditorObj, "replaceTarget", VOXEL_NAME_TO_ID)
        .name("替换目标方块")
        .disable();
        voxelsFolder
            .add(voxelEditorObj, "d", {
            "最快(易卡顿)": -1,
            极快: 16384,
            较快: 8192,
            快: 4096,
            中: 2048,
            慢: 1024,
            较慢: 512,
            最慢: 256,
            极慢: 128,
            最慢: 1,
        })
            .name("运行速度");
        voxelsFolder
            .add(voxelEditorObj, "swithBlocks")
            .name("   互换填充方块和替换目标方块");
        const fillButton = voxelsFolder
        .add(voxelEditorObj, "start")
        .name("开始填充");
        const abortFillButton = voxelsFolder
        .add(voxelEditorObj, "abort")
        .name("❌ 中止")
        .disable();
        function finishSettings() {
            getGameplayCore().setGameSettings(document.state.uiState.settings);
        }
        [
            settingsFolder
            .add(document.state.uiState.settings, "animationQuality", {
                无: 0,
                最低: 1,
                极低: 2,
                低: 4,
                中: 20,
                高: 100,
                极高: 200,
            })
            .name("动画质量"),
            settingsFolder
            .add(document.state.uiState.settings, "bloom")
            .name("Bloom (荧光效果)"),
            settingsFolder
            .add(
                document.state.uiState.settings,
                "cameraSensitivity",
                0.01,
                3,
                0.01
            )
            .name("视角灵敏度"),
            settingsFolder
            .add(document.state.uiState.settings, "drawDistance", 1, 1024, 1)
            .name("渲染距离(能见距离)"),
            settingsFolder
            .add(document.state.uiState.settings, "effectsMute")
            .name("音效静音"),
            settingsFolder
            .add(document.state.uiState.settings, "effectsVolume", 0, 1)
            .name("音效音量"),
            settingsFolder
            .add(document.state.uiState.settings, "fxaa")
            .name("FXAA抗锯齿"),
            settingsFolder
            .add(document.state.uiState.settings, "gamma", 0, 2)
            .name("伽马"),
            settingsFolder
            .add(document.state.uiState.settings, "hdSky")
            .name("高清天空"),
            settingsFolder
            .add(document.state.uiState.settings, "lowQualityTextures")
            .name("低质量贴图"),
            settingsFolder
            .add(document.state.uiState.settings, "masterMute")
            .name("主音量静音"),
            settingsFolder
            .add(document.state.uiState.settings, "masterVolume", 0, 1, 0.01)
            .name("主音量大小"),
            settingsFolder
            .add(document.state.uiState.settings, "maxParticleGroups", 0, 1024, 1)
            .name("最大粒子组数量"),
            settingsFolder
            .add(document.state.uiState.settings, "maxParticles", 0, 65526 * 2, 1)
            .name("最大粒子特效数量"),
            settingsFolder
            .add(document.state.uiState.settings, "maxSoundEffects", 0, 32, 1)
            .name("最大音效数量"),
            settingsFolder
            .add(document.state.uiState.settings, "musicMute")
            .name("音乐静音"),
            settingsFolder
            .add(document.state.uiState.settings, "musicVolume", 0, 1, 0.01)
            .name("音乐音量"),
            settingsFolder
            .add(document.state.uiState.settings, "parallaxMap")
            .name("视差贴图"),
            settingsFolder
            .add(document.state.uiState.settings, "parallaxDistance", 1, 128, 1)
            .name("视差距离"),
            settingsFolder
            .add(document.state.uiState.settings, "postprocess")
            .name("后期处理特效"),
            settingsFolder
            .add(document.state.uiState.settings, "reflections")
            .name("反射"),
            settingsFolder
            .add(document.state.uiState.settings, "resolutionScale", 0.1, 2, 0.1)
            .name("画面解析度(清晰度)"),
            settingsFolder.add(location, "reload").name("刷新以应用清晰度设置"),
            settingsFolder
            .add(document.state.uiState.settings, "safeShaders")
            .name("使用安全光影"),
            settingsFolder
            .add(document.state.uiState.settings, "shadowResolution", {
                关闭: 0,
                极低: 128,
                较低: 256,
                低: 512,
                中: 1024,
                高: 2048,
                较高: 4096,
                极高: 8192,
                超高: 16384,
            })
            .name("阴影质量"),
            settingsFolder
            .add(document.state.uiState.settings, "uiMute")
            .name("界面音效静音"),
            settingsFolder
            .add(document.state.uiState.settings, "uiVolume", 0, 1, 0.01)
            .name("界面音效音量"),
            settingsFolder
            .add(document.state.uiState.settings, "depthOfField", 0, 100, 1)
            .name("景深强度"),
            settingsFolder
            .add(document.state.uiState.settings, "volumetricScattering")
            .name("体积散射"),
        ].forEach((i) => i.onChange(finishSettings));
        needUpdateFolders.push(
            cameraFolder,
            videoEffectsFolder,
            settingsFolder,
            networkFolder
        );
    }
    async function setupEditorMode() {
        function sleep(ms){
            return new Promise(function (resolve, reject) {
                setTimeout(() => {
                    resolve()
                }, ms);
            });
        };
        gui.title("   Box3++ 工具箱 (EditorMode)");
        let loadingLabel = gui.add({ a() {} }, "a").disable();

        var gupi={
            'getOwner':()=>{
                navigator.clipboard.writeText(document.func.client.state.replica.project.prevHash);
                console.log({'hash':document.func.client.state.replica.project.prevHash})
                confirm('已在控制台输出并复制hash!hash为: '+ document.func.client.state.replica.project.prevHash);
            },
        }

        function setLoading(s) {
            if (!!s) loadingLabel.name("⏳ " + s + "...");
            else loadingLabel.name("✅ EditorMode 准备就绪");
        }

        setLoading("正在进入地图");
        await new Promise(requestAnimationFrame);
        const nstate = getEditorCore();
        await getEditorCore().onStart();
        while (!getGameplayCore().state) {
            await new Promise(requestAnimationFrame);
        }
        const state = getGameplayCore().state;
        Object.assign(document, { state });
        setLoading(null);
        const box3CoreElement = document.querySelector("#edit-react");
        const reactNodeName = Object.keys(box3CoreElement).filter((v) =>
                                                                  v.includes("reactContain")
                                                                 )[0];
        const core =
              box3CoreElement[reactNodeName].updateQueue.baseState.element.props.children
        .props.children.props;
        while (!core) {
            await new Promise(requestAnimationFrame);

        }
        await sleep(64);
        const cameraFolder = gui.addFolder("   摄像机视角");
        // cameraFolder
        //   .add(state.box3.state.camera, "mode", {
        //     第三人称: 0,
        //     "固定(FIXED)": 1,
        //     第一人称: 2,
        //     固定方向: 3,
        //     锁定: 4,
        //   })
        //   .name("视角模式");
        cameraFolder
            .add(state.box3.state.camera, "fovY", 0, 1, 0.01)
            .name("视场角(FOV)");
        cameraFolder
            .add(state.box3.state.camera, "distance", 0.1, 100)
            .name("摄像机距离");
        let getCode = gui.addFolder('爬取代码');
        let permission = gui.addFolder('修改权限');
        var GUI = {
            'str':'',
            'chat':()=>{
                if(!GUI.str)return;
                nstate.client.state.box3.chat.sendGlobalNotice(GUI.str,'发送信息来自' + state.box3.state.secret.userName);
            },
            'getOwner':()=>{
                nstate.client.state.replica.localOwner=true;
                window.all=true
                confirm('获取成功!');
            },
        };
        let msg =gui.addFolder('信息发送');
        msg.add(GUI,'str').name("输入信息文本");
        msg.add(GUI, 'chat').name('发送顶部横幅信息');
        let dict = nstate.client.state.codeEditor.fileDict;
        do{
            dict =nstate.client.state.codeEditor.fileDict;
            console.log(111)
            console.dir(nstate.client.state)
            await sleep(100);
        }while(JSON.stringify(dict)==='{}');
        let options = [];
        for(let x in dict){
            options[dict[x].name]=()=>{
                navigator.clipboard.writeText(dict[x].text);
                confirm('复制代码成功!');
            };
            getCode.add(options,dict[x].name).name(dict[x].name);
        };
        window.all=false
        document.func.client.state.debugging=true;
        var permissionList=nstate.client.state.replica.localPermissions;console.log(permissionList);
        var gug={
            'getOwner':()=>{
                try{
                    document.func.client.replica.stopProject();
                }catch(e){
                    confirm('服务器拒绝了访问')
                    return;
                }
                confirm('关闭成功!');
            },
        }
        var gup={
            'getOwner':()=>{
                try{
                    document.func.client.replica.startProject();
                }catch(e){
                    confirm('服务器拒绝了访问')
                    return;
                }
                confirm('开启成功!');
            },
        }

        permission.add(GUI,'getOwner').name("爬取所有权限");
        permission.add(gug,'getOwner').name("结束运行程序");
        permission.add(gup,'getOwner').name("开始运行程序");
        permission.add(gupi,'getOwner').name("在控制台输出地图hash并复制");
        console.log(state.box3.state.config)
        permission.add(state.box3.state.config, "admin").name("管理员标志");
        permission.add(state.box3.state.config, "development").name("开发模式标志");
        setInterval(function(){
            nstate.client.state.replica.localOwner=true;
            Object.keys(permissionList).forEach((listitem) => {
                if(permissionList[listitem]==false && window.all){
                    permissionList[listitem]=true;
                }
            })
        },100);
        var datas = {
            "清空聊天框":()=>{state.box3.state.chat.log=[];},
            "发送长消息":()=>{
                var datas = {
                    "清空聊天框":()=>{state.box3.state.chat.log=[];},
                    "发送长消息":()=>{
                        var dialogs = addwindow("发送长消息","可绕过禁言,在下方输入消息,回车换行,Shift+回车发送。\n另外,发送的内容请自行打开“聊天区”查看",500);
                        var div1 = addhtml(dialogs.dialog,"div",{class:"div"},"");
                        var div2 = addhtml(dialogs.dialog,"div",{class:"div"},"");
                        var input = addhtml(div2,"textarea",{style:"width:100%;height:250px;background:#0000;color:#fff;outline: none;resize: none;padding:10px;margin-top:10px"},"")
                        var fjcg=()=>{var a = addhtml(dialogs.dialog,"div",{class:"div"},"发送成功!");setTimeout(()=>{a.remove()},2000)}
                        var send = ()=>{
                            state.box3.chat.sendMessage(input.value);
                            setTimeout(()=>{input.value=""},100);fjcg()
                        }
                        var sendwhile=()=>{
                            setInterval(function(){
                                state.box3.chat.sendMessage(input.value);
                            },500);
                        }
                        addhtml(div1,"button",{},"粘贴文本").onclick=async()=>{input.value=await navigator.clipboard.readText();}
                        addhtml(div1,"button",{},"一键发送").onclick=send
                        addhtml(div1,"button",{},"刷屏").onclick=sendwhile
                        input.onkeydown=(e)=>{if(e.key=="Enter"&&e.shiftKey){send()}}
                    },
                }
                },
        }
        var p25 = gui.addFolder('聊天框设置');
        p25.add(datas, '清空聊天框').name('清空聊天框');
        p25.add(datas, '发送长消息').name('发送长消息');
        const videoEffectsFolder = gui.addFolder("   显示");

        videoEffectsFolder.add(state.box3.state, "hideUI").name("  隐藏界面");
        const networkFolder = gui.addFolder("   网络");
        networkFolder.add(state.box3.state.secret, "netPaused").name("网络暂停");
        needUpdateFolders.push(networkFolder, videoEffectsFolder, cameraFolder);
        const voxelEditorObj = {
            sx: 0,
            sy: 0,
            sz: 0,
            ex: 128,
            ey: 128,
            ez: 128,
            v: 364,
            replaceMode: false,
            replaceTarget: 0,
            d: 2048,
            blockFillAbort: false,
            async fill() {
                fillButton.disable().name("⌛ 正在填充...");
                if (this.ex < this.sx || this.ey < this.sy || this.ez < this.sz) {
                    alert("输入信息错误 (结束坐标不能大于起始坐标)");
                    fillButton.enable().name("开始填充");
                    abortFillButton.disable();
                    return;
                }
                let total =
                    (this.ex + 1 - this.sx) *
                    (this.ey + 1 - this.sy) *
                    (this.ez + 1 - this.sz);
                let c = 0;
                for (let x = this.sx; x <= this.ex; x++) {
                    for (let y = this.sy; y <= this.ey; y++) {
                        for (let z = this.sz; z <= this.ez; z++) {
                            if (this.blockFillAbort) break;
                            try {
                                if (this.replaceMode) {
                                    if (nstate.client.state.box3.voxel.getVoxel(x, y, z) === this.replaceTarget){
                                        nstate.client.state.box3.voxel._setVoxel(x, y, z, this.v);}
                                } else if (nstate.client.state.box3.voxel.getVoxel(x, y, z) !== this.v) {
                                    nstate.client.state.box3.voxel._setVoxel(x, y, z, this.v);
                                }
                            } catch (e) {
                                console.log("Fill block error", e);
                            }

                            c++;
                            if (this.d > 0 && c % this.d === 0) {
                                fillButton.name(
                                    `⌛ 正在填充 ${((c / total) * 100).toFixed(2)}%`
                                );
                                await new Promise(requestAnimationFrame);
                            }
                        }
                    }
                }
                this.blockFillAbort = false;
                fillButton.enable().name("开始填充");
                abortFillButton.disable();
            },
            start() {
                abortFillButton.enable();
                this.fill();
            },
            abort() {
                this.blockFillAbort = true;
            },
            swithBlocks() {
                const temp = Number(this.v);
                this.v = Number(this.replaceTarget);
                this.replaceTarget = temp;
                voxelsFolder.controllers.forEach((i) => i.updateDisplay());
            },
        };
        const voxelsFolder = gui.addFolder("   客户端世界编辑").close();

        voxelsFolder.add(voxelEditorObj, "sx", 0, 256, 1).name("起始X");
        voxelsFolder.add(voxelEditorObj, "sy", 0, 256, 1).name("起始Y");
        voxelsFolder.add(voxelEditorObj, "sz", 0, 704, 1).name("起始Z");
        voxelsFolder.add(voxelEditorObj, "ex", 0, 256, 1).name("结束X");
        voxelsFolder.add(voxelEditorObj, "ey", 0, 256, 1).name("结束Y");
        voxelsFolder.add(voxelEditorObj, "ez", 0, 704, 1).name("结束Z");
        voxelsFolder.add(voxelEditorObj, "v", VOXEL_NAME_TO_ID).name("方块");
        voxelsFolder
            .add(voxelEditorObj, "replaceMode")
            .name("替换模式")
            .onChange((v) =>
                      v ? replaceTargetSelect.enable() : replaceTargetSelect.disable()
                     );
        const replaceTargetSelect = voxelsFolder
        .add(voxelEditorObj, "replaceTarget", VOXEL_NAME_TO_ID)
        .name("替换目标方块")
        .disable();
        voxelsFolder
            .add(voxelEditorObj, "d", {
            "最快(易卡顿)": -1,
            极快: 16384,
            较快: 8192,
            快: 4096,
            中: 2048,
            慢: 1024,
            较慢: 512,
            最慢: 256,
            极慢: 128,
            最慢: 1,
        })
            .name("运行速度");
        voxelsFolder
            .add(voxelEditorObj, "swithBlocks")
            .name("   互换填充方块和替换目标方块");
        const fillButton = voxelsFolder
        .add(voxelEditorObj, "start")
        .name("开始填充");
        const abortFillButton = voxelsFolder
        .add(voxelEditorObj, "abort")
        .name("❌ 中止")
        .disable();
        gui
            .add(
            {
                clear() {
                    state.debugger.log = [];
                },
            },
            "clear"
        ).name("清空控制台");
        const projectFolder = gui.addFolder("项目");
        projectFolder.add(core, "restartServer").name("重启");
        console.log("core")
        console.dir(core)
        core.admin=true;
    }


    async function setupHashBlockTools() {
        const folder = gui.addFolder("   HashBlock 工具").close();
        folder
            .add(
            {
                help() {
                    alert(
                        "在Box3中,许多数据都是由hash存储的, 任何资源都可以用hash表示(比如一个地图,图片,版本信息等),如果要知道某个hash表示的内容,就可以通过 https://static.box3.codemao.cn/block/xxxxx 得到"
                    );
                },
            },
            "help"
        )
            .name("❔ 什么是HashBlock");

        const obj = {
            upload() {
                const input = document.createElement("input");
                input.type = "file";
                input.style.display = "none";
                input.addEventListener("change", () => {
                    uploadButton.disable();
                    let reader = new FileReader();
                    reader.addEventListener("load", () => {
                        GM_xmlhttpRequest({
                            method: "post",
                            url: "https://static.box3.codemao.cn/block",
                            data: reader.result,
                            binary: true,
                            onload({ response }) {
                                const { Key, Size } = JSON.parse(response);
                                alert(
                                    "上传成功! (如果上传了图片,可通过图片工具查看,hash已自动填入)\n\nHash: " +
                                    Key +
                                    "\n\n Size:" +
                                    Size
                                );
                                imageToolsObj.hash = Key;
                                uploadButton.enable();
                                input.remove();
                            },
                        });
                    });
                    reader.readAsBinaryString(input.files[0]);
                });

                input.click();
            },
            openByHash() {},
            write() {
                const input = document.createElement("input");
                input.type = "file";
                input.style.display = "none";
                input.addEventListener("change", () => {
                    writeButton.disable();
                    let reader = new FileReader();
                    reader.addEventListener("load", () => {
                        const textt = reader.result
                        GM_xmlhttpRequest({
                            method: "post",
                            url: "https://static.box3.codemao.cn/block",
                            data: textt,
                            binary: true,
                            async onload({ response }) {
                                const { Key, Size } = JSON.parse(response);
                                alert(
                                    "上传成功!\n\nHash: " +
                                    Key +
                                    "\n\n Size:" +
                                    Size
                                );
                                window.open("https://static.box3.codemao.cn/block/"+Key + '.html')
                                writeButton.enable();
                            },
                        });
                    });
                    reader.readAsBinaryString(input.files[0]);
                });

                input.click();
            },
        };
        const uploadButton = folder.add(obj, "upload").name("上传文件");
        const writeButton = folder.add(obj, "write").name("上传html文件");
        const imageFolder = folder.addFolder("   图片工具");
        const imageToolsObj = {
            width: 0,
            height: 0,
            hash: "",
            type: ".png",
            open() {
                open(
                    `https://static.box3.codemao.cn/block/${this.hash}_cover_${this.width}_${this.height}${this.type}`
                );
            },
        };
        needUpdateFolders.push(imageFolder);
        imageFolder.add(imageToolsObj, "width").name("宽度");
        imageFolder.add(imageToolsObj, "height").name("高度");
        imageFolder.add(imageToolsObj, "hash").name("HASH");
        imageFolder
            .add(imageToolsObj, "type", [
            ".png",
            ".jpg",
            ".jpeg",
            ".webm",
            ".gif",
            ".gmp",
            ".tga",
            ".dds",
            ".eps",
            ".hdr",
            ".raw",
        ])
            .name("图片格式");
        imageFolder.add(imageToolsObj, "open").name("打开图片");
    }
    async function setup() {
        if (gui) gui.destroy();

        gui = new lil.GUI({ title: "   Box3++ 工具箱" });
        gui.domElement.style.top = "unset";
        gui.domElement.style.bottom = "0";
        gui.domElement.style.userSelect = "none";

        const statsFolder = gui.addFolder("   性能监视器").close();
        statsFolder
            .add({ opacity: 0.9 }, "opacity", 0, 1)
            .name("透明度")
            .onChange((v) => {
            stats.domElement.style.opacity = v.toString();
            stats.domElement.style.display = v > 0 ? "block" : "none";
        });
        statsFolder
            .add({ zoom: 1 }, "zoom", 0.1, 5)
            .name("缩放")
            .onChange((v) => {
            stats.domElement.style.zoom = v.toString();
        });
        statsFolder
            .add(stats.domElement.style, "top", {
            上: "0px",
            下: "calc(100vh - 48px)",
        })
            .name("垂直位置");
        statsFolder
            .add(stats.domElement.style, "left", {
            左: "0px",
            右: "calc(100vw - 80px)",
        })
            .name("水平位置");

        function setUIWidth(v) {
            gui.domElement.style.width = v + "vw";
        }
        setUIWidth(GM_getValue("uiWidth", 20));
        gui
            .add({ width: GM_getValue("uiWidth", 20) }, "width", 10, 80)
            .name("工具箱界面宽度")
            .onFinishChange((v) => {
            GM_setValue("uiWidth", v);
            setUIWidth(v);
        });
        setupHashBlockTools();
        if (mode === "maas") {
            const folder = gui.addFolder("   Maas (商业版首页)");
            folder
                .add(
                {
                    go() {
                        location.href = "https://box3.codemao.cn";
                    },
                },
                "go"
            )
                .name("切换到原版首页");
        } else if (mode === "map-create") {
            var url = window.location.hash; /* 获取锚点(“#”后面的分段) */
            //alert(url); /* #test?name=test */
            if(url!=''){
                window.close();
            }
            const folder = gui.addFolder("✨ 创建地图");
            const obj = {
                createNormal() {
                    document
                        .querySelector(
                        "#main > main > div._3IbS6Ew1CROpnsaTbrniXH.pHRRH-pJlcoCY3qP0gcFI > div > div > div._1xZbWt8b7cKJ8PPcK2Ho81.-y0as17U00f3yUjy_bm6K"
                    )
                        .click();
                },
                async reload(){
                    for (let i = 0; i < 3; i++) {
                        document
                            .querySelector(
                            "#main > main > div.bg-white.mb-24.p-24-0.cKMigh6PpW3tleaZK6J1R > div > div.hAB8LjZSi73-MLk-0ZUWg.tab-bar > button._3AspHqpBNnv2Z9vUyC6Fnx.vbojj-sJcBnYnXKqRwxoU._12b-ZtA2Hl4-wYcKqK83AR._1SS6wc-FMtveQU1rUrkRW.Lz4uEvJd_qOzG39N7jnOg._1KXyfkOCOG7H7xR_ULs_R7._3mGcht4WhuRtvCwPGKNEvg"
                        )
                            .click();
                        await new Promise((r) => setTimeout(r, 100));
                    }
                    location.reload();
                },
                async createpg(){
                    var tools = {
                        createMap: async (x, y, z, storageMode) => {
                            return new Promise((resolve, reject) => {
                                if (x * y * z < 32768 || x * y * z > 67108864 || (x * y * z % 32768 != 0)) reject('Value Error: x*y*z必须是32768的倍数且在32768~67108864之间');
                                if (!['sqlite', 'pg'].includes(storageMode)) reject('Value Error: 数据库必须为sqlite/pg');
                                const voxels = {
                                    chunks: Array(x * y * z / 32768)
                                    .fill('QmYUffAgALxiUQonbhAVXjknTq3dNf3AfHQGQ8P5xny7TU'),
                                    shape: {
                                        x: x,
                                        y: y,
                                        z: z
                                    }
                                };
                                interiorTools.hashBlock(JSON.stringify(voxels))
                                    .then((hash) => {
                                    interiorTools.hashBlock(`{"ambientSound":"QmcNbLSSQfVcDpH9jSX38RSVrL1SZK3vNMZwaP7cMkKqvY","assets":"QmRRMcxmp8ic6kQRk3ieciT62uFPoeaxmTTm9b3hH9Wudt","collisionFilter":[],"committerId":0,"deleteAssets":"QmTgK2uYPscacJ9KaBS8tryXRF5mvjuRbubF7h9bG2GgoN","editRoot":"QmTgK2uYPscacJ9KaBS8tryXRF5mvjuRbubF7h9bG2GgoN","entities":"QmSvPd3sHK7iWgZuW47fyLy4CaZQe2DwxvRhrJ39VpBVMK","environment":"QmXePHzJ89XRkjME6bvw6hEnZgy3T8PpXFcYixo2BSHUCL","features":{"enableTriggerAPI":true},"folders":"QmSvPd3sHK7iWgZuW47fyLy4CaZQe2DwxvRhrJ39VpBVMK","info":"QmYoj6De7docrG9Ys5vuZskif6rMEBUpGJbFCpoRY1tF2b","physics":"QmTzt6Z6Mm11NQjTeXspDMJtddzDadzwhgwfWUtNG5XCrD","player":"QmSSNY4w5w9fTxvVTK3ENa1nnuYhogjgFHYricnYK4tg3k","prevHash":"QmYvXUJ3Vwn4WS7q4oHBMMzbH8o7aD18S2qYEiXpMotvgX","scriptAssets":"QmXTzyU4vrAsjViTWs5med452kMKDSjebL1a2TafjQmus7","scriptIndex":"index.js","storageMode":"${storageMode}","type":"project","version":"0.3.11","voxels":"${hash}","zones":"QmTgK2uYPscacJ9KaBS8tryXRF5mvjuRbubF7h9bG2GgoN"}`)
                                        .then((hash) => {
                                        axios({
                                            method: 'post',
                                            url: 'https://backend.box3.fun/container/create-game-edit',
                                            data: JSON.parse(`{"image":"Qmdkqjkx8YXCEzQuNrZhEr75dpRGHcWY7oiCxg5oQqfzox.png","name":"${x} * ${y} * ${z} ${storageMode} 地图","describe":"创建的其它大小其它数据库地图","hash":"${hash}","resourceId":0}`),
                                            withCredentials: true
                                        })
                                            .then(({
                                            request
                                        }) => {
                                            resolve(JSON.parse(request.responseText)['data']['value'].slice(5));
                                        });
                                    });
                                });
                            });
                        }
                    };

                    var interiorTools = {
                        hashBlock: async (data) => {
                            return new Promise((resolve) => {
                                GM_xmlhttpRequest({
                                    method: 'post',
                                    url: 'https://static.box3.codemao.cn/block',
                                    data: data,
                                    onload: ({
                                        response: res
                                    }) => {
                                        resolve(JSON.parse(res)
                                                .Key);
                                    }
                                });
                            });
                        }
                    };
                    tools.createMap(32, 1024, 1024, 'pg').then(console.log); // 更改这里的参数,此处为32*1024*1024的pg数据库地图
                },
                async createLarge() {
                    if (confirm("确认创建?")) {
                        var mapname = "复制的地图"
                        var themaphash = prompt('请输入地图hash(0为704巨大地图,1为1024超大地图,hash可以在创作端获取,把hash复制过来可以复制地图(不会复制合作者)):');
                        if(!themaphash){confirm("请输入hash!");return;}
                        if (!/^[A-Za-z0-9]+$/.test(themaphash)){confirm(themaphash+'不是有效的hash!');return;}
                        if(themaphash=='0'){
                            themaphash="QmTuELNrZixUHYytsqJAUCw8R22868ePtkNCQ4DMUd8wCg";
                            mapname="704地图"
                        }else if(themaphash=='1'){
                            themaphash = "QmNorKXGb2RwP3KRQBpkH2vfJJ4ziva5qMc1cU6SJyBSTa";
                            mapname="1024地图"

                        }
                        createLargeMapButton.name("创建中...").disable();
                        window.creathashmap(themaphash,"复制的地图");
                        for (let i = 0; i < 3; i++) {
                            document
                                .querySelector(
                                "#main > main > div.bg-white.mb-24.p-24-0.cKMigh6PpW3tleaZK6J1R > div > div.hAB8LjZSi73-MLk-0ZUWg.tab-bar > button._3AspHqpBNnv2Z9vUyC6Fnx.vbojj-sJcBnYnXKqRwxoU._12b-ZtA2Hl4-wYcKqK83AR._1SS6wc-FMtveQU1rUrkRW.Lz4uEvJd_qOzG39N7jnOg._1KXyfkOCOG7H7xR_ULs_R7._3mGcht4WhuRtvCwPGKNEvg"
                            )
                                .click();
                            await new Promise((r) => setTimeout(r, 100));
                        }
                        location.reload();
                        confirm('操作成功!(若界面无法打开,则表明hash不正确)')

                    }
                },
            };

            folder.add(obj, "createNormal").name("创建普通地图");
            const createLargeMapButton = folder
            .add(obj, "createLarge")
            .name("创建特殊地图")
            var c=folder
            .add(obj, "reload")
            .name("复制或删除了地图看不到效果?点击此按钮解决问题");
            console.dir(getWebsiteCore().rpc.container.api.getHash(""))
        } else if (mode === "game-view") {
            console.log(getWebsiteCore())
            const folder = gui.addFolder("   信息爬取");
            const obj = {
                async getData(){
                    return await getWebsiteCore().rpc.content.api.get({
                        type: "id",
                        data: {
                            contentId: Number(/\/g\/(\w+)/.exec(location.href)[1]),
                            type: 1,
                            isPublic: true,
                        },
                    });
                },
                async openHash() {
                    open(
                        "https://static.box3.codemao.cn/block/" + (await obj.getData()).hash
                    );
                },
                async logData() {
                    console.dir(await obj.getData());
                },
                async viewImage() {
                    open(
                        `https://static.box3.codemao.cn/block/${
                        (await obj.getData()).image
                        }_cover_1024_1024.png`
                    );
                },
                async openEdit() {
                    const t =getWebsiteCore().rpc.content.api
                    .get({
                        type: "id",
                        data: {
                            contentId: Number(location.pathname.replace("/g/", "")),
                            isPublic: true,
                            meshHash: true,
                            type: 1,
                            userId: 0,
                        },
                    });
                    t.catch(reason=> {
                        var hash=reason;
                        hash=JSON.parse(hash);
                        console.log(hash);
                        window.hash=JSON.parse(hash.body)[0];
                        GM_xmlhttpRequest({
                            method: "get",
                            url: "https://static.box3.codemao.cn/block/" + window.hash,
                            headers: {
                                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36 Edg/105.0.1343.27"
                            },
                            onload: function(res){
                                if(res.status === 200){
                                    console.log('成功')
                                    var restext = JSON.parse(res.responseText).versionControl
                                    GM_xmlhttpRequest({
                                        method: "get",
                                        url: "https://static.box3.codemao.cn/block/" + restext,
                                        headers: {
                                            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36 Edg/105.0.1343.27"
                                        },
                                        onload: function(res1){
                                            if(res.status === 200){
                                                console.log('成功')
                                                restext = JSON.parse(res1.responseText).branches.master.headHash
                                                console.log(restext)
                                                navigator.clipboard.writeText(restext);
                                                alert("此地图hash为: " + restext + "\n现在已经复制并在控制台输出!")
                                            }else{
                                                console.log('失败')
                                                console.log(res)
                                            }
                                        },
                                        onerror : function(err){
                                            console.log('error')
                                            console.log(err)
                                        }
                                    });
                                }else{
                                    console.log('失败')
                                    console.log(res)
                                }
                            },
                            onerror : function(err){
                                console.log('error')
                                console.log(err)
                            }
                        });
                    })

                },
                async copymap(){
                    const t =getWebsiteCore().rpc.content.api
                    .get({
                        type: "id",
                        data: {
                            contentId: Number(location.pathname.replace("/g/", "")),
                            isPublic: true,
                            meshHash: true,
                            type: 1,
                            userId: 0,
                        },
                    });
                    t.catch(reason=> {
                        var hash=reason;
                        hash=JSON.parse(hash);
                        window.getWebsiteCore = getWebsiteCore
                        console.log(hash);
                        window.hash=JSON.parse(hash.body)[0];
                        GM_xmlhttpRequest({
                            method: "get",
                            url: "https://static.box3.codemao.cn/block/" + window.hash,
                            headers: {
                                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36 Edg/105.0.1343.27"
                            },
                            onload: function(res){
                                if(res.status === 200){
                                    console.log('成功')
                                    var restext = JSON.parse(res.responseText).versionControl
                                    GM_xmlhttpRequest({
                                        method: "get",
                                        url: "https://static.box3.codemao.cn/block/" + restext,
                                        headers: {
                                            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36 Edg/105.0.1343.27"
                                        },
                                        onload: async function(res1){
                                            if(res.status === 200){
                                                console.log('成功')
                                                restext = JSON.parse(res1.responseText).branches.master.headHash
                                                console.log(restext)
                                                window.creathashmap(restext,"复制的地图")
                                                console.log("复制成功")
                                                alert("成功复制了此地图!\n\n可在创作与学习界面查看\n\n即将刷新界面");
                                                for(let i=0;i<10;i++){
                                                    document.querySelectorAll("a")[4].click()
                                                }
                                                window.open("https://box3.codemao.cn/me/content#reload",'_blank');
                                                location.reload();

                                            }else{
                                                console.log('失败')
                                                console.log(res)
                                            }
                                        },
                                        onerror : function(err){
                                            console.log('error')
                                            console.log(err)
                                        }
                                    });
                                }else{
                                    console.log('失败')
                                    console.log(res)
                                }
                            },
                            onerror : function(err){
                                console.log('error')
                                console.log(err)
                            }
                        });
                    })
                },
                async openGame(){
                    var t=await obj.getData();
                    t=t.play_container_name;
                    t=t.slice(5);
                    console.log(t);
                    alert("成功找出链接,正在打开...");
                    window.open("https://box3.codemao.cn/p/" + t);
                    alert("成功进入此图游戏!")
                },
                async openhash(){
                    const t =getWebsiteCore().rpc.content.api
                    .get({
                        type: "id",
                        data: {
                            contentId: Number(location.pathname.replace("/g/", "")),
                            isPublic: true,
                            meshHash: true,
                            type: 1,
                            userId: 0,
                        },
                    });
                    t.catch(reason=> {
                        var hash=reason;
                        hash=JSON.parse(hash);
                        console.log(hash);
                        hash=JSON.parse(hash.body)[0];
                    })
                    console.log(11)

                }

            };
            console.log(getWebsiteCore().rpc.content.api)
            folder.add(obj, "logData").name("在控制台输出content数据");
            folder.add(obj, "viewImage").name("查看高清地图封面(1024*1024)");
            folder.add(obj, "openEdit").name("在控制台输出此地图hash并复制");
            folder.add(obj, "copymap").name("复制此地图");
            //folder.add(obj, "openhash").name("进入此图特殊信息网页(其中prevhash是复制地图的hash)"); //bug,无法使用
            folder.add(obj, "openGame").name("进入此图游戏");
        } else if (mode === "game-play") {

            const startButton = gui
            .add(
                { setupGameMode: () => setupGameMode() && startButton.destroy() },
                "setupGameMode"
            )
            .name("   进入地图并启动GameplayMode");
            (
                await waitElement(
                    "#react-container > div > div.O66fmAuhyYLyfNI6acu4f > div._2CGySt2UC265XvYttBgcIv"
                )
            ).addEventListener("click", () => {
                startButton.disable().name("   已手动进入地图");
            });
        } else if (mode === "editor") {
            document.func = document.querySelector(".desktop")._reactRootContainer._internalRoot.current.updateQueue.baseState.element.props.children.props.children.props;
            console.log(document.func)
            var gupi={
                'getOwner':()=>{
                    navigator.clipboard.writeText(document.func.client.state.replica.project.prevHash);
                    console.log({'hash':document.func.client.state.replica.project.prevHash})
                    confirm('已在控制台输出并复制hash!hash为: '+ document.func.client.state.replica.project.prevHash);
                },
            }
            const startButton = gui
            .add(
                { setupEditorMode: () => setupEditorMode() && startButton.destroy() },
                "setupEditorMode"
            )
            .name("   进入编辑器并启动EditorMode");
            (
                await waitElement(
                    "#edit-react > div > div._5nY6rqz-36T32MKojdWKN > div._2ts7vbxFxGrpFZ13IL0EJl > button"
                )
            ).addEventListener("click", () => {
                startButton.disable().name("   已手动进入编辑器");
            });
        } else {
            gui.addFolder("❌ 此界面没有增强工具可用").close();
        }
    }
    await waitElement(".desktop");

    const needUpdateFolders = [gui];

    function update() {
        requestAnimationFrame(update);
        stats.update();
        if (location.href !== lastUrl) {
            lastUrl = location.href.toString();
            checkMode();
            setup();
        }
        needUpdateFolders.forEach((i) => {
            if (i) i.controllers.forEach((j) => j.updateDisplay());
            else needUpdateFolders.splice(needUpdateFolders.indexOf(i), 1);
        });
    }
    update();
})();