Hacker1254 / MineKhan-Modded

This is a Mod of Minekhan
3 stars 4 forks source link

Chat! #1

Open Hacker1254 opened 3 years ago

Hacker1254 commented 3 years ago

Ok this is where we now chat

Hacker1254 commented 3 years ago

this has to be fixed image

Hacker1254 commented 3 years ago

oh and should i make the biomes bigger to seem more closer to the real game?

Hacker1254 commented 3 years ago

@GuineaPigBridge where are you?

Hacker1254 commented 3 years ago

i need you as you are the main part of this project, im not super good at coding

ghost commented 3 years ago

Ok. So the hearts just need to be responsive. To do this, we just need to make the x and y pos of the hearts canvas.width or height/n.

Hacker1254 commented 3 years ago

ok how do we do that?

Hacker1254 commented 3 years ago

oh and how do you think we are gonna put in unlimited jumps when swiming?

ghost commented 3 years ago

Ok, so for the swimming, I was thinking of a if

(k === " " ) {
                if (Date.now() < p.lastJump + 0) {
                    p.velocity.y = p.jumpSpeed;

                } else {
                    p.lastJump = Date.now()
                }
            }

In the water collision.

Hacker1254 commented 3 years ago

hmm lets see if that works

Hacker1254 commented 3 years ago

sorry my teatcher is watching me

Hacker1254 commented 3 years ago

what line dose it go on?

ghost commented 3 years ago

Line 2425

ghost commented 3 years ago

And as for the hearts 💕 you just change the heart for loop into

        for(let i = 0;i < Math.floor(p.health/2);i++) {
            ctx.drawImage(heart, width / 2 - i*20, height / 2 + 280, 15, 15);
        }
Hacker1254 commented 3 years ago

Ok, so for the swimming, I was thinking of a if

(k === " " ) {
              if (Date.now() < p.lastJump + 0) {
                  p.velocity.y = p.jumpSpeed;

              } else {
                  p.lastJump = Date.now()
              }
          }

In the water collision.

im having trouble putting this in, can you send a screenshot of how it has to look like Sorry

Hacker1254 commented 3 years ago
    for(let i = 0;i < Math.floor(p.health/2);i++) {
        ctx.drawImage(heart, width / 2 - i*20, height / 2 + 280, 15, 15);
    }

image

Hacker1254 commented 3 years ago

image

Hacker1254 commented 3 years ago

This is as close as i can get it image

Hacker1254 commented 3 years ago

image

Hacker1254 commented 3 years ago

thisi s what happens when adding base64 of a Minecraf heart image i think i have done everything correct

Hacker1254 commented 3 years ago

https://github.com/Hacker1254/MineKhan-Modded/issues/1#issuecomment-832745337

ghost commented 3 years ago
<img src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAIAAABv85FHAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABSSURBVBhXZY7BDcAwCANZor88O0jH67KZgdgYEZSckDA+KYp58AUIFqjkwjHHi+lBPW8ktTUUcvek++05BBoKvdu1RP6l6xIstYB0CbAT6MLdFzGReAo/QKRrAAAAAElFTkSuQmCC' style='display:none' id='heartimg'>

That's it. It works on mine.

Hacker1254 commented 3 years ago

but we really have to add something We need to add way to swich between hearts and no fly and flying and no hearts, you know what i mean

Hacker1254 commented 3 years ago
<img src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAIAAABv85FHAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABSSURBVBhXZY7BDcAwCANZor88O0jH67KZgdgYEZSckDA+KYp58AUIFqjkwjHHi+lBPW8ktTUUcvek++05BBoKvdu1RP6l6xIstYB0CbAT6MLdFzGReAo/QKRrAAAAAElFTkSuQmCC' style='display:none' id='heartimg'>

That's it. It works on mine.

am i doing something wrong? image

ghost commented 3 years ago

Whu=ich browser are you using?

Hacker1254 commented 3 years ago

we need the hearts to resize like the hotbar does

Hacker1254 commented 3 years ago

Which browser are you using?

idk, i am on a chromebook if the changes anything

ghost commented 3 years ago

Ok. For the resizing hearts it's the same as the positioning.. you just make the value, 15, into something like width / 100

Hacker1254 commented 3 years ago

when testing using http://www.csgnetwork.com/htmlcodetest.html when i downsized the window the heart changed poz

Hacker1254 commented 3 years ago

https://github.com/Hacker1254/MineKhan-Modded/issues/1#issuecomment-832757274 do you think this is possable?

ghost commented 3 years ago

That's what I did.

ghost commented 3 years ago

Look. I will share you the code so you can just ctrl paste it!

ghost commented 3 years ago
<!DOCTYPE html>

<html>
    <head>
        <meta charset="utf-8">
        <title>Beta Survival!</title>
        <link id="vtfont" href="https://fonts.googleapis.com/css2?family=VT323&display=swap" rel="stylesheet">
    </head>
    <style>
        body {
            overflow: hidden; /* Hide scrollbars */
        }
        .world-select {
            width: 99vw;
            min-width: 300px;
            height: calc(100vh - 220px);
            position: absolute;
            bottom: 120px;
            overflow-y: auto;
            background-color: RGBA(0, 0, 0, 0.6);
            justify-content: center;
            margin: 0 auto;
        }
        .world {
            width: 250px;
            height: auto;
            border: 1px solid black;
            font-size: 18px;
            font-family: 'Courier New', Courier, monospace;
            color: rgb(180, 180, 180);
            margin: 0 auto;
            margin-top: 15px;
            padding: 5px;
            cursor: pointer;
        }
        strong {
            color: white;
        }
        .selected {
            border: 3px solid white;
            padding: 3px;
        }
        input[type=text] {
            background-color: black;
            caret-color: white;
            border: 2px solid gray;
            color: white;
            font-size: 24px;
            padding-left: 12px;
        }
        input[type=text]:focus {
            border: 2px solid lightgray;
        }
        #boxcentertop {
            z-index: 1;
            width: 80vw;
            max-width: 400px;
            height: 50px;
            position: relative;
            top: 30px;
            display: block;
            margin: 0 auto;
        }
        .hidden {
            display: none !important;
        }
        #onhover {
            background-color: rgba(0, 0, 0, 0.9);
            color: rgb(200, 200, 200);
            font-family: 'Courier New', Courier, monospace;
            word-wrap: normal;
            width: auto;
            max-width: 400px;
            position: absolute;
            z-index: 1;
            padding: 10px;
            cursor: default;
        }
        #quota {
            display: block;
            position: absolute;
            width: 99vw;
            margin: 0 auto;
            bottom: 110px;
            z-index: 1;
            background-color: RGBA(0, 0, 0, 0.6);
            justify-content: center;
            text-align: center;
            color: white;
        }
        .center {
        margin: auto;
        width: 60%;
        border: 3px solid #73AD21;
        padding: 10px;
        }
    </style>
    <body>
    <canvas id="overlay" tabindex="0" width="600" height="600" style="position: absolute; top: 0px; left: 0px"></canvas>
    <input type="text" id="savebox" class="hidden" spellcheck="false" style="position: absolute; top: 10px; left: 10px; z-index: 1;">
    <input type="text" id="boxcentertop" class="hidden" spellcheck="false">
    <div id="quota" class="hidden"></div>
    <div id="onhover" class="hidden"></div>
    <p id="savedirections" class="hidden" style="position: absolute; top: 40px; left: 10px; z-index: 1; background-color: rgba(255, 255, 255, 0.3);">
        To save your world, copy/paste the saveString<br>
        from this box into the code on line 189.<br>
        var loadString = "Your Code Here";<br>
        Then save the program as a Spin-off.
    </p>
        <p id="message" hidden="true" style="position: absolute; top: 10px; right: 10px; z-index: 1; text-align: right; background-color: rgba(255, 255, 255, 0.3);"></p>
    <img src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAIAAABv85FHAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABSSURBVBhXZY7BDcAwCANZor88O0jH67KZgdgYEZSckDA+KYp58AUIFqjkwjHHi+lBPW8ktTUUcvek++05BBoKvdu1RP6l6xIstYB0CbAT6MLdFzGReAo/QKRrAAAAAElFTkSuQmCC' style='display:none' id='heartimg'>
    <div class="world-select hidden" id="worlds"></div>
    <p id="message" class="hidden" style="position: absolute; top: 10px; right: 10px; z-index: 1; text-align: right; background-color: rgba(255, 255, 255, 0.3);"></p>
    <script>
        // Code edits will erase the world.
        // Place save code here to load your world. Make extra sure you got it copied so you don't paste in the wrong thing and delete your world on accident lol
        var loadString = ""
    </script>
    <script type="x-shader/vertex" id="blockVertexShader">
        attribute vec3  aVertex;
        attribute vec2  aTexture;
        attribute float aShadow;
        varying vec2  vTexture;
        varying float vShadow;
        varying float vFog;
        uniform mat4 uView;
        uniform float uDist;
        uniform vec3 uPos;

        void main(void) {
            vTexture = aTexture;
            vShadow = aShadow > 0.0 ? aShadow : 1.0;
            gl_Position = uView * vec4( aVertex, 1.0);

            float range = max(uDist / 5.0, 8.0);
            vFog = clamp((length(uPos.xz - aVertex.xz) - uDist + range) / range, 0.0, 1.0);
        }
    </script>
    <script type="x-shader/fragment" id="blockFragmentShader">
        #ifdef GL_FRAGMENT_PRECISION_HIGH
            precision highp float;
        #else
            precision mediump float;
        #endif

        uniform sampler2D uSampler;
        varying float vShadow;
        varying vec2 vTexture;
        varying float vFog;

        vec4 fog(vec4 color) {
            color.r += (0.33 - color.r) * vFog;
            color.g += (0.54 - color.g) * vFog;
            color.b += (0.72 - color.b) * vFog;
            return color;
        }

        void main(void){
            vec4 color = texture2D(uSampler, vTexture);
            gl_FragColor = fog(vec4(color.rgb * vShadow, color.a));
            if (gl_FragColor.a == 0.0) discard;
        }
    </script>
    <script type="x-shader/vertex" id="2dVertexShader">
        attribute vec2 aVertex;
        attribute vec2 aTexture;
        attribute float aShadow;
        varying vec2 vTexture;
        varying float vShadow;

        void main(void) {
            vTexture = aTexture;
            vShadow = aShadow;
            gl_Position = vec4(aVertex, 0.5, 1.0);
        }
    </script>
    <script type="x-shader/fragment" id="2dFragmentShader">
        #ifdef GL_FRAGMENT_PRECISION_HIGH
            precision highp float;
        #else
            precision mediump float;
        #endif

        uniform sampler2D uSampler;
        varying vec2 vTexture;
        varying float vShadow;

        void main(void) {
            vec4 color = texture2D(uSampler, vTexture);
            gl_FragColor = vec4(color.rgb * vShadow, color.a);
            if (gl_FragColor.a == 0.0) discard;
        }
    </script>
    <script type="application/javascript">
window.canvas = document.getElementById("overlay")
window.ctx = canvas.getContext("2d")
window.savebox = document.getElementById("savebox")
window.boxCenterTop = document.getElementById("boxcentertop")
window.saveDirections = document.getElementById("savedirections")
window.message = document.getElementById("message")
window.worlds = document.getElementById("worlds")
window.quota = document.getElementById("quota")
var hoverbox = document.getElementById("onhover")
ctx.canvas.width  = window.innerWidth
ctx.canvas.height = window.innerHeight

/* Set this text editor to max width by copying this into the JS console:
document.getElementsByClassName("_1chbfei")[0].setAttribute('style', 'max-width: 100vw !important')
*/
var MathGlob = Math
function MineKhan() {
    let Math = MathGlob
    let liquid = false
    let setPixel, getPixels
    let textures = {
        grassTop: function(n) {
            let r = 0, g = 0, b = 0, d = 0
            for (let x = 0; x < 16; x++) {
                for (let y = 0; y < 16; y++) {
                    d = Math.random() * 0.25 + 0.65
                    r = 0x54 * d
                    g = 0xa0 * d
                    b = 0x48 * d
                    setPixel(n, x, y, r, g, b)
                }
            }
        },
        grassSide: function(n) {
            let r = 0, g = 0, b = 0, d = 0
            let pix = getPixels("0g0g70ordrzz0u30g730wa4vzz0xnyl8f11lrk7315qj7jz1fh47pb6553365533033636350335403653650063306333633300635163503655353653535605335031350330553500033033366333433663663535336655335055335553353530355333033503300333336635353663650660554353355635155305303053556333333366353323553060365553063030663533555365534355335530")
            for (let i = 0; i < pix.length; i += 4) {
                setPixel(n, i >> 2 & 15, i >> 6, pix[i], pix[i+1], pix[i+2], pix[i+3])
            }
            for (let x = 0; x < 16; x++) {
                let m = Math.random() * 4 + 1
                for (let y = 0; y < m; y++) {
                    d = Math.random() * 0.25 + 0.65
                    r = 0x54 * d
                    g = 0xa0 * d
                    b = 0x48 * d
                    setPixel(n, x, y, r, g, b)
                }
            }
        },
        leaves: function(n) {
            let r = 0, g = 0, b = 0, a = 0
            for (let x = 0; x < 16; x++) {
                for (let y = 0; y < 16; y++) {
                    r = 0
                    g = Math.floor(Math.random() * 30 + 100)
                    b = Math.floor(Math.random() * 30)
                    if (Math.random() < 0.35) {
                        a = 0
                    } else {
                        a = 255
                    }
                    setPixel(n, x, y, r, g, b, a)
                }
            }
        },
        oakPlanks: function(n) {
            let r = 0
            for (let y = 0; y < 16; y++) {
                let a = (y & 3) === 3 ? 0.7 : 1
                for (let x = 0; x < 16; x++) {
                    let mid = x === 8 && (y & 7) > 3 && a === 1 ? 0.85 : 1
                    let rit = x === 15 && (y & 7) < 3 && a === 1 ? 0.85 : 1
                    r = (Math.random() * 0.1 + 0.9) * a * mid * rit
                    setPixel(n, x, y, 190 * r, 154 * r, 96 * r)
                }
            }
        },
        hitbox: function(n) {
            for (let x = 0; x < 16; x++) {
                for (let y = 0; y < 16; y++) {
                    setPixel(n, x, y, 0, 0, 0, 255)
                }
            }
        },
        Water: function(n) {
            let r = 0, g = 0, b = 0, a = 0;
            for (let x = 0; x < 16; x++) {
                for (let y = 0; y < 16; y++) {
                    a = 200;
                    r = 0;
                    g = 0;
                    b = random(200, 255);
                    setPixel(n, x, y, r, g, b, a);
                }
            }
        },
        dirt: "0g0g70ordrzz0u30g730wa4vzz0xnyl8f11lrk7315qj7jz1fh47pb6553365533033636350335403653650063306333633300635163503655353653535605335031350330553500033033366333433663663535336655335055335553353530355333033503300333336635353663650660554353355635155305303053556333333366353323553060365553063030663533555365534355335530",
        stone: "0g0g40sywflr0wb8hdr0zdjj0f13tzldr3333211210112222221212222220012121001110111222222233232233222111122111212333312223222222211010131223331331112222110010112211122233323223332222212232223332233332021211001212211122222332222233232111232112200101332112211122111321122222222233332222221123322122",
        logSide: "0g0g60fl1ssf0l5j1fj0qftm2n0wa7mdb14cs7wf16az8xr3143304330341432315230523034133230223052313113324032313232301342413230325232314343134131524131432313422343433143230343243342324331053324324242433315332332414303333432303231430331343230533053135133424052303323531343314231333343143340313114334314134131331432",
        logTop: "0g0g90l5j1fj0qftm2n0wa7mdb0z2esxr15quebj189da7z1cpma671f7ppfj1hzyayn1012101120110111077776768667777017334454555544811738877776777471164766666666756107576445544674601646636666466471165764655656756116576465464674610657646666566460164764434556756116576666666674610757767777787460175454444444447117776676686677711011101120110211",
        bedrock: "0g0g509gy58f0e7f7r30o8fd330rkrev31627mkf3111124324211212133434341443012110110111412224232433202422111112014111121134433112221221102211014432344323443410222122211011213234421122344344442110121213211143334134410144431102221123442334402111321134111112343420211101234433211211234421121011044312301123",
        glass: "0g0g50ybfh8f0znkiyo12rzshr1au95hb1lytipr4444444444444443411111111111111341114111111111104114111111111110413111111111111041111111111111124111111111111110411111111111111241111111111111124111111111111112411111111111111241111111111111123111111111111412411111111111411231111111111111133323222222222233",
        cobblestone: "0g0g60muaccf0r0pekf0un11q711vr5rz1a8mosf1ef1r0f2144011454313543145330554330132314342143342101321132113232134310021354131154443152453321033543234313433211243215332233213541231321542213533311021543232233121341323231101221044532211235411035532354215434213323154331432332113244323212332143111311232121135432",
        mossyCobble: "0g0gb0muaccf0mupnnj0p38xdr0r0pekf0rbmj9b0un11q70w1wkxr0y07svz11vr5rz1a8mosf1ef1r0f4199211276438a9619a8812764813858398951644251118533852182851464110531183233866642a176895308948428981498852118851988111851664158385117641484642305126244558824124185442111155214698112124761318998127651764653885847488164588511858685851588531841183352111338a984",
        stoneBricks: "0g0g70p2gyyn0rkrev30tj2nlr0xf9ou70zdjj0f12psrnj17g8flr5666666666665550645455555444445065454444445534406434554345434330632334544334324053244333345324302222222222222220110011000111111166666650566666663555544065455554544544306435354445544320634344454444343053345433332322305443344322222220222222221111110000111111",
        mossyStoneBricks: "0g0gc0mupnnj0p2gyyn0p38xdr0rbmj9b0rkrev30tj2nlr0w1wkxr0xf9ou70y07svz0zdjj0f12psrnj17g8flrab3668863b88a680b9a28683a9999332ba926363996a2931b9792a679a979721b737793993697591a72397773632297075225752332277924410441110000444886bbba1abbbb6883aaaa991ba9aaaa6a99a9971b97a7a399aa99351b797992a99997371a689a97777573371a897799755223791722757754000041110004400",
        bricks: "0g0g90vz62nz0yhavi712oqn7j13rinsv173m8lb193f4zj1b1w1rz1d7u7sv1j1u51b7742888777458777443513444435144410060033100503112256522225565222887747458777474214444315133333151341110600133105522225565522225677458887474588774435344444153444110511331106133325565222225652224777474287774745144444353444441501111106011111065222225655222566",
        coalOre: "0g0g70ehg7wf0hjr9j30j7xaf30sywflr0wb8hdr0zdjj0f13tzldr6556544543445666554536666553335454331453344511556665655105655644455443346545645556553215542143464552111065105555433556663334466661156543215455645565354100056665354514334663354455555666634106655443366412111054665105556005455554456665566544455555554655555566",
        ironOre: "0g0g80sywflr0wb8hdr0zdjj0f13tzldr1cpl2bj1gbvabj1o4exa71qwyvb33223211210112333221203333220002121005120011265223332322642322311122110013212312223220762217510131227655432542222100223330001133336523210762122312232021644423332021251001330021122222333301643322110033167666421332542223442122221123332233211122222221322222233",
        goldOre: "0g0g80sywflr0wb8hdr0zdjj0f13tzldr1x01czj1y6gem71z13ncv1z141z33223211210112333221203333220002121005120011265223332322642322311122110013212312223220762217610131227655432542222100223330001133336523210762122312232021644423332021251001330021122222333301643322110033167655421332642223442122221123332233211122222221322222233",
        diamondOre: "0g0g80h634zj0sagdtr0sywflr0wb8hdr0zdjj0f13tzldr1845xbz1ndl24f5445433432334555443425555442224343221342233461445554544604544533344332235434534445442764437632353447611054104444322445552223355556145432764344534454243600045554243413223552243344444555523605544332255367611043554604445004344443345554455433344444443544444455",
        redstoneOre: "0g0g90sywflr0wb8hdr0zdjj0f13oi67z13tzldr15wexa71b68mbj1f24cfz1yr4gsf4224211210112444221204444220002121005120011285224442422832422411122110014212412224220862218610141227655342532222100224440001144448524210862122412242021633324442021251001440021122222444401834422110044168655321442832224332122221124442244211122222221422222244",
        lapisOre: "0g0ga04hvenz04hvl6n04ihywv066fd3306r2ozj08z4sfz0sywflr0wb8hdr0zdjj0f13tzldr9889877876778999886669999886668787454386777813889889926329989977788776679867978889866428862576797861242398238888723679978767799993189872643386678998687222236258686627661237725788300799668893588779906612366339998700381039799887783339899877788888899888888899",
        emeraldOre: "0g0g7004swsf06mdmv30sywflr0wb8hdr0zdjj0f13tzldr1ohjdhb5445432232334555443445615442334343223310333422445555225555546133344361324555104445441061243255353445551054434444332232552323355555545461442244534444441053615224243433223310361344444556155551044223455103322553261334455444344441045554455433344554443544444455",
        coalBlock: "0g0g501e50xr03md24f05ul3b308mtq0v0bf3ri73322122002210012222121000210123321000122000022221001243222202210001233222100210020222221000001220132211001122222022210122343221002110123322210000012123221103200212122210002211232102112210012230002113432123322000123420023221000123210012221001222212212221000",
        ironBlock: "0g0gb1dawbnj1fj5rlr1hrdssf1m7r1mn1nlyvwf1pa4wsf1qe8xdr1s2ey9r1t6iyv31tqkz5r1ver01r32233333333222232aaaaa9998777772277777777777777105555555666444402aaaaaa999777771277777777777777105555566664444402aa9999977777771277777777777777105555555566644402aaaaaa999977771277777777777777105555556666444402aaaa9999777777127777777777777712222222112111111",
        goldBlock: "0g0g91kr8um71mphb0f1w77ain1xakkqn1ypvwu71yr43jz1yzk7pb1z0cef31z10mwv2222332223333221285577888776688125664877623324812564877462224471374777462264467137777462267762302877444667762330287444664462232037444664466222613744664466662461364667766666742032667762262776203267762332446261334462332666224132322332662264701101100011001100",
        diamondBlock: "0g0g90434min061d2in0h634zj0l2fpxb0sagdtr0vckf0f1845xbz1ndl24f1z141z33333223332222331378866777664477138445766432235713845766543335561265666543345546126666543346643203766555446643220376555445543323026555445544333412655445544443541245446644444653023446643343664302346643223554341225543223444335123233223443345601101100011001100",
        redstoneBlock: "0g0g50vx660v153407319j36671gh49a71runlz34444444444444444433433222334333443342202222232344332311001120234443211012011244443210000000042344302100000011134421100000000122442231000000111344324110210111234432110004001224443321101001110344322211111122224443124211022323443334433223333344444444444444444",
        lapisBlock: "0g0gd05lqqkf06zt0xr07js8hr07tw35r084kzr308e6ein08e99fj08yjpq708ys8ov0an2j270c0w4cf0dp94hr0fdf5kv98999989cb848484878b78736733677197b8867767623765967877377863366597687666378a63759a77776636a7736197a767676236763596763666a6233625c636636376632775c633687363332371963237873676327186672236338763619763722332677630877633332336667143766633633367708111111610220000",
        emeraldBlock: "0g0g606lfrb306mdmv307ei5fj07xmdbz0iaro5b10c5ptr0000000000000002055555454551144305000000200002430404555411114243050500000002425305050555445212130405054411421203040405451142120305040411020202130404041122120213042105410112420301212222222242030405510000441213012222222222224301011001110014532333333333333332",
        tntTop: "0g0g704qh5a70nyecxr13jyl8f14a4flr1d4tukf1otd8u71szz8jj5665566556655665622462246224622462136210012300234431403344004433566006655660566562246204121402246211120000231023443341000111403356651000000506656211020001140224621360200120112344331031140344335661560500611665621462146124612462236223622362234433443344334433",
        tntSides: "0g0ga07ipw5b0fbhzwf14a4flr1d4tukf1gwtvcv1ldqupr1otd8u71pk09vj1szz8jj1z141z38863886388638863663266326632663266326632663266326632663266326632663266326632663295775577755777799711071550711059745055115157177795717717115505479750571550550759444444444444444433223322332233226632663266326632663266326632663266326632663266323332333233323332",
        tntBottom: "0g0g413jyl8f14a4flr1d4tukf1otd8u72332233223322332300130013001300130013001300130011111111111111111233223322332233230013001300130013001300130013001111111111111111123322332233223323001300130013001300130013001300111111111111111112332233223322332300130013001300130013001300130011111111111111111",
        acaciaLogSide: "0g0g60ma181r0oi99fj0pcavi70t8nim70y9464f139ktmn3143304330341432315230523034133230223052313113324032313232301342413230325232314343134131524131432313422343433143230343243342324331053324324242433315332332414303333432303231430331343230533053135133424052303323531343314231333343143340313114334314134131331432",
        acaciaLogTop: "0g0gb0j7rlz30kvxmgv0ma2nen0o889hb0t8nim713rcxdr16jehof18hk3r31c3oo3j1fprugv1hy2osf432332211231433449999898a889999329556676777766a2395aa999989996910869888888889782197986677668968318688588886886932879868778789784487986876868968448798688887886833869866567789782287988888888968219799899999a96811976766666666691099988988a8899933412233444322114",
        acaciaPlanks: "0g0g711t8qgv13rcxdr16jehof18hk3r31c3oo3j1fprugv1hy2osf5456666656666652554423455544553345554543333445522112210011010010563666636665443545543432344555543345444255544333001121000011121056654466656666535455554333434332654433334444335300122100012110006556655366646566445533433555544443345432554333330110012221010000",
        birchLogSide: "0g0g80f1fcov0qqliwv1gxap6n1o60u7z1ptrf271uar6db1uum5mn1z141z36643366634663366346666777634443677744367666636777732100136777737366321101377631211336344363331001266344677766532343777777334556376336775577775777764455577766336336777766634477636777767777577634436633667553210026677763443100000133377761132116331677764336336",
        birchLogTop: "0g0ga0mk6h3316m5am719xxgqn1cg9ce71f8hx4v1jowirj1nv4jcv1nvimm71tgjy7z1z141z39818811001809889966665657556666816223343444433718627766665666360153655555555645106465334433563580535525555355368154653544545645995465354353563599546535555455358853653323445645115465555555563510646656666676350064343333333336016665565575566688901188999811009",
        birchPlanks: "0g0g717znmrj19xxgqn1cg9ce71f8hx4v1jowirj1nv4jcv1nvimm75456666656666652554423455544553345554543333445522112210011010010563666636665443545543432344555543345444255544333001121000011121056654466666666535455554333434332654433334444335300122100012110006356655366636566445533433554544443345432554333330110012221010000",
        darkOakLogSide: "0g0g60besef30dcwlbz0e6y70f0hj7ev30klcs8v0oho0e73143304330341432315230523034133230223052313113324032313232301342413230325232314343134131524131432313422343433143230343243342324331053324324242433315332332414303333432303231430331343230533053135133424052303323531343314231333343143340313114334314134131331432",
        darkOakLogTop: "0g0gb08ml79b0auqebj0dcmqdb0e6y70f0f117gf0g4r4730h8wirj0hj7ev30kb0idb0lz2akf0n393wf741441100140744779999898a889999419225565666655a1492aa999989995903859888888889681096985566558958408588288885885941869858668689687786985865858958778698588886885844859855256689681186988888888958109699899999a95800965655555555590399988988a8899944701144777411007",
        darkOakPlanks: "0g0g70bejy0v0dcmqdb0g4r4730h8wirj0kb0idb0lz2akf0n393wf5456666656666652554423455544553345554543333445522112210011010010563666636665443545543432344555543345444255544333001121000011121056654466666666535455554333434332654433334444335300122100012110006356655366636566445533433554544443345432554333330110012221010000",
        jungleLogSide: "0g0g90h96cxr0htdywv0m9k4xr0ne277j0orqc5b0rkbldr0sxvim70wunksf0ys83cv4688422222666444444114414441001111137510011441866644668842422224146644411143573444422235311010006646442222444886101000466412222444688411068844441122575311222100755444666664355741100112241201661144866643341111623533422111444644111004661664444664122244442222",
        jungleLogTop: "0g0g90h96cxr0htdywv0rkbldr0ys83cv160fym7188mku71batekf1f77h1b1h5ei2n3212210011213223377776768667777217444454555544812748877776777470164766666666756107576445544674620646646666466472165764655656756336576465464674633657646666566462264764444556756106576666666674600757767777787460175454444444447117776676686677722310122333211013",
        junglePlanks: "0g0g70sxkd1b0xdxkov160fym7188mku71batekf1f77h1b1h5ei2n5456666656666652554423455544553345554543333445522112210011010010563666636665443545543432344555543345444255544333001121000011121056654466666666535455554333434332654433334444335300122100012110006356655366636566445533433554544443345432554333330110012221010000",
        spruceLogSide: "0g0g60csc9vj0cskpof0dmmb5r0geuxof0lf4i670nnb4sf3243304330342431325130513034233130113051323223314031323131302341423130315131324343234232514232431323411343433243130343143341314332053314314141433325331331424303333431303132430332343130533053235233414051303313532343324132333343243340323224334324234232332431",
        spruceLogTop: "0g0g80ix87pb0nnb4sf0p1n6db0qzu7zz0v5xypr0xy569r106bshr11ueyv31012101120110111066665657556666016223343444433711627766665666361153655555555645106465334433563501535525555355361154653533545645115465353353563510546535555455350153653323445645115465555555563510646656666676350164343333333336116665565575566611011101120110211",
        sprucePlanks: "0g0g80nnb4sf0p1n6db0qzu7zz0v5xypr0xe36rj0xy569r106bshr11ueyv36567777767777762665523566655663356665643333556622112210011010010673777737776553656653532355666653356555266655333001121000011121067765577777777636566665333535332765533335555336300122100012110007367766377737677556633533665655553356532665333330110012221010000",
        sand: "0g0g61m6x62n1nb9nnj1opn5dr1r80f7j1scbi0v1u0izgf4223213232132313122121130142502432011422222121122331213133132122125213232322122321223332123122121421151211022121212212212111242112322310131232124212221120212231202321232232012311223212331112121213132145321123323230232323221223235332323203223232332321223232",
        gravel: "0g0g80rufq4f0vqwlbz0zxiprz125i9rz15rvcan1627mkf1d0twqn1dackxr0341152512122521522122312102333522103133522351352232321522512322132741122210253202140133526552213104226515530122553253522311225353521223310256122311652152322132123553102521325022533562113225212132222537415525331025232422215235323311243310351274122232321212",
        blackConcrete: "0g0g40149on3028826702882yn028dp8f1330112022012232303130022112212111032203010022012122012222321221011223213030101033110011212233120230013131003200032022012002002112233122202312230200102211312102222122132011021201223320211021220121122122321331201102120210001220112022023302312210123220102110",
        blackWool: "0g0gf0149on3028826702882yn028dp8f03cc2rj03cc3jz03chptr04gg3cv04glqf305kk3y705kpr0f06oo4jj07ss54v08ww5q70a106bj46348b45ab13993299eebb7742570367bc77dc97bcbbdeb996bc768c47cc96ec9749bb115953bb31314c414641672547eebb8879bea7eeb966eebacc45bca9ccbb35ca4657528733577b117949ee7beeccbbecbacb799839349966bb445911796611cd318b9bee92badecb9717bc77cedc97c99beb559b6424c946cd76ac44bc",
        blueConcrete: "0g0g30c98pof0c98qgv0c9ecqn1000001101000201011001000101120101211111010200000001000110100100220110011001121111001100100100010200102101101000000011110001101010101011020111110000000101121111101100100100010110111010210111001101000100000111110100101111000120000010100000110001200110010002",
        blueWool: "0g0gj0c98pof0c98qgv0c9ecqn0c9edj30ddcr270ddidbz0ddie4f0ehgrnj0ehgsfz0ehmepr0ehmfi70flkt1b0flqfb30flqg3j0flw2db0flw35r0gpugov0gq02yn0gqbd3335237c44ac12992289iidc6632460256de67ge96cebbhic985ce657e46ee85ie9648cc114942cc21214e413541562436iidc7769dia6iic955iicaee34cea9eecb24ea4546427622466d116949ii7biieecciecaed699728249955cb344911695511eg217c9bii92bahiec9617be67eigf86e99cid449b5324e935eh65ae34de",
        brownConcrete: "0g0g30pl5gqn0qp3u9r0qp9hbz0211211202222221222122211211122121011102222222212211200222112101121111211111222121222122122221201212121222112122222221220222222122221201121212022122122110221211122221211011221212121010121011111212122221121221222212122112112112211010222111121211220222221122",
        brownWool: "0g0gi0qp3u9r0qp9hbz0rt7uv30rtdh4v0sxbunz0sxbvgf0u1a8zj0u1fv9b0v5e8sf0v5e9kv0v5jvun0w9i9dr0xdgmwv0xdm9z30yhkni70yhq9rz0zloo3j0zluadb35228d45bd12aa219bhhed7721570257ef77gfa7dfccghdb95df758f47ff95hfb749dd115a52dd21214f413541571526hhed887aehb7hhda55hhdbff25dfbbffdc25fb4557518722576e117b4bhh7chhffddhfdbfe7ab82924bb55dc245b116b5511fg218dachhb1cbghfda717cf77fhgg97fabdhe55ac5214fa25fg75bf24ef",
        cyanConcrete: "0g0g305objsv05obklb05oh6v31112010122111202100121222111122012212211101111202102221210222122211111211221111011021102021212212220211021022022201210110010212210211110202101010201110112020221222112102111202212110222110211221001010211001221212202222122111221102211100222112011211120220211",
        cyanWool: "0g0ge05obklb05oh6v305omt4v05omtxb05osg7305oy2gv05p3pj305p9bsv05p9clb05peyv305pkl4v05pklxb05pq87305pq8zj242269347912662167dda95521450145ab55cb659b88cd97649b546b35bb64db7536991146419921113b312431451424dda96656ad75dd9644dd97bb249b77bb9814b73445416521454a115737dd58ddbb99db97ba56762623774498234711474411bc116968dd7187cdb965158b55bdcc65b679da44684213b624bc547b23ab",
        grayConcrete: "0g0g20ehlvr30flk9a70000000000000000000000000000000000000000000000000000000000000000000000000000100100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000100000000000000000001000000010000",
        grayWool: "0g0gd0ehlvr30flk9a70flpvjz0flpwcf0flvim70gptwxr0gpzjzz0htxxj30hu3jsv0hu3klb0iy1y4f0iy7ke70iy7l6n333359337803663157cca85531350135ab55cb658b88cc87539b545b35bb53ca7535990036329930103b303330350335cc995556ac75cc8633cc87ba339b77ba8813a73435305532355a005737cc58ccab88cb87ba56753533773498333700574300ac205968cc7087cca865058b55bccc55b679ca33684313a634bc537a33ab",
        greenConcrete: "0g0g20k2mku70k2s73z0001000100111100101010100101101101101100111010010111001001011000100001100011000100101010110101111010000001011001001000100001010110101110010011010000101011110000010110101000000001010000110101011101000111110011101010011000111100001100011000000111101001101100",
        greenWool: "0g0gh0k2mku70k2s6bj0k2s73z0l6qjun0l6w64f0l71rlr0mb054v0mb5qm70mb5ren0nf9qf30nffcov0ojdq7z0ojjbpb0ojoxz30pnnbi70pnsxrz0rw0y6746447c45ab1499419aggcb8841580168ce88ee98bebbfgba96ce867e48ee96gda849cc125953cc41114e424642681546ggcc7789cga8ggb966ggbaed45ceaaedbb15da4658517843586c21894agg8bggdebbgebaec89a749449966cb445a116a6621de327c9bgga1bafgdb9828be88egee98e9acgc559b6414d946ef86ad44ce",
        lightBlueConcrete: "0g0g3090ti4f090z4e70a4xipr2120111111121222222121122112211121121211112221221111102221122212112021111111122111112111112121211222221111122121212121112021122112211121211211111121220111212121111121222001122211222221022122111011221111111021211221211211121112212111111122121111211112222111",
        lightBlueWool: "0g0gq090z4e70a4xipr0a534zj0a58r9b0b974sf0b9crun0b9ie4f0cdgrnj0cdmepr0cds0zj0dhqein0dhw0sf0dhw1kv0em01dr0em5nnj0fq9o8v0fqfain0gudou70gujb3z0hyhon30hynawv0hynbpb0j2lp8f0j2rbi70k6pptr0latpmn7b57fm89ik26ff63fippnkdd739d04benodepogdkojjppkifblodcfo8doofbpoie8fml21af95mm62428o817b81bd397cppmlffefnpidppkgbbppkioo79loiiookj4aoi8cad93fd55adcn22dh8ippejppookkpokiondfif6f68hhbcmj78ai22cicb22op51fmfjppi3jippokfd2ejoeeoppofdofilpn9agjc738og7copebio78no",
        lightGrayConcrete: "0g0g20yjgg730yjggzj1000110001001010100010000000011101100010001001011010001100101010001000000000000010100000000100010100000000001010100010001000000000100000110000100001100000011101110001001011001001001001011000000000011001001001001010000000010000000010100110000000001000001001",
        lightGrayWool: "0g0gi0yjgg730yjggzj0znkgsf0znkhkv10rohdr10roi6711vshz311vsirj11vy5tr12zwjcv12zwk5b1440jy7144670f1584kjj1584lbz16c8l4v16c8lxb17gclq745339e55bd1399329bhhfd7732570257ff78hfa7dfcchhdb95df769f57ff95hfb759ed115953ee31215f514551572537hhed9979fhb7hhda55hhdbff35dfbbgfdc25fb5657529733577f117b5bhh8chhfgddhfdbgf79b93935bb56ec355b117b6511fh319e9chhb2cbhhfd9718cf78fhhg97f9bdhf55ac6325fa36fh75bf35ff",
        limeConcrete: "0g0g30ppd5a70ppirjz0qth5331121111111100111111111110111110111010211100110111211111211101111112111111111111101110111011110211011011110111110102111110100111000110112112110211112112112111212111111111111111011111011100101111111111212111111111112010011011221111111110111120011110111101111",
        limeWool: "0g0gg0ppirjz0qth5330qtmrcv0rxl4vz0rxqr5r0t1p4ov0t1uqyn0u5t4hr0u5yqrj0v9x4an0va2qkf0we143j0xi53wf0ym3hfj0ym94hr10uh5of35237c44bb1299228affcb6632460257cd67ed96bdbbefba85bd657d46dd85fca748cb104942cc21214d403540562435ffcb7779cfb6ffb955ffbbdc34bdbaecbb24cb4546427622465c01694aff7bffcebbfdbbec69a728249955cb344a105a5501ce207c9bffa2bbefcb9607bd77dfee86d9abfc449b5324c935de75bc34cd",
        magentaConcrete: "0g0g419jw2rj1anugan1ao02kf1ao03cv2233313332221332333333233213322333223332331233333313323103121223222131233211303233130230322231223302323103002220333103232232223333313231221331233333231232212303302330122232333333332123320111033031332321233232303322232223332223231233212323332233222231230323",
        magentaWool: "0g0gs1ao02kf1ao03cv1brygvz1bs435r1bs43y71cw2hhb1cw83r31cw84jj1e06i2n1e0c4cf1e0c54v1f4ainz1f4g4xr1f4g5q71g8ej9b1g8k5j31g8k6bj1hcijun1hco64f1hco6wv1igmkfz1igs6pr1igs7i71jkw7b31jkw83j1kp08ov1lt49a71mxdwxr6945dl77ij14ff43ehrrmjba537a039cmoacqogajojjqrjhe9koa9do7booe9rnhc7elk118f74ll41317o7169719a275arrmkddcfmribrrjg99rrjion57koihonjj38ni798a72db448bam11bh7hrrcjrrnojjrojiomafhd4e47hh99lj578h11ah9911nq41dlfjrrh2jiqrnjfa1cjoccorqpebofhkrm78gj9537ng59oqc9in57mo",
        orangeConcrete: "0g0g21p59iwv1q97wfz1011110111101111111011111110000111101110100111111110011111011001001101110110111111111110111111011111101110111111110111111111111111011110111101111011101101101111010010111111110110101110010011101111111010101001101111111011111110111111111001111111101111111111",
        orangeWool: "0g0gm1q97wfz1rdbw8v1rdbx1b1rdhjb31shfwu71shfxmn1tljxfj1tljy7z1tlpkhr1upnytb1uptl331uptlvj1vtrzen1vtxlof1vtxmgv1wy1m9r1wy1n271wy79bz1wycwe71wyijgf1wyo5q71wytssf46239f55de02aa219dllhe8731570268hi79kib7eieelled96fi779i58ii96lhd859ff005a52ff20205i504650671537llgf998ahld8lleb66lledih35fiddihee25hd5757519822587h008c5dll9ellhieeliedih7ad92925cc67fe355d007d7600hk209faelld1edllhea709ei89ilkj98iadflh55be7315hb37il86dh35hi",
        pinkConcrete: "0g0g31ltks8v1mxj5rz1mxj6kf1122212121112211111211211211122112111221111111121211212111122211111112112121112111111111111112111212221121111111212211112101110212211211122111211112212111211110211211211211111111121212221211121111112212211121211111211212211111111111121112111211212122122111",
        pinkWool: "0g0gv1mxj6kf1o1hk3j1o1n6db1p5r6yn1q9v6rj1q9v7jz1rdz7cv1re4uf31si37y71si8u7z1tm78jj1tmcutb1tmcvlr1uqgven1uqmigv1uqs4qn1vuqj271vuw5bz1vuw64f1vv1se71vv7fgf1vvd1q71vvd2in1vviosf1vvipkv1vvobun1vvocn31vvtywv1vvzlz31vw588v1vw591b6a46em78jk25gg53fjuuokdc638c03adoqcesqhckqkktukjfalqcbeq7dqqfaupjd7fml219g83mm52327q716a71ac386buunleedgoujduukhaauukjqp68lqjjrpkk39pj7b9c83ed439dbo12di7juuekuuprkkuqkjrocgje5f57iiabmk679j22bjba12ps31emgkuuj3kjtupkgc1ekqdequssfdqgjluo89hkb637ph6bqtdajp67oq",
        purpleConcrete: "0g0g40qo16v30rrzke70rrzl6n0rs57gf3222322033323333333312320220233231302222232332333202332233033330333322320012333323333330233022223332223332223232323303323233332332021220323110033223333322032332312323332322322202232223033232221223233023333332022233332332233232023333233223032323332323323332",
        purpleWool: "0g0gm0rrzl6n0rs57gf0sw3kzj0sw3lrz0u01zb30u07lkv0u07mdb0v45zwf0w84dfj0w84e7z0xc8e0v0xc8etb0yg6scf0ygcem70zkasxr10o96gv10o979b10oetj311sd72711sd7un12wh8fz154uxhb47349e45ce13aa32acllfe8742570279fh79jhb7ehddkleca7eh779h48hha7lgc94aee116a52ee31214h414741771547llfe999aflc8lleb77llechg45ehcchged26gc4767519832687f118c4cll9dllgheelhechf7ac93a34cc77ed446c117c7711gj219eadllc1dcklgea719dh99hljia8hacelf56bd7424gb47hk97cg44fh",
        redConcrete: "0g0g112voa9r0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
        redWool: "0g0gd12voa9r13zmnsv153l1bz167jev3167p14v17bnenz18fls7319jprzz1ano5j31ano6bj1brmjun1brs64f1f3yku7241258227712662157cc9754212401459b45bb647b77bc77548b445b25bb54ca7525881036218821112b202420441224cc8855569c75cc7644cc77ba228b77ba7713a724342155113549015627cc57ccab77cb77b946752522664487223711474401ab105867cc7177bca764057b55bcbb55b678c923674212a624bb547a229b",
        whiteConcrete: "0g0g31ktui9r1ku04jj1lxyi2n1121212211221121111221111121122111211111121112111212112211111211212222111111221112211112101110211111122211211211111111212221111112212121212222111121122211111111222111111112121222121111111111211121221112221111121222121112212122111221211212111121221211221212",
        whiteWool: "0g0gn1lxyi2n1lxyiv31ly454v1n22inz1n22jgf1n285q71o66j9b1o66k1r1o6c6bj1paajun1qeekfz1ricyrj1riil1b1smgykf1smmlmn1tqkz5r1tqqm7z1uuozr31uuumtb1vyt0cf1vyynen1x2x0xr1y711j37a56dj89hi26ee63egmmliba639a04acllacmlfailiimmigeajlaadl8blleamlgc8ejj219e94jj62429l817a81aa396ammkjddcelmhbmmifaammihll69jlhgllii49lh8a9a93db549bal12bg8gmmcimmlliimlihllaegd6e68ggaaji689g22agaa12lm41djeimmg3ihmmliea1cilcclmmmeblegjml99fia638lf6almcahl68ll",
        yellowConcrete: "0g0g41to1w5b1us09of1us5vy71us5wqn2111111122222221232021021213122222211020112110121211022201012212122222312211221122213110202011133121132211112221221211211111112120231210211111201132001211212111211102220222021102211223011100222121202222222132111211111112111112021121120222221222011012122121",
        yellowWool: "0g0gj1us5vy71usbj0f1vw9wjj1vwfitb1vwfjlr1vwl5vj1vwl6nz1vwqsxr1x0p6gv1x0utj31x10fsv1x10glb1x163nj1y54h6n1y5a48v1y5a51b1y5frb31y5fs3j1y5lf5r45239e44bc0299219biiec7731470158eg78hga7cgcchicb95dg759g47gg95ifb849ed005942ee20104g404540571436iied9989eib7iica55iicbgf34dgbbgfcc15fb4557419722576e007b4bii8ciifgccigcbge79b92924bb55ec345b006b5500fh209e9ciib1cbhifc9708cg88gihh97g9bdie45ac5314fa35gh85bf34eg",
        bookshelf: "0g0gt03fxnnj04laqdb0a0ot1b0b6j6db0c8r6db0deww730df88ov0egz6rj0gpo9330ht5kov0k20av30nf40zj0pnc1dr0qoh8fz0sy416n0w8kcn30wc5n9b0yicu0v11vb08v11vskcf13z03jz16atkvz17fehvj1as1ce71czhmv31e50qv31g6nvgf1gbtpfj1ks44qnommllhlllmmmmlmoo44477444772534oo799kk999ni9637olkffqk99gpjk631lsqffqk64gnik631soqfdqf6gcngf651ookd9kf3c7igf350mlorrsssroorrroolollmmhmllmmmllhoo77227cb7427724ooqk763gck99cb97olpj063gckq4gc10hsqk063a8fqkgcpjsoqf065a87kqgc10omkfe35ccefkcb03oorrsssroorrrrooo",
        netherBricks: "0g0g706o77cv08w9lhb0b4bzlr0dcedq70fkb4sf0gof6670iwn6kf0000000000000000556515666555165532340433333405333432133223321333000111000001110015666544155545540543333306433333143333221433243211000001110000014416641644164414330433063304330423143314321333140111011101110111464414666644166433330433344303333322033333330333",
        redNetherBricks: "0g0g70c7i51b0efew3j0fjiwov0ive1a70nbiubj0svgd1b0v3ir5r0000000000000000556515666555165532340433333405333432133223321333000111000001110015666544155545540543333306433333143333221433243211000001110000014416641644164414330433063304330423143314321333140111011101110111464414666644166433330433344303333322033333330333",
        netherQuartzOre: "0g0gc0hs27zz0m7vq4f0m81d6n0ncb0u70rsft330u2845b0v4m7sv10p0npb1bug2671e4e1vj1n1fv9b1smb9xb1312426644044643344320246610442164643068a921424676466baa866314246766ba8674662643166895678a8544641466565ba95146866310289856642b9742469b814466ab862467ba624646b952364a950268665602426852108b61623214642649a9164941364667689238976124667423804646442246624630246644",
        netherrack: "0g0g70hs27zz0m7vq4f0m81d6n0ncb0u70rsft330v4m7sv10p0npb1312425544044543344320245210442154543012432142456545423545531424565424566455254315431566554244541452054554214565531024541154246642454221445541452456540245455112354554025655200242254210254102321454254122154541354556512234565124556423204545442245524530245544",
        netherWartBlock: "0g0g60of09vj0suze9r0xana4f1539r7j1brbain1g7rcvz0122001222532010222235202110022112111222202202320122201003202242042220021211122103224300222201201022220122252002210210211223102220000221222200222022221132210202012201104100222102201012211021042230122022012022212012022002220222202202220234022205220322022302",
        quartzBlockBottom: "0g0g31sm5mv31tq9ngf1tqfbb32222221111000222121111000002222111100000012222110000001122221110001111000110000011100000110000011111222200001111222222211111111222211000112222220110012222222211222222222210000022222211000000222222110110112222111100112222221100101122222111101222222222210000",
        quartzBlockSide: "0g0g61p9z6kf1qe37y71sm5mv31tq9ngf1tqfbb31uujcov5555555555555555543333222224444153322222234444304222223344433330423333222332222153322222332222215333444422223330544444433333333054433222334444405332234444444430544444444432222154444433222222405444332332334440533322334444443042323344444333311100011111110000",
        quartzBlockTop: "0g0g61p9z6kf1qe37y71sm5mv31tq9ngf1tqfbb31uujcov5555555555555555543333222224444153322222234444304222223344433330423333222332222153322222332222215333444422223330544444433333333054433222334444405332234444444430544444444432222154444433222222405444332332334440533322334444443042323344444333311100011111110000",
        quartzPillar: "0g0g41qe37y71sm5mv31tqfbb31uujcov0202031312130212020302130313031202131313120313020313130213031313131303120302121313020312031203131302131313020303120313031303130312130303131303120213131302131212131312130202120313021213120302031302120313031303130202130303131303120313031213120212131302121202",
        quartzPillarTop: "0g0g51p9z6kf1qe37y71sm5mv31tqfbb31uujcov1021223242413020222122211221122002223434434322223123443344443211113412222221431242442344443244222243242222424423423324244142441222442424324234143244242211423322224423334432442421341222222143111123444444443213222234444443222002211222122112221203142423220200",
        chiseledQuartzBlock: "0g0g61p9z6kf1qe37y71sm5mv31tq9ngf1tqfbb31uujcov0444404333022440043331222204444003322111000444300222223004443331023011101110022103315544432202200110441100220000554444144033344544433204314444440111230011441101054044444432154104400110111004401444454005544440133321100004443002323155440333300444414444132220",
        chiseledQuartzBlockTop: "0g0g51p9z6kf1qe37y71tq9ngf1tqfbb31uujcov0334314232133420021100022000112002044203204421200204120420412021020113032020003102233304212233200000000421110000323433442223344422222222222222220000001321000001044343142143222104011204214110201302130420313020120234032023403002111003201100400222314222133420",
        chiseledStoneBricks: "0g0g70oigd8f0qqoef30sz21vj0xf6vpb0yjgikf11vskcf17g711b6666655666566562644424444224444054110000111123205206665566561530640654434442154064064322334315405205430005421630540632000643062052054300053206306215435655420630641644545432053064163232322215406421110000011530556655666655543053343234433232202000000000000000",
        smoothStone: "0g0g70wb8hdr0yjgikf12zwkxr17gcnb318kgnwf1asop331d0wq9r1011002200220121054556666553445204334454444555501666455566655442255444545666645006555555554343412556664665545550243343443434466116656545555455620555554566656661054544334454454215555666665566622444566454433452165445556555455104456665566544401022112221000121",
        soulSand: "0g0g60egz94v0ht5n270k181z30oho3jz0sxyiv30xeekfz3130033202100434433135531100234004241451321012411423432542212230032442154212211332234104322433212002314322315332220023322345513032322211233445304302332013540423202342330242132302341043213032020354044321232020431445421112202350345131123302354202432103212415",
        glowstone: "0g0g80u1fugv0v5phbz10pn01r11uj6db1kqv1tr1x1ncov1y6jzlr1y711j35420045440276423654011240346654024032201131054033034542017510033014766405664136502566552454236751004554020132450531024010352320465230213276501576442654346640335420454220452013000320356304012311354016754036520357660354215764034654201003242013154216521100013",
        andesite: "0g0g60sywflr0wb8hdr0yjgikf11vsl4v17gcnb31asolxb2114332104423221245323445330122411334410122443224410133445433231235442223333211433323243210013432324542104543333423310154332333433114143343224533143342221154333332321101544332201331254422332333443443233212331543333334533112233301244323223541321322323234432",
        diorite: "0g0g60xfchz311vskcf19of1fj1gd8su71ktouf31smgykf1222352233524355201322135345543151355101325543235255312553213225351243153532255245355541332335235332455214554102243123234555521232203255443244250155213551234541244310134335513055510144221512352542543554223223212343125454353242115521455321245235554124421255",
        granite: "0g0ga0l56fi70plgu0v0yhw0sf1424tmn17eb8cf1aqna4f1e3al8f1e3x7un1jnje2n1qczoxr7434244344046445431548244224422341444335249241464143446145532344234232113754334244255342143347242445524442555322424474302155243346344255522344245535242552464245557314243844212555415442024425030323244424324439435524312355344448553215435547245544733324244425",
        polishedAndesite: "0g0g90oilzi70sz23gf0v7a3270yjgjcv10ru60v11vyakf11w3ugv19okmwv1e4v30f7878888888788785843334442443134084444443376644307416763343446440844442456433334083344333424433318444634666554320833433332123444184562444466776608434433443344540844667633366444081444444664466208466654333134760844433334465334083333664444334406100000000110000",
        polishedDiorite: "0g0g80qqu0ov0xfi5tr11vy6m71asubcv1f94t1b1jpkttr1o60w731ri7bpb7776677777666774764556657643564166764466577434506565333565454441675334576435457176544567733456607566767655346450744765756564534063556655467534516566544565544761664334655465666075673555775644517557654375466340745654576435744072445566545654512101110001100110",
        polishedGranite: "0g0g90l56fi70plgu0v0yhw0sf1424tmn17eb8cf1aqna4f1e3al8f1e3x7un1jnje2n8888786866866565843344345444334374744554442446418444243334553342844444453344443262343344443734428345543644444431844444423445544263422344554433636444433443344442734444444464334284473344244442318455444445543341633443264434455264443444334447412111001110111100",
        cactusstop: "0g0gp000006w000b8qg000mha0000xptk0018yd40018zy0001k8hk001vh140026pko02a3gmw02a3i7s02aep6g02aeqrc02apzaw02b17ug02bcge002bnoxk02bnqig02byz2002ca7lk04k6yns04ki77c04ktfqw04kthbs04l4pvchh32bd40fhh628hhhlefjjgdnllihkkh3ekmnlg5imlccklh2fnllnjajmleellhbkleelmjmmnllmh7djlddjmmjfjnnle34gmmmonmj5fmmmle0djikonmnjlnkij4flj77jlilmmja5e0hmmhilf3ilmmihjd3flllljfkjlkkmlh7hlnllmnj9fddlj6hmmlgennj9fddlj6hkmld9jnoillkljhhdglkfdjldilhcehh23dhc2f60ch717h",
        cactusSide: "0g0gk000006w001k8hk02a3gmw02a3i7s02aeqrc02apzaw02b17ug02bcge002bchyw02bnoxk02bnqig02byz2002ca7lk02clg5404k6yns04ki77c04ki8s804ktfqw04kthbs04l4pvc4chijhh74cgiihfa4chdff402bfijhfa06fdhi734bfiif5017cdfhc704cijica4bffihf717fijica4affggf74bfhjhca08cffff74bhhhe404gffffi704efff734gfffff717ffffca4ggfif402bhhhhca4fffhf734cjhhf404fffiic74cjggi7306bfiib705ggffca19ffhhc719fffjia4cjhff502cjgfhca4cifffa34cfeffda",
    }

    let blockData = [
        {
            name: "air",
            id: 0,
            textures: [],
            transparent: true,
            shadow: false,
        },
        {
            name: "grass",
            textures: [ "dirt", "grassTop", "grassSide" ],
        },
        { name: "dirt" },
        { name: "stone" },
        { name: "bedrock" },
        { name: "sand" },
        { name: "gravel" },
        {
            name: "leaves",
            transparent: true,
        },
        {
            name: "glass",
            transparent: true,
            shadow: false,
        },
        { name: "cobblestone" },
        { name: "mossyCobble" },
        { name: "stoneBricks" },
        { name: "mossyStoneBricks" },
        { name: "bricks" },
        { name: "coalOre" },
        { name: "ironOre" },
        { name: "goldOre" },
        { name: "diamondOre" },
        { name: "redstoneOre" },
        { name: "lapisOre" },
        { name: "emeraldOre" },
        { name: "coalBlock" },
        { name: "ironBlock" },
        { name: "goldBlock" },
        { name: "diamondBlock" },
        { name: "redstoneBlock" },
        { name: "lapisBlock" },
        { name: "emeraldBlock" },
        { name: "oakPlanks" },
        {
            name: "oakLog",
            textures: [ "logTop", "logSide" ],
        },
        { name: "acaciaPlanks" },
        {
            name: "acaciaLog",
            textures: [ "acaciaLogTop", "acaciaLogSide" ],
        },
        { name: "birchPlanks" },
        {
            name: "birchLog",
            textures: [ "birchLogTop", "birchLogSide" ],
        },
        { name: "darkOakPlanks" },
        {
            name: "darkOakLog",
            textures: [ "darkOakLogTop", "darkOakLogSide" ],
        },
        { name: "junglePlanks" },
        {
            name: "jungleLog",
            textures: [ "jungleLogTop", "jungleLogSide" ],
        },
        { name: "sprucePlanks" },
        {
            name: "spruceLog",
            textures: [ "spruceLogTop", "spruceLogSide" ],
        },
        {
            name: "cactus",
            textures: ["cactusstop", "cactusSide"]
        },
        { name: "whiteWool" },
        { name: "orangeWool" },
        { name: "magentaWool" },
        { name: "lightBlueWool" },
        { name: "yellowWool" },
        { name: "limeWool" },
        { name: "pinkWool" },
        { name: "grayWool" },
        { name: "lightGrayWool" },
        { name: "cyanWool" },
        { name: "purpleWool" },
        { name: "blueWool" },
        { name: "brownWool" },
        { name: "greenWool" },
        { name: "redWool" },
        { name: "blackWool" },
        { name: "whiteConcrete" },
        { name: "orangeConcrete" },
        { name: "magentaConcrete" },
        { name: "lightBlueConcrete" },
        { name: "yellowConcrete" },
        { name: "limeConcrete" },
        { name: "pinkConcrete" },
        { name: "grayConcrete" },
        { name: "lightGrayConcrete" },
        { name: "cyanConcrete" },
        { name: "purpleConcrete" },
        { name: "blueConcrete" },
        { name: "brownConcrete" },
        { name: "greenConcrete" },
        { name: "redConcrete" },
        { name: "blackConcrete" },
        {
            name: "bookshelf",
            textures: [ "oakPlanks", "bookshelf" ]
        },
        { name: "netherrack" },
        { name: "soulSand" },
        { name: "glowstone" },
        { name: "netherWartBlock" },
        { name: "netherBricks" },
        { name: "redNetherBricks" },
        { name: "netherQuartzOre" },
        {
            name: "quartzBlock",
            textures: ["quartzBlockBottom", "quartzBlockTop", "quartzBlockSide"]
        },
        {
            name: "quartzPillar",
            textures: ["quartzPillarTop", "quartzPillar"]
        },
        {
            name: "chiseledQuartzBlock",
            textures: ["chiseledQuartzBlock", "chiseledQuartzBlockTop"]
        },
        { name: "chiseledStoneBricks" },
        { name: "smoothStone" },
        { name: "andesite" },
        { name: "polishedAndesite" },
        { name: "diorite" },
        { name: "polishedDiorite" },
        { name: "granite" },
        { name: "polishedGranite" },
        { name: "Water",transparent: true,shadow: false,
    },
        // { // I swear, if y'all don't stop asking about TNT every 5 minutes!
        //   name: "tnt",
        //   textures: ["tntBottom", "tntTop", "tntSides"]
        // },
    ]
    const BLOCK_COUNT = blockData.length;

    //Set defaults on blockData
    (function() {
        for (let i = 1; i < BLOCK_COUNT; i++) {
            let data = blockData[i]
            data.id = i

            if (!data.textures) {
                data.textures = [ data.name, data.name, data.name, data.name, data.name, data.name ]
            } else if (typeof data.textures === "string") {
                let texture = data.textures
                data.textures = [ texture, texture, texture, texture, texture, texture ]
            } else if (data.textures.length === 3) {
                data.textures[3] = data.textures[2]
                data.textures[4] = data.textures[2]
                data.textures[5] = data.textures[2]
            } else if (data.textures.length === 2) {
            // Top and bottom are the first texture, sides are the second.
                data.textures[2] = data.textures[1]
                data.textures[3] = data.textures[2]
                data.textures[4] = data.textures[2]
                data.textures[5] = data.textures[2]
                data.textures[1] = data.textures[0]
            }

            data.transparent = data.transparent || false
            data.shadow = data.shadow !== undefined ? data.shadow : true
        }
    })()

    let win = window.parent
    let doc = document
    let console = win.console
    let world

    let seedHash
    let hash = (function() {
        let seed = Math.random() * 2100000000 | 0
        let PRIME32_2 = 1883677709
        let PRIME32_3 = 2034071983
        let PRIME32_4 = 668265263
        let PRIME32_5 = 374761393

        seedHash = function(s) {
            seed = s | 0
        }

        return function(x, y) {
            let h32 = 0

            h32 = seed + PRIME32_5 | 0
            h32 += 8

            h32 += Math.imul(x, PRIME32_3)
            h32 = Math.imul(h32 << 17 | h32 >> 32 - 17, PRIME32_4)
            h32 += Math.imul(y, PRIME32_3)
            h32 = Math.imul(h32 << 17 | h32 >> 32 - 17, PRIME32_4)

            h32 ^= h32 >> 15
            h32 *= PRIME32_2
            h32 ^= h32 >> 13
            h32 *= PRIME32_3
            h32 ^= h32 >> 16

            return h32 / 2147483647
        }
    })()
    let worldSeed

    //The noise and random functions are copied from the processing.js source code; these others are polyfills made by me to avoid needing to remove all the pjs draw calls
    let currentRandom = null
    function Marsaglia(i1, i2) {
    // from http://www.math.uni-bielefeld.de/~sillke/ALGORITHMS/random/marsaglia-c
        let z = (i1 | 0) || 362436069, w = i2 || hash(521288629, z) * 2147483647 | 0

        let nextInt = function() {
            z=36969*(z&65535)+(z>>>16) & 0xFFFFFFFF
            w=18000*(w&65535)+(w>>>16) & 0xFFFFFFFF
            return ((z&0xFFFF)<<16 | w&0xFFFF) & 0xFFFFFFFF
        }

        this.nextDouble = function() {
            let i = nextInt() / 4294967296
            return i < 0 ? 1 + i : i
        }
        this.nextInt = nextInt
    }
    let randomSeed = function(seed) {
        currentRandom = (new Marsaglia(seed)).nextDouble
    }
    let random = function(min, max) {
        if (!max) {
            if (min) {
                max = min
                min = 0
            } else {
                min = 0
                max = 1
            }
        }
        return currentRandom() * (max - min) + min
    }
    let noiseProfile = { generator: undefined, octaves: 4, fallout: 0.5, seed: undefined }
    function PerlinNoise(seed) {
        let rnd = seed !== undefined ? new Marsaglia(seed) : Marsaglia.createRandomized()
        let i, j
        // http://www.noisemachine.com/talk1/17b.html
        // http://mrl.nyu.edu/~perlin/noise/
        // generate permutation
        let perm = new Uint8Array(512)
        for(i=0;i<256;++i) {
            perm[i] = i
        }
        for(i=0;i<256;++i) {
            let t = perm[j = rnd.nextInt() & 0xFF]; perm[j] = perm[i]; perm[i] = t
        }
        // copy to avoid taking mod in perm[0]
        for(i=0;i<256;++i) {
            perm[i + 256] = perm[i]
        }

        function grad3d(i,x,y,z) {
            let h = i & 15; // convert into 12 gradient directions
            let u = h<8 ? x : y,
                v = h<4 ? y : h===12||h===14 ? x : z
            return ((h&1) === 0 ? u : -u) + ((h&2) === 0 ? v : -v)
        }

        function grad2d(i,x,y) {
            let v = (i & 1) === 0 ? x : y
            return (i&2) === 0 ? -v : v
        }

        function grad1d(i,x) {
            return (i&1) === 0 ? -x : x
        }

        function lerp(t,a,b) {
            return a + t * (b - a)
        }

        this.noise3d = function(x, y, z) {
            let X = Math.floor(x) & 255, Y = Math.floor(y) & 255, Z = Math.floor(z) & 255
            x -= Math.floor(x); y -= Math.floor(y); z -= Math.floor(z)
            let fx = (3-2*x)*x*x, fy = (3-2*y)*y*y, fz = (3-2*z)*z*z
            let p0 = perm[X]+Y, p00 = perm[p0] + Z, p01 = perm[p0 + 1] + Z,
                p1 = perm[X + 1] + Y, p10 = perm[p1] + Z, p11 = perm[p1 + 1] + Z
            return lerp(fz,
                lerp(fy, lerp(fx, grad3d(perm[p00], x, y, z), grad3d(perm[p10], x-1, y, z)),
                    lerp(fx, grad3d(perm[p01], x, y-1, z), grad3d(perm[p11], x-1, y-1,z))),
                lerp(fy, lerp(fx, grad3d(perm[p00 + 1], x, y, z-1), grad3d(perm[p10 + 1], x-1, y, z-1)),
                    lerp(fx, grad3d(perm[p01 + 1], x, y-1, z-1), grad3d(perm[p11 + 1], x-1, y-1,z-1))))
        }

        this.noise2d = function(x, y) {
            let X = Math.floor(x)&255, Y = Math.floor(y)&255
            x -= Math.floor(x); y -= Math.floor(y)
            let fx = (3-2*x)*x*x, fy = (3-2*y)*y*y
            let p0 = perm[X]+Y, p1 = perm[X + 1] + Y
            return lerp(fy,
                lerp(fx, grad2d(perm[p0], x, y), grad2d(perm[p1], x-1, y)),
                lerp(fx, grad2d(perm[p0 + 1], x, y-1), grad2d(perm[p1 + 1], x-1, y-1)))
        }

        this.noise1d = function(x) {
            let X = Math.floor(x)&255
            x -= Math.floor(x)
            let fx = (3-2*x)*x*x
            return lerp(fx, grad1d(perm[X], x), grad1d(perm[X+1], x-1))
        }
    }
    let noiseSeed = function(seed) {
        noiseProfile.seed = seed
        noiseProfile.generator = new PerlinNoise(noiseProfile.seed)
    }
    let noise = function(x, y, z) {
        let generator = noiseProfile.generator
        let effect = 1, k = 1, sum = 0
        for(let i = 0; i < noiseProfile.octaves; ++i) {
            effect *= noiseProfile.fallout
            switch (arguments.length) {
                case 1:
                    sum += effect * (1 + generator.noise1d(k*x))/2; break
                case 2:
                    sum += effect * (1 + generator.noise2d(k*x, k*y))/2; break
                case 3:
                    sum += effect * (1 + generator.noise3d(k*x, k*y, k*z))/2; break
            }
            k *= 2
        }
        return sum
    }

    let caveNoise
    // Copied and modified from https://github.com/blindman67/SimplexNoiseJS
    function openSimplexNoise(clientSeed) {
        const SQ4 = 2
        const toNums = function(s) { return s.split(",").map(function(s) { return new Uint8Array(s.split("").map(function(v) { return Number(v) })) }) }
        const decode = function(m, r, s) { return new Int8Array(s.split("").map(function(v) { return parseInt(v, r) + m })) }
        const toNumsB32 = function(s) { return s.split(",").map(function(s) { return parseInt(s, 32) }) }
        const NORM_3D = 1.0 / 206.0
        const SQUISH_3D = 1 / 3
        const STRETCH_3D = -1 / 6
        var base3D = toNums("0000110010101001,2110210120113111,110010101001211021012011")
        const gradients3D = decode(-11, 23, "0ff7mf7fmmfffmfffm07f70f77mm7ff0ff7m0f77m77f0mf7fm7ff0077707770m77f07f70")
        var lookupPairs3D = function() { return new Uint16Array(toNumsB32("0,2,1,1,2,2,5,1,6,0,7,0,10,2,12,2,41,1,45,1,50,5,51,5,g6,0,g7,0,h2,4,h6,4,k5,3,k7,3,l0,5,l1,5,l2,4,l5,3,l6,4,l7,3,l8,d,l9,d,la,c,ld,e,le,c,lf,e,m8,k,ma,i,p9,l,pd,n,q8,k,q9,l,15e,j,15f,m,16a,i,16e,j,19d,n,19f,m,1a8,f,1a9,h,1aa,f,1ad,h,1ae,g,1af,g,1ag,b,1ah,a,1ai,b,1al,a,1am,9,1an,9,1bg,b,1bi,b,1eh,a,1el,a,1fg,8,1fh,8,1qm,9,1qn,9,1ri,7,1rm,7,1ul,6,1un,6,1vg,8,1vh,8,1vi,7,1vl,6,1vm,7,1vn,6")) }
        var p3D = decode(-1, 5, "112011210110211120110121102132212220132122202131222022243214231243124213241324123222113311221213131221123113311112202311112022311112220342223113342223311342223131322023113322023311320223113320223131322203311322203131")
        const setOf = function(count) { var a = [],i = 0; while (i < count) { a.push(i++) } return a }
        const doFor = function(count, cb) { var i = 0; while (i < count && cb(i++) !== true) {} }

        function shuffleSeed(seed,count){
            seed = seed * 1664525 + 1013904223 | 0
            count -= 1
            return count > 0 ? shuffleSeed(seed, count) : seed
        }
        const types = {
            _3D : {
                base : base3D,
                squish : SQUISH_3D,
                dimensions : 3,
                pD : p3D,
                lookup : lookupPairs3D,
            }
        }

        function createContribution(type, baseSet, index) {
            var i = 0
            const multiplier = baseSet[index ++]
            const c = { next : undefined }
            while(i < type.dimensions) {
                const axis = ("xyzw")[i]
                c[axis + "sb"] = baseSet[index + i]
                c["d" + axis] = - baseSet[index + i++] - multiplier * type.squish
            }
            return c
        }

        function createLookupPairs(lookupArray, contributions){
            var i
            const a = lookupArray()
            const res = new Map()
            for (i = 0; i < a.length; i += 2) { res.set(a[i], contributions[a[i + 1]]); }
            return res
        }

        function createContributionArray(type) {
            const conts = []
            const d = type.dimensions
            const baseStep = d * d
            var k, i = 0
            while (i < type.pD.length) {
                const baseSet = type.base[type.pD[i]]
                let previous, current
                k = 0
                do {
                    current = createContribution(type, baseSet, k)
                    if (!previous) { conts[i / baseStep] = current; }
                    else { previous.next = current; }
                    previous = current
                    k += d + 1
                } while(k < baseSet.length)

                current.next = createContribution(type, type.pD, i + 1)
                if (d >= 3) { current.next.next = createContribution(type, type.pD, i + d + 2) }
                if (d === 4) { current.next.next.next = createContribution(type, type.pD, i + 11) }
                i += baseStep
            }
            const result = [conts, createLookupPairs(type.lookup, conts)]
            type.base = undefined
            type.lookup = undefined
            return result
        }

        let temp = createContributionArray(types._3D)
        const contributions3D = temp[0], lookup3D = temp[1]
        const perm = new Uint8Array(256)
        const perm3D = new Uint8Array(256)
        const source = new Uint8Array(setOf(256))
        var seed = shuffleSeed(clientSeed, 3)
        doFor(256, function(i) {
            i = 255 - i
            seed = shuffleSeed(seed, 1)
            var r = (seed + 31) % (i + 1)
            r += r < 0 ? i + 1 : 0
            perm[i] = source[r]
            perm3D[i] = (perm[i] % 24) * 3
            source[r] = source[i]
        })
        base3D = undefined
        lookupPairs3D = undefined
        p3D = undefined

        return function(x, y, z) {
            const pD = perm3D
            const p = perm
            const g = gradients3D
            const stretchOffset = (x + y + z) * STRETCH_3D
            const xs = x + stretchOffset, ys = y + stretchOffset, zs = z + stretchOffset
            const xsb = Math.floor(xs), ysb = Math.floor(ys), zsb = Math.floor(zs)
            const squishOffset  = (xsb + ysb + zsb) * SQUISH_3D
            const dx0 = x - (xsb + squishOffset), dy0 = y - (ysb + squishOffset), dz0 = z - (zsb + squishOffset)
            const xins = xs - xsb, yins = ys - ysb, zins = zs - zsb
            const inSum = xins + yins + zins
            var c = lookup3D.get(
                (yins - zins + 1) |
                ((xins - yins + 1) << 1) |
                ((xins - zins + 1) << 2) |
                (inSum << 3) |
                ((inSum + zins) << 5) |
                ((inSum + yins) << 7) |
                ((inSum + xins) << 9)
            )
            var i, value = 0
            while (c !== undefined) {
                const dx = dx0 + c.dx, dy = dy0 + c.dy, dz = dz0 + c.dz
                let attn = 2 - dx * dx - dy * dy - dz * dz
                if (attn > 0) {
                    i = pD[(((p[(xsb + c.xsb) & 0xFF] + (ysb + c.ysb)) & 0xFF) + (zsb + c.zsb)) & 0xFF]
                    attn *= attn
                    value += attn * attn * (g[i++] * dx + g[i++] * dy + g[i] * dz)
                }
                c = c.next
            }
            return value * NORM_3D + 0.5
        }
    }

    let PVector = function(x, y, z) {
        this.x = x
        this.y = y
        this.z = z
        this.set = function(x, y, z) {
            if (y === undefined) {
                this.x = x.x
                this.y = x.y
                this.z = x.z
            } else {
                this.x = x
                this.y = y
                this.z = z
            }
        }
        this.normalize = function() {
            let mag = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z)
            this.x /= mag
            this.y /= mag
            this.z /= mag
        }
        this.add = function(v) {
            this.x += v.x
            this.y += v.y
            this.z += v.z
        }
        this.mult = function(m) {
            this.x *= m
            this.y *= m
            this.z *= m
        }
    }
    let fill = function(r, g, b) {
        if (g === undefined) {
            g = r
            b = r
        }
        ctx.fillStyle = "rgb(" + r + ", " + g + ", " + b + ")"
    }
    let stroke = function(r, g, b) {
        if (g === undefined) {
            g = r
            b = r
        }
        ctx.strokeStyle = "rgb(" + r + ", " + g + ", " + b + ")"
    }
    let line = function(x1, y1, x2, y2) {
        ctx.moveTo(x1, y1)
        ctx.lineTo(x2, y2)
    }
    function text(txt, x, y, h) {
        h = h || 0

        let lines = txt.split("\n")
        for (let i = 0; i < lines.length; i++) {
            ctx.fillText(lines[i], x, y + h * i)
        }
    }
    function textSize(size) {
        ctx.font = size + 'px Monospace' // VT323
    }
    let strokeWeight = function(num) {
        ctx.lineWidth = num
    }
    const ARROW = "arrow"
    const HAND = "pointer"
    const CROSS = "crosshair"
    let cursor = function(type) {
        canvas.style.cursor = type
    }
    randomSeed(Math.random() * 10000000 | 0)

    async function createDatabase() {
        return await new Promise(async (resolve, reject) => {
            let request = window.indexedDB.open("MineKhan", 1)

            request.onupgradeneeded = function(event) {
                let DB = event.target.result
                // Worlds will contain and ID containing the timestamp at which the world was created, a "saved" timestamp,
                // and a "data" string that's identical to the copy/paste save string
                let store = DB.createObjectStore("worlds", { keyPath: "id" })
                store.createIndex("id", "id", { unique: true })
                store.createIndex("data", "data", { unique: false })
            }

            request.onsuccess = function(e) {
                resolve(request.result)
            }

            request.onerror = function(e) {
                console.error(e)
                reject(e)
            }
        })
    }
    async function loadFromDB(id) {
        return await new Promise(async (resolve, reject) => {
            let db = await createDatabase()
            let trans = db.transaction("worlds", "readwrite")
            let store = trans.objectStore("worlds")
            let req = id ? store.get(id) : store.getAll()
            req.onsuccess = function(e) {
                resolve(req.result)
                db.close()
            }
            req.onerror = function(e) { 
                resolve(null)
                db.close()
            }
        })
    }
    async function saveToDB(id, data) {
        return new Promise(async (resolve, reject) => {
            let db = await createDatabase()
            let trans = db.transaction("worlds", "readwrite")
            let store = trans.objectStore("worlds")
            let req = store.put({ id: id, data: data })
            req.onsuccess = function() {
                resolve(req.result)
            }
            req.onerror = function(e) {
                reject(e)
            }
        })
    }
    async function deleteFromDB(id) {
        return new Promise(async (resolve, reject) => {
            let db = await createDatabase()
            let trans = db.transaction("worlds", "readwrite")
            let store = trans.objectStore("worlds")
            let req = store.delete(id)
            req.onsuccess = function() {
                resolve(req.result)
            }
            req.onerror = function(e) {
                reject(e)
            }
        })
    }

    function save() {
        saveToDB(world.id, {
            id: world.id,
            edited: Date.now(),
            name: world.name,
            version: version,
            code: world.getSaveString()
        }).then(() => world.edited = Date.now()).catch(e => console.error(e))
    }

    // Expose these functions to the global scope for debugging
    win.saveToDB = saveToDB
    win.loadFromDB = loadFromDB
    win.createDatabase = createDatabase
    win.deleteFromDB = deleteFromDB

    //globals
    //{
    let version = "Alpha 0.7.1 - Modded"
    let reach = 5 // Max distance player can place or break blocks
    let sky = [0.33, 0.54, 0.72] // 0 to 1 RGB color scale
    let superflat = false
    let trees = true
    let caves = true

    let blockIds = {}
    blockData.forEach(block => blockIds[block.name] = block.id)
    win.blockData = blockData
    win.blockIds = blockIds

    let currentFov

    // Configurable and savable settings
    let settings = {
        renderDistance: 4,
        fov: 70, // Field of view in degrees
        mouseSense: 100 // Mouse sensitivity as a percentage of the default
    }
    let locked = true
    let generatedChunks
    let mouseX, mouseY, mouseDown
    let width = window.innerWidth
    let height = window.innerHeight

    if (height === 400) alert("Canvas is too small. Click the \"Settings\" button to the left of the \"Vote Up\" button under the editor and change the height to 600.")

    let generator = {
        height: 80, // Height of the hills
        smooth: 0.01, // Smoothness of the terrain
        extra: 30, // Extra height added to the world.
        caveSize: 0.00, // Redefined right above where it's used
        biomeSmooth: 0.007 // smooth of biomes
};

    let maxHeight = 255
    let blockOutlines = false
    let blockFill = true
    let updateHUD = true
    const CUBE     = 0
    const SLAB     = 0x100 // 9th bit
    const STAIR    = 0x200 // 10th bit
    const FLIP     = 0x400 // 11th bit
    const NORTH    = 0 // 12th and 13th bits for the 4 directions
    const SOUTH    = 0x800
    const EAST     = 0x1000
    const WEST     = 0x1800
    const ROTATION = 0x1800 // Mask for the direction bits
    let blockMode  = CUBE
    let tex
    let textureMap
    let dirtBuffer
    let dirtTexture
    let textureCoords
    let texCoordsBuffers
    let mainbg, dirtbg // Background images
    let bigArray = win.bigArray || new Float32Array(600000)
    win.bigArray = bigArray

    // Callback functions for all the screens; will define them further down the page
let drawScreens = {
    "main menu": () => {},
    "options": () => {},
    "play": () => {},
    "pause": () => {},
    "creation menu": () => {},
    "inventory": () => {},
    "multiplayer menu": () => {},
    "comingsoon menu": () => {},
    "loadsave menu": () => {},
    "dead": () => {}
}
    let html = {
        pause: {
            enter: [window.message],
            exit: [window.savebox, window.saveDirections, window.message]
        },
        "loadsave menu": {
            enter: [window.worlds, window.boxCenterTop, window.quota],
            exit: [window.worlds, window.boxCenterTop, window.quota],
            onenter: () => {
                window.boxCenterTop.placeholder = "Enter Save String (Optional)"
                if (navigator && navigator.storage && navigator.storage.estimate) {
                    navigator.storage.estimate().then(data => {
                        window.quota.innerText = `${data.usage.toLocaleString()} / ${data.quota.toLocaleString()} bytes (${(100 * data.usage / data.quota).toLocaleString(undefined, { maximumSignificantDigits: 2 })}%) of your quota used`
                    }).catch(console.error)
                }
                window.boxCenterTop.onmousedown = e => {
                    let elem = document.getElementsByClassName("selected")
                    if (elem && elem[0]) {
                        elem[0].classList.remove("selected")
                    }
                    selectedWorld = 0
                    Button.draw()
                }
            },
            onexit: () => {
                window.boxCenterTop.onmousedown = null
            }
        },
        "creation menu": {
            enter: [window.boxCenterTop],
            exit: [window.boxCenterTop],
            onenter: () => {
                window.boxCenterTop.placeholder = "Enter World Name"
                window.boxCenterTop.value = ""
            }
        },
        loading: {
            onenter: startLoad
        },
        editworld: {
            enter: [window.boxCenterTop],
            exit: [window.boxCenterTop],
            onenter: () => {
                window.boxCenterTop.placeholder = "Enter World Name"
                window.boxCenterTop.value = ""
            }
        }
    }

    let screen = "main menu"
    let previousScreen = screen
    function changeScene(newScene) {
        if (screen === "options") {
            saveToDB("settings", settings).catch(e => console.error(e))
        }

        if (html[screen] && html[screen].exit) {
            for (let element of html[screen].exit) {
                element.classList.add("hidden")
            }
        }

        if (html[newScene] && html[newScene].enter) {
            for (let element of html[newScene].enter) {
                element.classList.remove("hidden")
            }
        }

        if (html[newScene] && html[newScene].onenter) {
            html[newScene].onenter()
        }
        if (html[screen] && html[screen].onexit) {
            html[screen].onexit()
        }

        previousScreen = screen
        screen = newScene
        mouseDown = false
        drawScreens[screen]()
        Button.draw()
        Slider.draw()
    }
    let hitBox = {}
    let holding = 0
    let Key = {}
    let modelView = win.modelView || new Float32Array(16)
    win.modelView = modelView
    let glCache
    let worlds, selectedWorld = 0
    let freezeFrame = 0
    let p
    let vec1 = new PVector(), vec2 = new PVector(), vec3 = new PVector()
    let move = {
        x: 0,
        y: 0,
        z: 0,
        ang: Math.sqrt(0.5),
    }
    let p2 = {
        x: 0,
        y: 0,
        z: 0,
    }
    let place
    let inventory = {
        hotbar: [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ],
        main: [],
        hotbarSlot: 0,
        size: 40 * Math.min(width, height) / 600,
        holding: 0,
    }
    //}

    function play() {
        canvas.onblur()
        p.lastBreak = Date.now()
        updateHUD = true
        use3d()
        gl.clearColor(sky[0], sky[1], sky[2], 1.0)
        getPointer()
        fill(255, 255, 255)
        textSize(10)
        changeScene("play")
    }

    let gl
    function getPointer() {
        if (canvas.requestPointerLock) {
            canvas.requestPointerLock()
        }
    }
    function releasePointer() {
        if (doc.exitPointerLock) {
            doc.exitPointerLock()
        }
    }

    let Block = {
        top: 0x4,
        bottom: 0x8,
        north: 0x20,
        south: 0x10,
        east: 0x2,
        west: 0x1,
    }
    let Sides = {
        top: 0,
        bottom: 1,
        north: 2,
        south: 3,
        east: 4,
        west: 5,
    }

    // GLSL Shader code (written in script tags at the top of the file)
    let vertexShaderSrc3D = document.getElementById("blockVertexShader").text
    let fragmentShaderSrc3D = document.getElementById("blockFragmentShader").text
    let vertexShaderSrc2D = document.getElementById("2dVertexShader").text
    let fragmentShaderSrc2D = document.getElementById("2dFragmentShader").text

    function createProgramObject(curContext, vetexShaderSource, fragmentShaderSource) {
        let vertexShaderObject = curContext.createShader(curContext.VERTEX_SHADER)
        curContext.shaderSource(vertexShaderObject, vetexShaderSource)
        curContext.compileShader(vertexShaderObject)
        if (!curContext.getShaderParameter(vertexShaderObject, curContext.COMPILE_STATUS)) {
            throw curContext.getShaderInfoLog(vertexShaderObject)
        }

        let fragmentShaderObject = curContext.createShader(curContext.FRAGMENT_SHADER)
        curContext.shaderSource(fragmentShaderObject, fragmentShaderSource)
        curContext.compileShader(fragmentShaderObject)
        if (!curContext.getShaderParameter(fragmentShaderObject, curContext.COMPILE_STATUS)) {
            throw curContext.getShaderInfoLog(fragmentShaderObject)
        }

        let programObject = curContext.createProgram()
        curContext.attachShader(programObject, vertexShaderObject)
        curContext.attachShader(programObject, fragmentShaderObject)
        curContext.linkProgram(programObject)
        if (!curContext.getProgramParameter(programObject, curContext.LINK_STATUS)) {
            throw "Error linking shaders."
        }

        return programObject
    }

    let program3D, program2D

    function objectify(x, y, z, width, height, textureX, textureY) {
        return {
            x: x,
            y: y,
            z: z,
            w: width,
            h: height,
            tx: textureX,
            ty: textureY
        }
    }
    let shapes = {
        /*
            [
                [(-x, -z), (+x, -z), (+x, +z), (-x, +z)], // minX = 0,  minZ = 2,  maxX = 6, maxZ = 8
                [(-x, +z), (+x, +z), (+x, -z), (-x, -z)], // minX = 9,  minZ = 10, maxX = 3, maxZ = 4
                [(+x, +y), (-x, +y), (-x, -y), (+x, -y)], // minX = 6,  minY = 7,  maxX = 0, maxY = 1
                [(-x, +y), (+x, +y), (+x, -y), (-x, -y)], // minX = 9,  minY = 10, maxX = 3, maxY = 4
                [(+y, -z), (+y, +z), (-y, +z), (-y, -z)], // minY = 10, minZ = 11, maxY = 4, maxZ = 5
                [(+y, +z), (+y, -z), (-y, -z), (-y, +z)]  // minY = 7,  minZ = 8,  maxY = 1, maxZ = 2
            ]
            */
        cube: {
            verts: [
                // x, y, z, width, height, textureX, textureY
                // 0, 0, 0 is the corner on the top left of the texture
                [objectify( 0,  0,  0, 16, 16, 0, 0)], //bottom
                [objectify( 0, 16, 16, 16, 16, 0, 0)], //top
                [objectify(16, 16, 16, 16, 16, 0, 0)], //north
                [objectify( 0, 16,  0, 16, 16, 0, 0)], //south
                [objectify(16, 16,  0, 16, 16, 0, 0)], //east
                [objectify( 0, 16, 16, 16, 16, 0, 0)]  //west
            ],
            cull: {
                top: 3,
                bottom: 3,
                north: 3,
                south: 3,
                east: 3,
                west: 3
            },
            texVerts: [],
            varients: [],
            buffer: null,
            size: 6
        },
        slab: {
            verts: [
                [objectify( 0, 0,  0, 16, 16, 0, 0)], //bottom
                [objectify( 0, 8, 16, 16, 16, 0, 0)], //top
                [objectify(16, 8, 16, 16, 8, 0, 0)], //north
                [objectify( 0, 8,  0, 16, 8, 0, 0)], //south
                [objectify(16, 8,  0, 16, 8, 0, 0)], //east
                [objectify( 0, 8, 16, 16, 8, 0, 0)]  //west
            ],
            cull: {
                top: 0,
                bottom: 3,
                north: 1,
                south: 1,
                east: 1,
                west: 1
            },
            texVerts: [],
            buffer: null,
            size: 6,
            varients: [],
            flip: true,
            rotate: false
        },
        stair: {
            verts: [
                [objectify( 0, 0,  0, 16, 16, 0, 0)], //bottom
                [objectify( 0, 8,  8, 16, 8, 0, 8), objectify( 0, 16,  16, 16, 8, 0, 0)], //top
                [objectify(16, 16, 16, 16, 16, 0, 0)], //north
                [objectify( 0, 8,  0, 16, 8, 0, 0), objectify( 0, 16,  8, 16, 8, 0, 0)], //south
                [objectify(16, 8, 0, 8, 8, 8, 0), objectify(16, 16, 8, 8, 16, 0, 0)], //east
                [objectify( 0, 8, 8, 8, 8, 0, 0), objectify( 0, 16, 16, 8, 16, 8, 0)]  //west
            ],
            cull: {
                top: 0,
                bottom: 3,
                north: 3,
                south: 0,
                east: 0,
                west: 0
            },
            texVerts: [],
            buffer: null,
            size: 10,
            varients: [],
            flip: true,
            rotate: true
        },
    }
    win.shapes = shapes

    function compareArr(arr, out) {
        let minX = 1000
        let maxX = -1000
        let minY = 1000
        let maxY = -1000
        let minZ = 1000
        let maxZ = -1000
        let min = Math.min
        let max = Math.max
        let num = 0
        for (let i = 0; i < arr.length; i += 3) {
            num = arr[i]
            minX = minX > num ? num : minX
            maxX = maxX < num ? num : maxX
            num = arr[i + 1]
            minY = minY > num ? num : minY
            maxY = maxY < num ? num : maxY
            num = arr[i + 2]
            minZ = minZ > num ? num : minZ
            maxZ = maxZ < num ? num : maxZ
        }
        out[0] = minX
        out[1] = minY
        out[2] = minZ
        out[3] = maxX
        out[4] = maxY
        out[5] = maxZ
        return out
    }

    function initShapes() {
        function mapCoords(rect, face) {
            let x = rect.x
            let y = rect.y
            let z = rect.z
            let w = rect.w
            let h = rect.h
            let tx = rect.tx
            let ty = rect.ty
            let tex = [tx+w,ty, tx,ty, tx,ty+h, tx+w,ty+h]
            let pos = null
            switch(face) {
                case 0: // Bottom
                    pos = [x,y,z, x+w,y,z, x+w,y,z+h, x,y,z+h]
                    break
                case 1: // Top
                    pos = [x,y,z, x+w,y,z, x+w,y,z-h, x,y,z-h]
                    break
                case 2: // North
                    pos = [x,y,z, x-w,y,z, x-w,y-h,z, x,y-h,z]
                    break
                case 3: // South
                    pos = [x,y,z, x+w,y,z, x+w,y-h,z, x,y-h,z]
                    break
                case 4: // East
                    pos = [x,y,z, x,y,z+w, x,y-h,z+w, x,y-h,z]
                    break
                case 5: // West
                    pos = [x,y,z, x,y,z-w, x,y-h,z-w, x,y-h,z]
                    break
            }
            pos = pos.map(c => c / 16 - 0.5)
            let minmax = compareArr(pos, [])
            pos.max = minmax.splice(3, 3)
            pos.min = minmax
            tex = tex.map(c => c / 16 / 16)

            return {
                pos: pos,
                tex: tex
            }
        }

        // 90 degree clockwise rotation; returns a new shape object
        function rotate(shape) {
            let verts = shape.verts
            let texVerts = shape.texVerts
            let cull = shape.cull
            let pos = []
            tex = []
            for (let i = 0; i < verts.length; i++) {
                let side = verts[i]
                pos[i] = []
                tex[i] = []
                for (let j = 0; j < side.length; j++) {
                    let face = side[j]
                    let c = []
                    pos[i][j] = c
                    for (let k = 0; k < face.length; k += 3) {
                        c[k] = face[k + 2]
                        c[k + 1] = face[k + 1]
                        c[k + 2] = -face[k]
                    }

                    tex[i][j] = texVerts[i][j].slice() // Copy texture verts exactly
                    if (i === 0) {
                        // Bottom
                        c.push(...c.splice(0, 3))
                        tex[i][j].push(...tex[i][j].splice(0, 2))
                    }
                    if (i === 1) {
                        // Top
                        c.unshift(...c.splice(-3, 3))
                        tex[i][j].unshift(...tex[i][j].splice(-2, 2))
                    }

                    let minmax = compareArr(c, [])
                    c.max = minmax.splice(3, 3)
                    c.min = minmax
                }
            }
            let temp = tex[2] // North
            tex[2] = tex[5] // North = West
            tex[5] = tex[3] // West = South
            tex[3] = tex[4] // South = East
            tex[4] = temp // East = North

            temp = pos[2] // North
            pos[2] = pos[5] // North = West
            pos[5] = pos[3] // West = South
            pos[3] = pos[4] // South = East
            pos[4] = temp // East = North

            let cull2 = {
                top: cull.top,
                bottom: cull.bottom,
                north: cull.west,
                west: cull.south,
                south: cull.east,
                east: cull.north
            }

            let buffer = gl.createBuffer()
            gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(pos.flat(2)), gl.STATIC_DRAW)

            return {
                verts: pos,
                texVerts: tex,
                cull: cull2,
                rotate: true,
                flip: shape.flip,
                buffer: buffer,
                size: shape.size,
                varients: shape.varients
            }
        }

        // Reflect over the y plane; returns a new shape object
        function flip(shape) {
            let verts = shape.verts
            let texVerts = shape.texVerts
            let cull = shape.cull
            let pos = []
            tex = []
            for (let i = 0; i < verts.length; i++) {
                let side = verts[i]
                pos[i] = []
                tex[i] = []
                for (let j = 0; j < side.length; j++) {
                    let face = side[j].slice().reverse()
                    let c = []
                    pos[i][j] = c
                    for (let k = 0; k < face.length; k += 3) {
                        c[k] = face[k + 2]
                        c[k + 1] = -face[k + 1]
                        c[k + 2] = face[k]
                    }
                    let minmax = compareArr(c, [])
                    c.max = minmax.splice(3, 3)
                    c.min = minmax

                    tex[i][j] = texVerts[i][j].slice() // Copy texture verts exactly
                }
            }
            let temp = pos[0] // Bottom
            pos[0] = pos[1] // Bottom = Top
            pos[1] = temp // Top = Bottom

            temp = tex[0] // Bottom
            tex[0] = tex[1] // Bottom = Top
            tex[1] = temp // Top = Bottom

            let cull2 = {
                top: cull.bottom,
                bottom: cull.top,
                north: (cull.north & 1) << 1 | (cull.north & 2) >> 1,
                west: (cull.west & 1) << 1 | (cull.west & 2) >> 1,
                south: (cull.south & 1) << 1 | (cull.south & 2) >> 1,
                east: (cull.east & 1) << 1 | (cull.east & 2) >> 1
            }

            let buffer = gl.createBuffer()
            gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(pos.flat(2)), gl.STATIC_DRAW)

            return {
                verts: pos,
                texVerts: tex,
                cull: cull2,
                rotate: shape.rotate,
                flip: shape.flip,
                buffer: buffer,
                size: shape.size,
                varients: shape.varients
            }
        }

        for (let shape in shapes) {
            let obj = shapes[shape]
            let verts = obj.verts

            // Populate the vertex coordinates
            for (let i = 0; i < verts.length; i++) {
                let side = verts[i]
                let texArr = []
                obj.texVerts.push(texArr)
                for (let j = 0; j < side.length; j++) {
                    let face = side[j]
                    let mapped = mapCoords(face, i)
                    side[j] = mapped.pos
                    texArr.push(mapped.tex)
                }
            }

            if (obj.rotate) {
                let v = obj.varients
                let east = rotate(obj)
                let south = rotate(east)
                let west = rotate(south)
                v[0] = obj
                v[2] = south
                v[4] = east
                v[6] = west
            }
            if (obj.flip) {
                let v = obj.varients
                v[1] = flip(obj)
                if (obj.rotate) {
                    v[3] = flip(v[2])
                    v[5] = flip(v[4])
                    v[7] = flip(v[6])
                }
            }

            obj.buffer = gl.createBuffer()
            gl.bindBuffer(gl.ARRAY_BUFFER, obj.buffer)
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts.flat(2)), gl.STATIC_DRAW)
        }

        for (let i = 0; i < BLOCK_COUNT; i++) {
            let baseBlock = blockData[i]
            let slabBlock = Object.create(baseBlock)
            let stairBlock = Object.create(baseBlock)
            slabBlock.shape = shapes.slab
            baseBlock.shape = shapes.cube
            stairBlock.shape = shapes.stair
            blockData[i | SLAB] = slabBlock
            blockData[i | STAIR] = stairBlock
            let v = slabBlock.shape.varients
            for (let j = 0; j < v.length; j++) {
                if (v[j]) {
                    let block = Object.create(baseBlock)
                    block.shape = v[j]
                    blockData[i | SLAB | j << 10] = block
                }
            }
            v = stairBlock.shape.varients
            for (let j = 0; j < v.length; j++) {
                if (v[j]) {
                    let block = Object.create(baseBlock)
                    block.shape = v[j]
                    blockData[i | STAIR | j << 10] = block
                }
            }
        }
    }
    let indexOrder;
    (function() {
        let arr = []
        for (let i = 0; i < 100000; i++) {
            arr.push(0 + i * 4, 1 + i * 4, 2 + i * 4, 0 + i * 4, 2 + i * 4, 3 + i * 4)
        }
        indexOrder = new Uint32Array(arr)
    })()

    let hexagonVerts
    let slabIconVerts
    let stairIconVerts
    let blockIcons
    {
        let side = Math.sqrt(3) / 2
        let s = side
        let q = s / 2
        hexagonVerts = new Float32Array([
            0, 1, 1, side, 0.5, 1, 0, 0, 1, -side, 0.5, 1,
            0, 0, 1, side, 0.5, 1, side, -0.5, 1, 0, -1, 1,
            -side, 0.5, 1, 0, 0, 1, 0, -1, 1, -side, -0.5, 1,
        ])

        slabIconVerts = new Float32Array([
            0, 0.5, 1, side, 0, 1, 0, -0.5, 1, -side, 0, 1,
            0, -0.5, 1, side, 0, 1, side, -0.5, 1, 0, -1, 1,
            -side, 0, 1, 0, -0.5, 1, 0, -1, 1, -side, -0.5, 1,
        ])

        stairIconVerts = [
            -s,0.5,0,0,1,         0,1,1,0,1,         q,0.75,1,0.5,1,    -q,0.25,0,0.5,1,    // top of the top step
            -q,-0.25,0,0,1,       q,0.25,1,0,1,      s,0,1,0.5,1,        0,-0.5,0,0.5,1,    // top of the bottom step
            -q,0.25,0,0,0.6,      q,0.75,1,0,0.6,    q,0.25,1,0.5,0.6,  -q,-0.25,0,0.5,0.6, // front of the top step
            0,-0.5,0,0,0.6,       s,0,1,0,0.6,       s,-0.5,1,0.5,0.6,   0,-1,0,0.5,0.6,    // front of the bottom step
            -s,0.5,0,0,0.8,      -q,0.25,0.5,0,0.8, -q,-0.75,0.5,1,0.8, -s,-0.5,0,1,0.8,    // side of the top step
            -q,-0.25,0.5,0.5,0.8, 0,-0.5,1,0.5,0.8,  0,-1,1,1,0.8,      -q,-0.75,0.5,1,0.8, // side of the bottom step
        ]
    }
    function genIcons() {
        blockIcons = [null]
        blockIcons.lengths = []
        let texOrder = [ 1, 2, 3 ]
        let shadows = [ 1, 0.4, 0.7 ]
        let scale = 0.16 / height * inventory.size
        for (let i = 1; i < BLOCK_COUNT; i++) {
            let data = []
            let block = blockData[i]
            for (let j = 11; j >= 0; j--) {
                data.push(-hexagonVerts[j * 3 + 0] * scale)
                data.push(hexagonVerts[j * 3 + 1] * scale)
                data.push(0.1666666)
                data.push(textureCoords[textureMap[block.textures[texOrder[Math.floor(j / 4)]]]][(j * 2 + 0) % 8])
                data.push(textureCoords[textureMap[block.textures[texOrder[Math.floor(j / 4)]]]][(j * 2 + 1) % 8])
                data.push(shadows[Math.floor(j / 4)])
            }
            let buffer = gl.createBuffer()
            gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW)
            blockIcons[i] = buffer
            blockIcons.lengths[i] = 6 * 3

            data = []
            for (let j = 11; j >= 0; j--) {
                let tex = textureCoords[textureMap[block.textures[texOrder[Math.floor(j / 4)]]]]

                data.push(-slabIconVerts[j * 3 + 0] * scale)
                data.push(slabIconVerts[j * 3 + 1] * scale)
                data.push(0.1666666)
                data.push(tex[(j * 2 + 0) % 8])
                data.push(tex[(j * 2 + 1) % 8])
                data.push(shadows[Math.floor(j / 4)])
            }
            buffer = gl.createBuffer()
            gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW)
            blockIcons[i | SLAB] = buffer
            blockIcons.lengths[i | SLAB] = 6 * 3

            data = []
            let v = stairIconVerts
            for (let j = 23; j >= 0; j--) {
                let num = Math.floor(j / 8)
                let tex = textureCoords[textureMap[block.textures[texOrder[num]]]]
                let tx = tex[0]
                let ty = tex[1]
                data.push(-v[j * 5 + 0] * scale)
                data.push(v[j * 5 + 1] * scale)
                data.push(0.1666666)
                data.push(tx + v[j * 5 + 2] / 16)
                data.push(ty + v[j * 5 + 3] / 16)
                data.push(shadows[num])
            }
            buffer = gl.createBuffer()
            gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW)
            blockIcons[i | STAIR] = buffer
            blockIcons.lengths[i | STAIR] = 6 * 6
        }
    }

    function uniformMatrix(cacheId, programObj, vrName, transpose, matrix) {
        let vrLocation = glCache[cacheId]
        if(vrLocation === undefined) {
            vrLocation = gl.getUniformLocation(programObj, vrName)
            glCache[cacheId] = vrLocation
        }
        gl.uniformMatrix4fv(vrLocation, transpose, matrix)
    }
    function vertexAttribPointer(cacheId, programObj, vrName, size, VBO) {
        let vrLocation = glCache[cacheId]
        if(vrLocation === undefined) {
            vrLocation = gl.getAttribLocation(programObj, vrName)
            glCache[cacheId] = vrLocation
        }
        if (vrLocation !== -1) {
            gl.enableVertexAttribArray(vrLocation)
            gl.bindBuffer(gl.ARRAY_BUFFER, VBO)
            gl.vertexAttribPointer(vrLocation, size, gl.FLOAT, false, 0, 0)

        }
    }

    //Generate buffers for every block face and store them
    let sideEdgeBuffers
    let indexBuffer

    function cross(v1, v2, result) {
        let x = v1.x,
            y = v1.y,
            z = v1.z,
            x2 = v2.x,
            y2 = v2.y,
            z2 = v2.z
        result.x = y * z2 - y2 * z
        result.y = z * x2 - z2 * x
        result.z = x * y2 - x2 * y
    }

    let matrix = new Float32Array(16); // A temperary matrix that may store random data.
    let projection = new Float32Array(16)
    let defaultModelView = new Float32Array([ -10,0,0,0,0,10,0,0,0,0,-10,0,0,0,0,1 ])
    class Matrix {
        constructor(arr) {
            this.elements = new Float32Array(arr || 16)
        }
        translate(x, y, z) {
            let a = this.elements
            a[3] += a[0] * x + a[1] * y + a[2] * z
            a[7] += a[4] * x + a[5] * y + a[6] * z
            a[11] += a[8] * x + a[9] * y + a[10] * z
            a[15] += a[12] * x + a[13] * y + a[14] * z
        }
        rotX(angle) {
            let elems = this.elements
            let c = Math.cos(angle)
            let s = Math.sin(angle)
            let t = elems[1]
            elems[1] = t * c + elems[2] * s
            elems[2] = t * -s + elems[2] * c
            t = elems[5]
            elems[5] = t * c + elems[6] * s
            elems[6] = t * -s + elems[6] * c
            t = elems[9]
            elems[9] = t * c + elems[10] * s
            elems[10] = t * -s + elems[10] * c
            t = elems[13]
            elems[13] = t * c + elems[14] * s
            elems[14] = t * -s + elems[14] * c
        }
        rotY(angle) {
            let c = Math.cos(angle)
            let s = Math.sin(angle)
            let elems = this.elements
            let t = elems[0]
            elems[0] = t * c + elems[2] * -s
            elems[2] = t * s + elems[2] * c
            t = elems[4]
            elems[4] = t * c + elems[6] * -s
            elems[6] = t * s + elems[6] * c
            t = elems[8]
            elems[8] = t * c + elems[10] * -s
            elems[10] = t * s + elems[10] * c
            t = elems[12]
            elems[12] = t * c + elems[14] * -s
            elems[14] = t * s + elems[14] * c
        }
        transpose() {
            let matrix = this.elements
            let temp = matrix[4]
            matrix[4] = matrix[1]
            matrix[1] = temp

            temp = matrix[8]
            matrix[8] = matrix[2]
            matrix[2] = temp

            temp = matrix[6]
            matrix[6] = matrix[9]
            matrix[9] = temp

            temp = matrix[3]
            matrix[3] = matrix[12]
            matrix[12] = temp

            temp = matrix[7]
            matrix[7] = matrix[13]
            matrix[13] = temp

            temp = matrix[11]
            matrix[11] = matrix[14]
            matrix[14] = temp
        }
        copyArray(from) {
            let to = this.elements
            for (let i = 0; i < from.length; i++) {
                to[i] = from[i]
            }
        }
        copyMatrix(from) {
            let to = this.elements
            from = from.elements
            for (let i = 0; i < from.length; i++) {
                to[i] = from[i]
            }
        }
    }

    class Plane {
        constructor(nx, ny, nz) {
            this.set(nx, ny, nz)
        }
        set(nx, ny, nz) {
            // Pre-computed chunk offsets to reduce branching during culling
            this.dx = nx > 0 ? 16 : 0
            this.dy = ny > 0
            this.dz = nz > 0 ? 16 : 0

            // Normal vector for the plane
            this.nx = nx
            this.ny = ny
            this.nz = nz
        }
    }

    let defaultTransformation = new Matrix([ -10,0,0,0,0,10,0,0,0,0,-10,0,0,0,0,1 ])
    class Camera {
        constructor() {
            this.x = 0
            this.y = 0
            this.z = 0
            this.rx = 0; // Pitch
            this.ry = 0; // Yaw
            this.currentFov = 0
            this.defaultFov = settings.fov
            this.targetFov = settings.fov
            this.step = 0
            this.lastStep = 0
            this.projection = new Float32Array(5)
            this.transformation = new Matrix()
            this.direction = { x: 1, y: 0, z: 0 }; // Normalized direction vector
            this.frustum = [] // The 5 planes of the viewing frustum (there's no far plane)
            for (let i = 0; i < 5; i++) {
                this.frustum.push(new Plane(1, 0, 0))
            }
        }
        FOV(fov, time) {
            if (fov === this.currentFov) return

            if (!fov) {
                let now = Date.now()
                fov = this.currentFov + this.step * (now - this.lastStep)
                this.lastStep = now
                if (Math.sign(this.targetFov - this.currentFov) !== Math.sign(this.targetFov - fov)) {
                    fov = this.targetFov
                }
            }
            else if (time) {
                this.targetFov = fov
                this.step = (fov - this.currentFov) / time
                this.lastStep = Date.now()
                return
            } else {
                this.targetFov = fov
            }

            const tang = Math.tan(fov * Math.PI / 360)
            const scale = 1 / tang
            const near = 1
            const far = 1000000
            this.currentFov = fov; // Store the state of the projection matrix
            this.nearH = near * tang; // This is needed for frustum culling

            this.projection[0] = scale / width * height
            this.projection[1] = scale
            this.projection[2] = -far / (far - near)
            this.projection[3] = -1
            this.projection[4] = -far * near / (far - near)
        }
        transform() {
            this.transformation.copyMatrix(defaultTransformation)
            this.transformation.rotX(this.rx)
            this.transformation.rotY(this.ry)
            this.transformation.translate(-this.x, -this.y, -this.z)
        }
        getMatrix() {
            let proj = this.projection
            let view = this.transformation.elements
            matrix[0]  = proj[0] * view[0]
            matrix[1]  = proj[1] * view[4]
            matrix[2]  = proj[2] * view[8] + proj[3] * view[12]
            matrix[3]  = proj[4] * view[8]
            matrix[4]  = proj[0] * view[1]
            matrix[5]  = proj[1] * view[5]
            matrix[6]  = proj[2] * view[9] + proj[3] * view[13]
            matrix[7]  = proj[4] * view[9]
            matrix[8]  = proj[0] * view[2]
            matrix[9]  = proj[1] * view[6]
            matrix[10] = proj[2] * view[10] + proj[3] * view[14]
            matrix[11] = proj[4] * view[10]
            matrix[12] = proj[0] * view[3]
            matrix[13] = proj[1] * view[7]
            matrix[14] = proj[2] * view[11] + proj[3] * view[15]
            matrix[15] = proj[4] * view[11]
            return matrix
        }
        setDirection() {
            if (this.targetFov !== this.currentFov) {
                this.FOV()
            }
            this.direction.x = -Math.sin(this.ry) * Math.cos(this.rx)
            this.direction.y = Math.sin(this.rx)
            this.direction.z = Math.cos(this.ry) * Math.cos(this.rx)
            this.computeFrustum()
        }
        computeFrustum() {
            let X = vec1
            let dir = this.direction
            X.x = dir.z
            X.y = 0
            X.z = -dir.x
            X.normalize()

            let Y = vec2
            Y.set(dir)
            Y.mult(-1)
            cross(Y, X, Y)

            //Near plane
            this.frustum[0].set(dir.x, dir.y, dir.z)

            let aux = vec3
            aux.set(Y)
            aux.mult(this.nearH)
            aux.add(dir)
            aux.normalize()
            cross(aux, X, aux)
            this.frustum[1].set(aux.x, aux.y, aux.z)

            aux.set(Y)
            aux.mult(-this.nearH)
            aux.add(dir)
            aux.normalize()
            cross(X, aux, aux)
            this.frustum[2].set(aux.x, aux.y, aux.z)

            aux.set(X)
            aux.mult(-this.nearH * width / height)
            aux.add(dir)
            aux.normalize()
            cross(aux, Y, aux)
            this.frustum[3].set(aux.x, aux.y, aux.z)

            aux.set(X)
            aux.mult(this.nearH * width / height)
            aux.add(dir)
            aux.normalize()
            cross(Y, aux, aux)
            this.frustum[4].set(aux.x, aux.y, aux.z)
        }
        canSee(x, y, z, maxY) {
            x -= 0.5
            y -= 0.5
            z -= 0.5
            maxY += 0.5
            let px = 0, py = 0, pz = 0, plane = null
            let cx = p.x, cy = p.y, cz = p.z
            for (let i = 0; i < 5; i++) {
                plane = this.frustum[i]
                px = x + plane.dx
                py = plane.dy ? maxY : y
                pz = z + plane.dz
                if ((px - cx) * plane.nx + (py - cy) * plane.ny + (pz - cz) * plane.nz < 0) {
                    return false
                }
            }
            return true
        }
    }

    function trans(matrix, x, y, z) {
        let a = matrix
        a[3] += a[0] * x + a[1] * y + a[2] * z
        a[7] += a[4] * x + a[5] * y + a[6] * z
        a[11] += a[8] * x + a[9] * y + a[10] * z
        a[15] += a[12] * x + a[13] * y + a[14] * z
    }
    function rotX(matrix, angle) {
        // This function is basically multiplying 2 4x4 matrices together,
        // but 1 of them has a bunch of 0's and 1's in it,
        // so I removed all terms that multiplied by 0, and just left off the 1's.
        // mat2 = [1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]
        let elems = matrix
        let c = Math.cos(angle)
        let s = Math.sin(angle)
        let t = elems[1]
        elems[1] = t * c + elems[2] * s
        elems[2] = t * -s + elems[2] * c
        t = elems[5]
        elems[5] = t * c + elems[6] * s
        elems[6] = t * -s + elems[6] * c
        t = elems[9]
        elems[9] = t * c + elems[10] * s
        elems[10] = t * -s + elems[10] * c
        t = elems[13]
        elems[13] = t * c + elems[14] * s
        elems[14] = t * -s + elems[14] * c
    }
    function rotY(matrix, angle) {
    //source = c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1
        let c = Math.cos(angle)
        let s = Math.sin(angle)
        let elems = matrix
        let t = elems[0]
        elems[0] = t * c + elems[2] * -s
        elems[2] = t * s + elems[2] * c
        t = elems[4]
        elems[4] = t * c + elems[6] * -s
        elems[6] = t * s + elems[6] * c
        t = elems[8]
        elems[8] = t * c + elems[10] * -s
        elems[10] = t * s + elems[10] * c
        t = elems[12]
        elems[12] = t * c + elems[14] * -s
        elems[14] = t * s + elems[14] * c
    }
    function transpose(matrix) {
        let temp = matrix[4]
        matrix[4] = matrix[1]
        matrix[1] = temp

        temp = matrix[8]
        matrix[8] = matrix[2]
        matrix[2] = temp

        temp = matrix[6]
        matrix[6] = matrix[9]
        matrix[9] = temp

        temp = matrix[3]
        matrix[3] = matrix[12]
        matrix[12] = temp

        temp = matrix[7]
        matrix[7] = matrix[13]
        matrix[13] = temp

        temp = matrix[11]
        matrix[11] = matrix[14]
        matrix[14] = temp
    }
    function matMult() {
    //Multiply the projection matrix by the view matrix; this is optimized specifically for these matrices by removing terms that are always 0.
        let proj = projection
        let view = modelView
        matrix[0] = proj[0] * view[0]
        matrix[1] = proj[0] * view[1]
        matrix[2] = proj[0] * view[2]
        matrix[3] = proj[0] * view[3]
        matrix[4] = proj[5] * view[4]
        matrix[5] = proj[5] * view[5]
        matrix[6] = proj[5] * view[6]
        matrix[7] = proj[5] * view[7]
        matrix[8] = proj[10] * view[8] + proj[11] * view[12]
        matrix[9] = proj[10] * view[9] + proj[11] * view[13]
        matrix[10] = proj[10] * view[10] + proj[11] * view[14]
        matrix[11] = proj[10] * view[11] + proj[11] * view[15]
        matrix[12] = proj[14] * view[8]
        matrix[13] = proj[14] * view[9]
        matrix[14] = proj[14] * view[10]
        matrix[15] = proj[14] * view[11]
    }
    function copyArr(a, b) {
        for (let i = 0; i < a.length; i++) {
            b[i] = a[i]
        }
    }
    function FOV(fov) {
        let tang = Math.tan(fov * 0.5 * Math.PI / 180)
        let scale = 1 / tang
        let near = 1
        let far = 1000000
        currentFov = fov

        projection[0] = scale / width * height
        projection[5] = scale
        projection[10] = -far / (far - near)
        projection[11] = -1
        projection[14] = -far * near / (far - near)
    }
    function initModelView(camera, x, y, z, rx, ry) {
        if (camera) {
            camera.transform()
            uniformMatrix("view3d", program3D, "uView", false, camera.getMatrix())
        } else {
            copyArr(defaultModelView, modelView)
            rotX(modelView, rx)
            rotY(modelView, ry)
            trans(modelView, -x, -y, -z)
            matMult()
            transpose(matrix)
            uniformMatrix("view3d", program3D, "uView", false, matrix)
        }
    }

    function timeString(millis) {
        if (millis > 300000000000 || !millis) {
            return "never"
        }
        const SECOND = 1000
        const MINUTE = SECOND * 60
        const HOUR = MINUTE * 60
        const DAY = HOUR * 24
        const YEAR = DAY * 365

        if (millis < MINUTE) {
            return "just now"
        }

        let years = Math.floor(millis / YEAR)
        millis -= years * YEAR

        let days = Math.floor(millis / DAY)
        millis -= days * DAY

        let hours = Math.floor(millis / HOUR)
        millis -= hours * HOUR

        let minutes = Math.floor(millis / MINUTE)

        if (years) {
            return `${years} year${years > 1 ? "s" : ""} and ${days} day${day !== 1 ? "s" : ""} ago`
        }
        if (days) {
            return `${days} day${days > 1 ? "s" : ""} and ${hours} hour${hours !== 1 ? "s" : ""} ago`
        }
        if (hours) {
            return `${hours} hour${hours > 1 ? "s" : ""} and ${minutes} minute${minutes !== 1 ? "s" : ""} ago`
        }
        return `${minutes} minute${minutes > 1 ? "s" : ""} ago`
    }
    function roundBits(number) {
        return Math.round(number * 1000000) / 1000000
    }
    function rayTrace(x, y, z, shape) {
        let cf, cd = 1e9; //Closest face and distance
        let m; //Absolute distance to intersection point
        let ix, iy, iz; //Intersection coords
        let minX, miny, minz, maxX, maxY, maxZ, min, max; //Bounds of face coordinates
        let east = p.direction.x < 0
        let top = p.direction.y < 0
        let north = p.direction.z < 0
        let verts = shape.verts
        let faces = verts[0]

        //Top and bottom faces

        if (top) {
            faces = verts[1]
        }
        if (p.direction.y) {
            for (let face of faces) {
                min = face.min
                minX = min[0]
                minZ = min[2]
                max = face.max
                maxX = max[0]
                maxZ = max[2]
                m = (y + face[1] - p.y) / p.direction.y
                ix = m * p.direction.x + p.x
                iz = m * p.direction.z + p.z
                if (m > 0 && m < cd && ix >= x + minX && ix <= x + maxX && iz >= z + minZ && iz <= z + maxZ) {
                    cd = m; //Ray crosses bottom face
                    cf = top ? "top" : "bottom"
                }
            }
        }

        //West and East faces
        if (east) {
            faces = verts[4]
        } else {
            faces = verts[5]
        }
        if (p.direction.x) {
            for (let face of faces) {
                min = face.min
                minY = min[1]
                minZ = min[2]
                max = face.max
                maxY = max[1]
                maxZ = max[2]
                m = (x + face[0] - p.x) / p.direction.x
                iy = m * p.direction.y + p.y
                iz = m * p.direction.z + p.z
                if (m > 0 && m < cd && iy >= y + minY && iy <= y + maxY && iz >= z + minZ && iz <= z + maxZ) {
                    cd = m
                    cf = east ? "east" : "west"
                }
            }
        }

        //South and North faces
        if (north) {
            faces = verts[2]
        } else {
            faces = verts[3]
        }
        if (p.direction.z) {
            for (let face of faces) {
                min = face.min
                minX = min[0]
                minY = min[1]
                max = face.max
                maxX = max[0]
                maxY = max[1]
                m = (z + face[2] - p.z) / p.direction.z
                ix = m * p.direction.x + p.x
                iy = m * p.direction.y + p.y
                if (m > 0 && m < cd && ix >= x + minX && ix <= x + maxX && iy >= y + minY && iy <= y + maxY) {
                    cd = m
                    cf = north ? "north" : "south"
                }
            }
        }
        return [ cd, cf ]
    }
    function runRayTrace(x, y, z) {
        let block = world.getBlock(x, y, z)
        if (block) {
            let shape = blockData[block].shape
            let rt = rayTrace(x, y, z, blockData[block].shape)

            if (rt[1] && rt[0] < hitBox.closest) {
                hitBox.closest = rt[0]
                hitBox.face = rt[1]
                hitBox.pos = [ x, y, z ]
                hitBox.shape = blockData[block].shape
            }
        }
    }
    function lookingAt() {
        // Checks blocks in front of the player to see which one they're looking at
        hitBox.pos = null
        hitBox.closest = 1e9

        if (p.spectator) {
            return
        }
        let blockState = world.getBlock(p2.x, p2.y, p2.z)
        if (blockState) {
            hitBox.pos = [ p2.x, p2.y, p2.z ]
            hitBox.closest = 0
            hitBox.shape = blockData[blockState].shape
            return
        }

        let pd = p.direction

        // Target block
        let tx = Math.round(pd.x * reach + p.x)
        let ty = Math.round(pd.y * reach + p.y)
        let tz = Math.round(pd.z * reach + p.z)

        let minX = p2.x
        let maxX = 0
        let minY = p2.y
        let maxY = 0
        let minZ = p2.z
        let maxZ = 0

        for (let i = 0; i < reach + 1; i++) {
            if (i > reach) {
                i = reach
            }
            maxX = Math.round(p.x + pd.x * i)
            maxY = Math.round(p.y + pd.y * i)
            maxZ = Math.round(p.z + pd.z * i)
            if (maxX === minX && maxY === minY && maxZ === minZ) {
                continue
            }
            if (minX !== maxX) {
                if (minY !== maxY) {
                    if (minZ !== maxZ) {
                        runRayTrace(maxX, maxY, maxZ)
                    }
                    runRayTrace(maxX, maxY, minZ)
                }
                if (minZ !== maxZ) {
                    runRayTrace(maxX, minY, maxZ)
                }
                runRayTrace(maxX, minY, minZ)
            }
            if (minY !== maxY) {
                if (minZ !== maxZ) {
                    runRayTrace(minX, maxY, maxZ)
                }
                runRayTrace(minX, maxY, minZ)
            }
            if (minZ !== maxZ) {
                runRayTrace(minX, minY, maxZ)
            }
            if (hitBox.pos) {
                return; //The ray has collided; it can't possibly find a closer collision now
            }
            minZ = maxZ
            minY = maxY
            minX = maxX
        }
    }
    let inBox = function(x, y, z, w, h, d) {
        let iy = y - h/2 - p.topH
        let ih = h + p.bottomH + p.topH
        let ix = x - w/2 - p.w
        let iw = w + p.w*2
        let iz = z - d/2 - p.w
        let id = d + p.w*2
        return p.x > ix && p.y > iy && p.z > iz && p.x < ix + iw && p.y < iy + ih && p.z < iz + id
    }
    let onBox = function(x, y, z, w, h, d) {
        let iy = roundBits(y - h/2 - p.topH)
        let ih = roundBits(h + p.bottomH + p.topH)
        let ix = roundBits(x - w/2 - p.w)
        let iw = roundBits(w + p.w*2)
        let iz = roundBits(z - d/2 - p.w)
        let id = roundBits(d + p.w*2)
        return p.x > ix && p.y > iy && p.z > iz && p.x < ix + iw && p.y <= iy + ih && p.z < iz + id
    }
    function collided(x, y, z, vx, vy, vz, block) {
        if(p.spectator) {
            return false
        }
        let verts = blockData[block].shape.verts
        let px = roundBits(p.x - p.w - x)
        let py = roundBits(p.y - p.bottomH - y)
        let pz = roundBits(p.z - p.w - z)
        let pxx = roundBits(p.x + p.w - x)
        let pyy = roundBits(p.y + p.topH - y)
        let pzz = roundBits(p.z + p.w - z)
        let minX, minY, minZ, maxX, maxY, maxZ, min, max
        if(block == blockIds.Water) {
            p.jumpSpeed = 0.3;
            p.gravityStength = -0.03;
            liquid = true
            r = 255
            return false;
        }

        else{
            liquid = false
            p.jumpSpeed = 0.3;
            p.gravityStength = -0.031;
            }
            if (block == !blockIds.Water){
            p.jumpSpeed = 0.3;
            p.gravityStength = -0.031;
            }

        //Top and bottom faces
        let faces = verts[0]
        if (vy <= 0) {
            faces = verts[1]
        }
        if (!vx && !vz) {
            for (let face of faces) {
                min = face.min
                minX = min[0]
                minZ = min[2]
                max = face.max
                maxX = max[0]
                maxZ = max[2]
                if (face[1] > py && face[1] < pyy && minX < pxx && maxX > px && minZ < pzz && maxZ > pz) {
                    if (vy <= 0) {
                        p.onGround = true
                        p.y = Math.round((face[1] + y + p.bottomH) * 10000) / 10000
                        return false
                    } else {
                        return true
                    }
                }
            }
            return false
        }

        //West and East faces
        if (vx < 0) {
            faces = verts[4]
        } else if (vx > 0) {
            faces = verts[5]
        }
        if (vx) {
            let col = false
            for (let face of faces) {
                min = face.min
                minZ = min[2]
                minY = min[1]
                max = face.max
                maxZ = max[2]
                maxY = max[1]
                if (face[0] > px && face[0] < pxx && minY < pyy && maxY > py && minZ < pzz && maxZ > pz) {
                    if (maxY - py > 0.5) {
                        p.canStepX = false
                    }
                    col = true
                }
            }
            return col
        }

        //South and North faces
        if (vz < 0) {
            faces = verts[2]
        } else if (vz > 0) {
            faces = verts[3]
        }
        if (vz) {
            let col = false
            for (let face of faces) {
                min = face.min
                minX = min[0]
                minY = min[1]
                max = face.max
                maxX = max[0]
                maxY = max[1]
                if (face[2] > pz && face[2] < pzz && minY < pyy && maxY > py && minX < pxx && maxX > px) {
                    if (maxY - py > 0.5) {
                        p.canStepZ = false
                    }
                    col = true
                }
            }
            return col
        }
    }
    let contacts = {
        array: [],
        size: 0,
        add: function(x, y, z, block) {
            if (this.size === this.array.length) {
                this.array.push([ x, y, z, block ])
            } else {
                this.array[this.size][0] = x
                this.array[this.size][1] = y
                this.array[this.size][2] = z
                this.array[this.size][3] = block
            }
            this.size++
        },
        clear: function() {
            this.size = 0
        },
    }
    let resolveContactsAndUpdatePosition = function() {
        let pminX = p2.x - 1
        let pmaxX = p2.x + 1
        let pminY = p2.y - 2
        let pmaxY = p2.y + 1
        let pminZ = p2.z - 1
        let pmaxZ = p2.z + 1
        let block = null
        let vel = p.velocity

        for (let x = pminX; x <= pmaxX; x++) {
            for (let y = pminY; y <= pmaxY; y++) {
                for (let z = pminZ; z <= pmaxZ; z++) {
                    let block = world.getBlock(x, y, z)
                    if (block) {
                        contacts.add(x, y, z, block)
                    }
                }
            }
        }

        let dt = (win.performance.now() - p.lastUpdate) / 33
        dt = dt > 2 ? 2 : dt

        p.previousX = p.x
        p.previousY = p.y
        p.previousZ = p.z

        //Check collisions in the Y direction
        p.onGround = false
        p.canStepX = false
        p.canStepZ = false
        p.y += vel.y * dt
        for (let i = 0; i < contacts.size; i++) {
            block = contacts.array[i]
            if (collided(block[0], block[1], block[2], 0, vel.y, 0, block[3])) {
                p.y = p.previousY
                vel.y = 0
                break
            }
        }
        if (p.y === p.previousY && !p.flying) {
            p.canStepX = true
            p.canStepZ = true
        }

        var sneakLock = false, sneakSafe = false
        if (p.sneaking) {
            for (let i = 0; i < contacts.size; i++) {
                block = contacts.array[i]
                if (onBox(block[0], block[1], block[2], 1, 1, 1)) {
                    sneakLock = true
                    break
                }
            }
        }

        //Check collisions in the X direction
        p.x += vel.x * dt
        for (let i = 0; i < contacts.size; i++) {
            block = contacts.array[i]
            if (collided(block[0], block[1], block[2], vel.x, 0, 0, block[3])) {
                if (p.canStepX && !world.getBlock(block[0], block[1] + 1, block[2]) && !world.getBlock(block[0], block[1] + 2, block[2])) {
                    continue
                }
                p.x = p.previousX
                vel.x = 0
                break
            }
            if (sneakLock && onBox(block[0], block[1], block[2], 1, 1, 1)) {
                sneakSafe = true
            }
        }

        if (sneakLock && !sneakSafe) {
            p.x = p.previousX
            vel.x = 0
        }
        sneakSafe = false

        //Check collisions in the Z direction
        p.z += vel.z * dt
        for (let i = 0; i < contacts.size; i++) {
            block = contacts.array[i]
            if (collided(block[0], block[1], block[2], 0, 0, vel.z, block[3])) {
                if (p.canStepZ && !world.getBlock(block[0], block[1] + 1, block[2]) && !world.getBlock(block[0], block[1] + 2, block[2])) {
                    continue
                }
                p.z = p.previousZ
                vel.z = 0
                break
            }
            if (sneakLock && onBox(block[0], block[1], block[2], 1, 1, 1)) {
                sneakSafe = true
            }
        }

        if (sneakLock && !sneakSafe) {
            p.z = p.previousZ
            vel.z = 0
        }

        if (!p.flying) {
            let drag = p.onGround ? 0.5 : 0.85
            p.velocity.z += (p.velocity.z * drag - p.velocity.z) * dt
            p.velocity.x += (p.velocity.x * drag - p.velocity.x) * dt
        } else {
            let drag = 0.9
            p.velocity.z += (p.velocity.z * drag - p.velocity.z) * dt
            p.velocity.x += (p.velocity.x * drag - p.velocity.x) * dt
            p.velocity.y += (p.velocity.y * 0.8 - p.velocity.y) * dt
            if (p.onGround && !p.spectator) {
                p.flying = false
            }
        }

        p.lastUpdate = win.performance.now()
        contacts.clear()
        lookingAt()
    }
    let runGravity = function() {
        if (p.flying) {
            return
        }
        let dt = (win.performance.now() - p.lastUpdate) / 33
        dt = dt > 2 ? 2 : dt
        if(p.onGround) {
            if(p.velocity.y < -0.5) {
                p.health -= -Math.floor(p.velocity.y*-(p.velocity.y*15));
            }
            if(Key[" "]) {
                p.velocity.y = p.jumpSpeed;
                p.onGround = false;
            } else {

                p.velocity.y = 0;
            }
        } else {
            p.velocity.y += p.gravityStength * dt;
            if(p.velocity.y < -p.maxYVelocity) {
                p.velocity.y = -p.maxYVelocity;
            }
        }
        if(p.health <= 0) {
         setTimeout(() => {  changeScene("dead"); }, 200);

        }
    };

    function box2(sides, tex) {
        if (blockFill) {
            let i = 0
            for (let side in Block) {
                if (sides & Block[side]) {
                    vertexAttribPointer("aVertex", program3D, "aVertex", 3, sideEdgeBuffers[Sides[side]])
                    vertexAttribPointer("aTexture", program3D, "aTexture", 2, texCoordsBuffers[textureMap[tex[i]]])
                    gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, 0)
                }
                i++
            }
        }
        if (blockOutlines) {
            vertexAttribPointer("aVertex", program3D, "aVertex", 3, hitBox.shape.buffer)
            vertexAttribPointer("aTexture", program3D, "aTexture", 2, texCoordsBuffers[textureMap.hitbox])
            for (let i = 0; i < hitBox.shape.size; i++) {
                gl.drawArrays(gl.LINE_LOOP, i * 4, 4)
            }
        }
    }
    function block2(x, y, z, t, camera) {
        if (camera) {
            camera.transformation.translate(x, y, z)
            uniformMatrix("view3d", program3D, "uView", false, camera.getMatrix())
        } else {
            //copyArr(modelView, matrix)
            trans(modelView, x, y, z)
            matMult()
            trans(modelView, -x, -y, -z)
            transpose(matrix)
            uniformMatrix("view3d", program3D, "uView", false, matrix)
        }
        box2(0xff, blockData[t].textures)
    }

    function changeWorldBlock(t) {
        let pos = hitBox.pos
        if(pos && pos[1] > 0 && pos[1] < maxHeight) {
            let shape = t && blockData[t].shape
            if (t && shape.rotate) {
                let pi = Math.PI / 4
                if (p.ry <= pi) {} // North; default
                else if (p.ry < 3 * pi) {
                    t |= WEST
                } else if (p.ry < 5 * pi) {
                    t |= SOUTH
                } else if (p.ry < 7 * pi) {
                    t |= EAST
                }
            }

            if (t && shape.flip && hitBox.face !== "top" && (hitBox.face === "bottom" || (p.direction.y * hitBox.closest + p.y) % 1 < 0.5)) {
                t |= FLIP
            }

            world.setBlock(hitBox.pos[0], hitBox.pos[1], hitBox.pos[2], t)
            if (t) {
                p.lastPlace = Date.now()
            } else {
                p.lastBreak = Date.now()
            }
        }
    }
    function newWorldBlock() {
        if(!hitBox.pos || !holding) {
            return
        }
        let pos = hitBox.pos, x= pos[0], y = pos[1], z = pos[2]
        switch(hitBox.face) {
            case "top":
                y += 1
                break
            case "bottom":
                y -= 1
                break
            case "south":
                z -= 1
                break
            case "north":
                z += 1
                break
            case "west":
                x -= 1
                break
            case "east":
                x += 1
                break
        }
        if (!inBox(x, y, z, 1, 1, 1) && !world.getBlock(x, y, z)) {
            pos[0] = x
            pos[1] = y
            pos[2] = z
            changeWorldBlock(holding < 0xff ? (holding | blockMode) : holding)
        }
    }

    // Save the coords for a small sphere used to carve out caves
    let sphere;
    {
        let blocks = []
        let radius = 3.5
        let radsq = radius * radius
        for (let i = -radius; i <= radius; i++) {
            for (let j = -radius; j <= radius; j++) {
                for (let k = -radius; k <= radius; k++) {
                    if (i*i + j*j + k*k < radsq) {
                        blocks.push(i|0, j|0, k|0)
                    }
                }
            }
        }
        sphere = new Int8Array(blocks)
    }

    function isCave(x, y, z) {
        // Generate a 3D rigid multifractal noise shell.
        // Then generate another one with different coordinates.
        // Overlay them on top of each other, and the overlapping parts should form a cave-like structure.
        // This is extremely slow, and requires generating 2 noise values for every single block in the world.
        // TODO: replace with a crawler system of some sort, that will never rely on a head position in un-generated chunks.
        let smooth = 0.02
        let caveSize = 0.0055
        let cave1 = Math.abs(0.5 - caveNoise(x * smooth, y * smooth, z * smooth)) < caveSize
        let cave2 = Math.abs(0.5 - caveNoise(y * smooth, z * smooth, x * smooth)) < caveSize
        return (cave1 && cave2)
    }
    function carveSphere(x, y, z) {
        if (y > 3) {
            for (let i = 0; i < sphere.length; i += 3) {
                world.setBlock(x + sphere[i], y + sphere[i + 1], z + sphere[i + 2], blockIds.air, true)
            }
        }
    }

    let renderedChunks = 0
    function getBlock(x, y, z, blocks) {
        return blocks[((x >> 4) + 1) * 9 + ((y >> 4) + 1) * 3 + (z >> 4) + 1][((x & 15) << 8) + ((y & 15) << 4) + (z & 15)]
    }
    /**
     * Returns a 1 if the face is exposed and should be drawn, or a 0 if the face is hidden
     * 
     * @param {number} x - The X coordinate of the block that may be covering a face
     * @param {number} y - The Y coordinate of the block that may be covering a face
     * @param {number} z - The Z coordinate of the block that may be covering a face
     * @param {Collection} blocks - Some collection of blocks that can return the block at (x, y, z)
     * @param {number} type - The blockstate of the block that's being considered for face culling
     * @param {function} func - The function that can be called to return a block from the blocks collection
    */
    function hideFace(x, y, z, blocks, type, func, sourceDir, dir) {
        let block = func.call(world, x, y, z, blocks)
        if (!block) {
            return 1
        }

        let data = blockData[block]
        let sourceData = blockData[type]

        let sourceRange = 3
        let hiderRange = 3
        if (func !== getBlock || screen === "loading") {
            // getBlock is only used during the optimize phase of worldGen
            sourceRange = sourceData.shape.cull[sourceDir]
            hiderRange = data.shape.cull[dir]
        }

        if ((sourceRange & hiderRange) !== sourceRange || sourceRange === 0 || block !== type && data.transparent || data.transparent && data.shadow) {
            return 1
        }
        return 0
    }
    let getShadows = {
        shade: [ 1, 0.85, 0.7, 0.6, 0.3 ],
        ret: [],
        blocks: [],
        top: function(x, y, z, block) { // Actually the bottom... How did these get flipped?
            let blocks = this.blocks
            let ret = this.ret
            blocks[0] = blockData[getBlock(x-1, y-1, z-1, block)].shadow
            blocks[1] = blockData[getBlock(x, y-1, z-1, block)].shadow
            blocks[2] = blockData[getBlock(x+1, y-1, z-1, block)].shadow
            blocks[3] = blockData[getBlock(x-1, y-1, z, block)].shadow
            blocks[4] = blockData[getBlock(x, y-1, z, block)].shadow
            blocks[5] = blockData[getBlock(x+1, y-1, z, block)].shadow
            blocks[6] = blockData[getBlock(x-1, y-1, z+1, block)].shadow
            blocks[7] = blockData[getBlock(x, y-1, z+1, block)].shadow
            blocks[8] = blockData[getBlock(x+1, y-1, z+1, block)].shadow

            ret[0] = this.shade[blocks[0] + blocks[1] + blocks[3] + blocks[4]]*0.75
            ret[1] = this.shade[blocks[1] + blocks[2] + blocks[4] + blocks[5]]*0.75
            ret[2] = this.shade[blocks[5] + blocks[4] + blocks[8] + blocks[7]]*0.75
            ret[3] = this.shade[blocks[4] + blocks[3] + blocks[7] + blocks[6]]*0.75
            return ret
        },
        bottom: function(x, y, z, block) { // Actually the top
            let blocks = this.blocks
            let ret = this.ret
            blocks[0] = blockData[getBlock(x-1, y+1, z-1, block)].shadow
            blocks[1] = blockData[getBlock(x, y+1, z-1, block)].shadow
            blocks[2] = blockData[getBlock(x+1, y+1, z-1, block)].shadow
            blocks[3] = blockData[getBlock(x-1, y+1, z, block)].shadow
            blocks[4] = blockData[getBlock(x, y+1, z, block)].shadow
            blocks[5] = blockData[getBlock(x+1, y+1, z, block)].shadow
            blocks[6] = blockData[getBlock(x-1, y+1, z+1, block)].shadow
            blocks[7] = blockData[getBlock(x, y+1, z+1, block)].shadow
            blocks[8] = blockData[getBlock(x+1, y+1, z+1, block)].shadow

            ret[0] = this.shade[blocks[4] + blocks[3] + blocks[7] + blocks[6]]
            ret[1] = this.shade[blocks[5] + blocks[4] + blocks[8] + blocks[7]]
            ret[2] = this.shade[blocks[1] + blocks[2] + blocks[4] + blocks[5]]
            ret[3] = this.shade[blocks[0] + blocks[1] + blocks[3] + blocks[4]]
            return ret
        },
        north: function(x, y, z, block) {
            let blocks = this.blocks
            let ret = this.ret
            blocks[0] = blockData[getBlock(x-1, y-1, z+1, block)].shadow
            blocks[1] = blockData[getBlock(x, y-1, z+1, block)].shadow
            blocks[2] = blockData[getBlock(x+1, y-1, z+1, block)].shadow
            blocks[3] = blockData[getBlock(x-1, y, z+1, block)].shadow
            blocks[4] = blockData[getBlock(x, y, z+1, block)].shadow
            blocks[5] = blockData[getBlock(x+1, y, z+1, block)].shadow
            blocks[6] = blockData[getBlock(x-1, y+1, z+1, block)].shadow
            blocks[7] = blockData[getBlock(x, y+1, z+1, block)].shadow
            blocks[8] = blockData[getBlock(x+1, y+1, z+1, block)].shadow

            ret[0] = this.shade[blocks[5] + blocks[4] + blocks[8] + blocks[7]]*0.95
            ret[1] = this.shade[blocks[4] + blocks[3] + blocks[7] + blocks[6]]*0.95
            ret[2] = this.shade[blocks[0] + blocks[1] + blocks[3] + blocks[4]]*0.95
            ret[3] = this.shade[blocks[1] + blocks[2] + blocks[4] + blocks[5]]*0.95
            return ret
        },
        south: function(x, y, z, block) {
            let blocks = this.blocks
            let ret = this.ret
            blocks[0] = blockData[getBlock(x-1, y-1, z-1, block)].shadow
            blocks[1] = blockData[getBlock(x-1, y, z-1, block)].shadow
            blocks[2] = blockData[getBlock(x-1, y+1, z-1, block)].shadow
            blocks[3] = blockData[getBlock(x, y-1, z-1, block)].shadow
            blocks[4] = blockData[getBlock(x, y, z-1, block)].shadow
            blocks[5] = blockData[getBlock(x, y+1, z-1, block)].shadow
            blocks[6] = blockData[getBlock(x+1, y-1, z-1, block)].shadow
            blocks[7] = blockData[getBlock(x+1, y, z-1, block)].shadow
            blocks[8] = blockData[getBlock(x+1, y+1, z-1, block)].shadow

            ret[0] = this.shade[blocks[1] + blocks[2] + blocks[4] + blocks[5]]*0.95
            ret[1] = this.shade[blocks[5] + blocks[4] + blocks[8] + blocks[7]]*0.95
            ret[2] = this.shade[blocks[4] + blocks[3] + blocks[7] + blocks[6]]*0.95
            ret[3] = this.shade[blocks[0] + blocks[1] + blocks[3] + blocks[4]]*0.95
            return ret
        },
        east: function(x, y, z, block) {
            let blocks = this.blocks
            let ret = this.ret
            blocks[0] = blockData[getBlock(x+1, y-1, z-1, block)].shadow
            blocks[1] = blockData[getBlock(x+1, y, z-1, block)].shadow
            blocks[2] = blockData[getBlock(x+1, y+1, z-1, block)].shadow
            blocks[3] = blockData[getBlock(x+1, y-1, z, block)].shadow
            blocks[4] = blockData[getBlock(x+1, y, z, block)].shadow
            blocks[5] = blockData[getBlock(x+1, y+1, z, block)].shadow
            blocks[6] = blockData[getBlock(x+1, y-1, z+1, block)].shadow
            blocks[7] = blockData[getBlock(x+1, y, z+1, block)].shadow
            blocks[8] = blockData[getBlock(x+1, y+1, z+1, block)].shadow

            ret[0] = this.shade[blocks[1] + blocks[2] + blocks[4] + blocks[5]]*0.8
            ret[1] = this.shade[blocks[5] + blocks[4] + blocks[8] + blocks[7]]*0.8
            ret[2] = this.shade[blocks[4] + blocks[3] + blocks[7] + blocks[6]]*0.8
            ret[3] = this.shade[blocks[0] + blocks[1] + blocks[3] + blocks[4]]*0.8
            return ret
        },
        west: function(x, y, z, block) {
            let blocks = this.blocks
            let ret = this.ret
            blocks[0] = blockData[getBlock(x-1, y-1, z-1, block)].shadow
            blocks[1] = blockData[getBlock(x-1, y, z-1, block)].shadow
            blocks[2] = blockData[getBlock(x-1, y+1, z-1, block)].shadow
            blocks[3] = blockData[getBlock(x-1, y-1, z, block)].shadow
            blocks[4] = blockData[getBlock(x-1, y, z, block)].shadow
            blocks[5] = blockData[getBlock(x-1, y+1, z, block)].shadow
            blocks[6] = blockData[getBlock(x-1, y-1, z+1, block)].shadow
            blocks[7] = blockData[getBlock(x-1, y, z+1, block)].shadow
            blocks[8] = blockData[getBlock(x-1, y+1, z+1, block)].shadow

            ret[0] = this.shade[blocks[7] + blocks[8] + blocks[4] + blocks[5]]*0.8
            ret[1] = this.shade[blocks[5] + blocks[4] + blocks[2] + blocks[1]]*0.8
            ret[2] = this.shade[blocks[4] + blocks[3] + blocks[1] + blocks[0]]*0.8
            ret[3] = this.shade[blocks[6] + blocks[7] + blocks[3] + blocks[4]]*0.8
            return ret
        },
    }

    function interpolateShadows(shadows, x, y) {
        let sx = (shadows[1] - shadows[0]) * x + shadows[0]
        let sx2 = (shadows[3] - shadows[2]) * x + shadows[2]
        return (sx2 - sx) * y + sx
    }

    class Section {
        constructor(x, y, z, size, chunk) {
            this.x = x
            this.y = y
            this.z = z
            this.size = size
            this.arraySize = size * size * size
            this.blocks = new Int32Array(this.arraySize)
            this.compressed = new Uint8Array(this.arraySize)
            this.renderData = []
            this.renderLength = 0
            this.faces = 0
            this.hasVisibleBlocks = false
            this.chunk = chunk
            this.edited = false
            this.caves = !caves
            this.pallete = [0]
            this.palleteMap = {"0": 0}
            this.palleteSize = 0
        }
        getBlock(x, y, z) {
            let s = this.size
            return this.blocks[x * s * s + y * s + z]
        }
        setBlock(x, y, z, blockId) {
            let s = this.size
            this.blocks[x * s * s + y * s + z] = blockId
        }
        deleteBlock(x, y, z) {
            let s = this.size
            this.blocks[x * s * s + y * s + z] = 0
        }
        optimize() {
            let visible = false
            let pos = 0
            let xx = this.x
            let yy = this.y
            let zz = this.z
            let blockState = 0
            let palleteIndex = 0
            let index = 0
            let s = this.size
            let blocks = this.blocks
            this.hasVisibleBlocks = false
            this.renderLength = 0
            let localBlocks = world.getAdjacentSubchunks(xx, yy, zz)

            //Check all the blocks in the subchunk to see if they're visible.
            for (let i = 0; i < s; i++) {
                for (let j = 0; j < s; j++) {
                    for (let k = 0; k < s; k++, index++) {
                        blockState = blocks[index]

                        if (this.palleteMap[blockState] === undefined) {
                            this.palleteMap[blockState] = this.pallete.length
                            palleteIndex = this.pallete.length
                            this.pallete.push(blockState)
                        } else {
                            palleteIndex = this.palleteMap[blockState]
                        }

                        visible = blockState && (hideFace(i-1, j, k, localBlocks, blockState, getBlock, "west", "east")
                        | hideFace(i+1, j, k, localBlocks, blockState, getBlock, "east", "west") << 1
                        | hideFace(i, j-1, k, localBlocks, blockState, getBlock, "bottom", "top") << 2
                        | hideFace(i, j+1, k, localBlocks, blockState, getBlock, "top", "bottom") << 3
                        | hideFace(i, j, k-1, localBlocks, blockState, getBlock, "south", "north") << 4
                        | hideFace(i, j, k+1, localBlocks, blockState, getBlock, "north", "south") << 5)
                        if (visible) {
                            pos = (i | j << 4 | k << 8) << 19
                            this.renderData[this.renderLength++] = 1 << 31 | pos | visible << 13 | palleteIndex
                            this.hasVisibleBlocks = true
                        }
                    }
                }
            }
        }
        updateBlock(x, y, z, world) {
            if (!world.meshQueue.includes(this.chunk)) {
                world.meshQueue.push(this.chunk)
            }
            let i = x
            let j = y
            let k = z
            let s = this.size
            x += this.x
            y += this.y
            z += this.z
            let blockState = this.blocks[i * s * s + j * s + k]
            let visible = blockState && (hideFace(x-1, y, z, 0, blockState, world.getBlock, "west", "east")
            | hideFace(x+1, y, z, 0, blockState, world.getBlock, "east", "west") << 1
            | hideFace(x, y-1, z, 0, blockState, world.getBlock, "bottom", "top") << 2
            | hideFace(x, y+1, z, 0, blockState, world.getBlock, "top", "bottom") << 3
            | hideFace(x, y, z-1, 0, blockState, world.getBlock, "south", "north") << 4
            | hideFace(x, y, z+1, 0, blockState, world.getBlock, "north", "south") << 5)
            let pos = (i | j << 4 | k << 8) << 19
            let index = -1

            // Find index of current block in this.renderData
            for (let i = 0; i < this.renderLength; i++) {
                if ((this.renderData[i] & 0x7ff80000) === pos) {
                    index = i
                    break
                }
            }

            // Update pallete
            if (this.palleteMap[blockState] === undefined) {
                this.palleteMap[blockState] = this.pallete.length
                this.pallete.push(blockState)
            }

            if (index < 0 && !visible) {
                // Wasn't visible before, isn't visible after.
                return
            }
            if (!visible) {
                // Was visible before, isn't visible after.
                this.renderData.splice(index, 1)
                this.renderLength--
                this.hasVisibleBlocks = !!this.renderLength
                return
            }
            if (visible && index < 0) {
                // Wasn't visible before, is visible after.
                index = this.renderLength++
                this.hasVisibleBlocks = true
            }
            this.renderData[index] = 1 << 31 | pos | visible << 13 | this.palleteMap[blockState]
        }
        genMesh(barray, index) {
            if (!this.renderLength) {
                return index
            }
            let length = this.renderLength
            let rData = this.renderData
            let x = 0, y = 0, z = 0, loc = 0, data = 0, sides = 0, tex = null, x2 = 0, y2 = 0, z2 = 0, verts = null, texVerts = null, texShapeVerts = null, tx = 0, ty = 0
            let wx = this.x, wy = this.y, wz = this.z
            let blocks = world.getAdjacentSubchunks(wx, wy, wz)
            let block = null

            let shadows = null
            let blockSides = Object.keys(Block)
            let side = ""
            let shapeVerts = null
            let shapeTexVerts = null
            let pallete = this.pallete
            let intShad = interpolateShadows

            for (let i = 0; i < length; i++) {
                data = rData[i]
                block = blockData[pallete[data & 0x1fff]]
                tex = block.textures
                sides = data >> 13 & 0x3f
                loc = data >> 19 & 0xfff
                x = loc & 15
                y = loc >> 4 & 15
                z = loc >> 8 & 15

                x2 = x + this.x
                y2 = y + this.y
                z2 = z + this.z

                shapeVerts = block.shape.verts
                shapeTexVerts = block.shape.texVerts

                let texNum = 0
                for (let n = 0; n < 6; n++) {
                    side = blockSides[n]
                    if (sides & Block[side]) {
                        shadows = getShadows[side](x, y, z, blocks)
                        let directionalFaces = shapeVerts[Sides[side]]
                        // if (directionalFaces.length > 1) {
                        //  let average = (shadows[0] + shadows[1] + shadows[2] + shadows[3]) / 4
                        //  shadows[0] = average
                        //  shadows[1] = average
                        //  shadows[2] = average
                        //  shadows[3] = average
                        // }
                        for (let facei = 0; facei < directionalFaces.length; facei++) {
                            verts = directionalFaces[facei]
                            texVerts = textureCoords[textureMap[tex[texNum]]]
                            tx = texVerts[0]
                            ty = texVerts[1]
                            texShapeVerts = shapeTexVerts[n][facei]

                            barray[index] = verts[0] + x2
                            barray[index+1] = verts[1] + y2
                            barray[index+2] = verts[2] + z2
                            barray[index+3] = tx + texShapeVerts[0]
                            barray[index+4] = ty + texShapeVerts[1]
                            barray[index+5] = shadows[0]

                            barray[index+6] = verts[3] + x2
                            barray[index+7] = verts[4] + y2
                            barray[index+8] = verts[5] + z2
                            barray[index+9] = tx + texShapeVerts[2]
                            barray[index+10] = ty + texShapeVerts[3]
                            barray[index+11] = shadows[1]

                            barray[index+12] = verts[6] + x2
                            barray[index+13] = verts[7] + y2
                            barray[index+14] = verts[8] + z2
                            barray[index+15] = tx + texShapeVerts[4]
                            barray[index+16] = ty + texShapeVerts[5]
                            barray[index+17] = shadows[2]

                            barray[index+18] = verts[9] + x2
                            barray[index+19] = verts[10] + y2
                            barray[index+20] = verts[11] + z2
                            barray[index+21] = tx + texShapeVerts[6]
                            barray[index+22] = ty + texShapeVerts[7]
                            barray[index+23] = shadows[3]
                            index += 24
                        }
                    }
                    texNum++
                }
            }
            return index
        }
        carveCaves() {
            let wx = this.x + 16, wz = this.z + 16, wy = this.y + 16
            for (let x = this.x, xx = 0; x < wx; x++, xx++) {
                for (let z = this.z, zz = 0; z < wz; z++, zz++) {
                    wy = this.chunk.tops[zz * 16 + xx]
                    for (let y = this.y; y < wy; y++) {
                        if (isCave(x, y, z)) {
                            carveSphere(x, y, z)
                        }
                    }
                }
            }
            this.caves = true
        }
        tick() {
            for (let i = 0; i < 3; i++) {
                let rnd = Math.random() * this.blocks.length | 0
                if ((this.blocks[rnd]) === blockIds.grass) {
                    // Spread grass

                    let x = (rnd >> 8) + this.x
                    let y = (rnd >> 4 & 15) + this.y
                    let z = (rnd & 15) + this.z
                    if (!blockData[world.getBlock(x, y + 1, z)].transparent) {
                        world.setBlock(x, y, z, blockIds.dirt, false)
                        return
                    }

                    let rnd2 = Math.random() * 27 | 0
                    let x2 = rnd2 % 3 - 1
                    rnd2 = (rnd2 - x2 - 1) / 3
                    let y2 = rnd2 % 3 - 1
                    rnd2 = (rnd2 - y2 - 1) / 3
                    z += rnd2 - 1
                    x += x2
                    y += y2

                    if (world.getBlock(x, y, z) === blockIds.dirt && world.getBlock(x, y + 1, z) === blockIds.air) {
                        world.setBlock(x, y, z, blockIds.grass, false)
                    }

                }
            }
        }
    }
    let emptySection = new Section(0, 0, 0, 16)
    let fullSection = new Section(0, 0, 0, 16)
    fullSection.blocks.fill(blockIds.bedrock)

    class Chunk {
        constructor(x, z) {
            this.x = x
            this.z = z
            this.maxY = 0
            this.minY = 255
            this.sections = []
            this.cleanSections = []
            this.tops = new Uint8Array(16 * 16); // Store the heighest block at every (x,z) coordinate
            this.optimized = false
            this.generated = false; // Terrain
            this.populated = superflat; // Trees and ores
            this.lazy = false
            this.edited = false
            this.loaded = false
            this.caves = !caves
        }
        getBlock(x, y, z) {
            let s = y >> 4
            return this.sections.length > s ? this.sections[s].getBlock(x, y & 15, z) : 0
        }
        setBlock(x, y, z, blockID, hidden, user) {
            if (!this.sections[y >> 4]) {
                do {
                    this.sections.push(new Section(this.x, this.sections.length * 16, this.z, 16, this))
                } while (!this.sections[y >> 4])
            }
            if (user && !this.sections[y >> 4].edited) {
                this.cleanSections[y >> 4] = this.sections[y >> 4].blocks.slice()
                this.sections[y >> 4].edited = true
                this.edited = true
            }
            this.sections[y >> 4].setBlock(x, y & 15, z, blockID, hidden)
        }
        optimize() {
            for (let i = 0; i < this.sections.length; i++) {
                this.sections[i].optimize()
            }
            if (!world.meshQueue.includes(this)) {
                world.meshQueue.push(this)
            }
            this.optimized = true
        }
        render() {
            if (!this.buffer) {
                return
            }
            if (p.canSee(this.x, this.minY, this.z, this.maxY)) {
                renderedChunks++
                gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer)
                gl.vertexAttribPointer(glCache.aVertex, 3, gl.FLOAT, false, 24, 0)
                gl.vertexAttribPointer(glCache.aTexture, 2, gl.FLOAT, false, 24, 12)
                gl.vertexAttribPointer(glCache.aShadow, 1, gl.FLOAT, false, 24, 20)
                gl.drawElements(gl.TRIANGLES, 6 * this.faces, gl.UNSIGNED_INT, 0)
            }
        }
        updateBlock(x, y, z, world, lazy) {
            if (this.buffer) {
                this.lazy = lazy
                if (this.sections.length > y >> 4) {
                    this.sections[y >> 4].updateBlock(x, y & 15, z, world)
                }
            }
        }
        deleteBlock(x, y, z, user) {
            if (!this.sections[y >> 4]) {
                return
            }
            if (user && !this.sections[y >> 4].edited) {
                this.cleanSections[y >> 4] = this.sections[y >> 4].blocks.slice()
                this.sections[y >> 4].edited = true
                this.edited = true
            }
            this.sections[y >> 4].deleteBlock(x, y & 15, z)
            this.minY = y < this.minY ? y : this.minY
            this.maxY = y > this.maxY ? y : this.maxY
        }
        carveCaves() {
            for (let i = 0; i < this.sections.length; i++) {
                if (!this.sections[i].caves) {
                    this.sections[i].carveCaves()
                    if (i + 1 >= this.sections.length) {
                        this.caves = true
                    }
                    return
                }
            }
        }
populate() {
            randomSeed(hash(this.x, this.z) * 210000000)
            let wx = 0, wz = 0, ground = 0, top = 0, rand = 0, place = false
            let biomeSmooth = generator.biomeSmooth;
            let biome = 0;
            let gen = 0;
            let trueX = this.x;
            let trueZ = this.z;
            let smoothness = generator.smooth
            let hilliness = generator.hilliness
            var items = [blockIds.jungleLog, blockIds.darkOakLog, blockIds.acaciaLog,blockIds.birchLog]
            var randomtree = items[Math.floor(Math.random() * items.length)];
            for (let i = 0; i < 16; i++) {
                for (let k = 0; k < 16; k++) {
                    wx = this.x + i
                    wz = this.z + k
                    ground = this.tops[k * 16 + i]
                    gen = superflat ? 4 : Math.round(noise((trueX + i) * smoothness, (trueZ + k) * smoothness) * hilliness) + generator.extra
                    biome = superflat ? 0 : Math.round(noise((trueX + i) * biomeSmooth, (trueZ + k) * biomeSmooth));
if (trees && random() < 0.005 && this.getBlock(i, ground, k) === blockIds.grass) {

                        top = ground + Math.floor(4.5 + random(2.5))
                        rand = Math.floor(random(4096))
                        let tree = random() < 0.6 ? blockIds.oakLog : ++top && blockIds.birchLog

                        //Center
                        for (let j = ground + 1; j <= top; j++) {
                            this.setBlock(i, j, k, tree)
                        }
                        this.setBlock(i, top + 1, k, blockIds.leaves)
                        this.setBlock(i, ground, k, blockIds.dirt)

                        //Bottom leaves
                        for (let x = -2; x <= 2; x++) {
                            for (let z = -2; z <= 2; z++) {
                                if (x || z) {
                                    if ((x * z & 7) === 4) {
                                        place = rand & 1
                                        rand >>>= 1
                                        if (place) {
                                            world.spawnBlock(wx + x, top - 2, wz + z, blockIds.leaves)
                                        }
                                    } else {
                                        world.spawnBlock(wx + x, top - 2, wz + z, blockIds.leaves)
                                    }
                                }
                            }
                        }

                        //2nd layer leaves
                        for (let x = -2; x <= 2; x++) {
                            for (let z = -2; z <= 2; z++) {
                                if (x || z) {
                                    if ((x * z & 7) === 4) {
                                        place = rand & 1
                                        rand >>>= 1
                                        if (place) {
                                            world.spawnBlock(wx + x, top - 1, wz + z, blockIds.leaves)
                                        }
                                    } else {
                                        world.spawnBlock(wx + x, top - 1, wz + z, blockIds.leaves)
                                    }
                                }
                            }
                        }

                        //3rd layer leaves
                        for (let x = -1; x <= 1; x++) {
                            for (let z = -1; z <= 1; z++) {
                                if (x || z) {
                                    if (x & z) {
                                        place = rand & 1
                                        rand >>>= 1
                                        if (place) {
                                            world.spawnBlock(wx + x, top, wz + z, blockIds.leaves)
                                        }
                                    } else {
                                        world.spawnBlock(wx + x, top, wz + z, blockIds.leaves)
                                    }
                                }
                            }
                        }

                        //Top leaves
                        world.spawnBlock(wx + 1, top + 1, wz, blockIds.leaves)
                        world.spawnBlock(wx, top + 1, wz - 1, blockIds.leaves)
                        world.spawnBlock(wx, top + 1, wz + 1, blockIds.leaves)
                        world.spawnBlock(wx - 1, top + 1, wz, blockIds.leaves)
                    }
    if (random() < 0.01 && this.getBlock(i, ground, k) && biome > 0.5 && ground > 60) {
                    top = ground + Math.floor(2.5 + random(1.5));
                    rand = Math.floor(random(4096));
                    let tree = blockIds.cactus;

                    //Center
                    for (let j = ground + 1; j <= top; j++) {
                        this.setBlock(i, j, k, tree);
                    }
                    this.setBlock(i, ground, k, blockIds.sand);
                } // cactus

                    // Blocks of each per chunk in Minecraft
                    // Coal: 185.5
                    // Iron: 111.5
                    // Gold: 10.4
                    // Redstone: 29.1
                    // Diamond: 3.7
                    // Lapis: 4.1
                ground -= 4;

                if (random() < 3.7 / 256) {
                    let y = random() * 16 | 0 + 1;
                    y = y < ground ? y : ground;
                    if (this.getBlock(i, y, k)) {
                        this.setBlock(i, y < ground ? y : ground, k, blockIds.diamondOre);
                    }
                }

                if (random() < 111.5 / 256) {
                    let y = random() * 64 | 0 + 1;
                    y = y < ground ? y : ground;
                    if (this.getBlock(i, y, k)) {
                        this.setBlock(i, y < ground ? y : ground, k, blockIds.ironOre);
                    }
                }

                if (random() < 185.5 / 256) {
                    let y = random() * ground | 0 + 1;
                    y = y < ground ? y : ground;
                    if (this.getBlock(i, y, k)) {
                        this.setBlock(i, y < ground ? y : ground, k, blockIds.coalOre);
                    }
                }

                if (random() < 10.4 / 256) {
                    let y = random() * 32 | 0 + 1;
                    y = y < ground ? y : ground;
                    if (this.getBlock(i, y, k)) {
                        this.setBlock(i, y < ground ? y : ground, k, blockIds.goldOre);
                    }
                }

                if (random() < 29.1 / 256) {
                    let y = random() * 16 | 0 + 1;
                    y = y < ground ? y : ground;
                    if (this.getBlock(i, y, k)) {
                        this.setBlock(i, y < ground ? y : ground, k, blockIds.redstoneOre);
                    }
                }

                if (random() < 4.1 / 256) {
                    let y = random() * 32 | 0 + 1;
                    y = y < ground ? y : ground;
                    if (this.getBlock(i, y, k)) {
                        this.setBlock(i, y < ground ? y : ground, k, blockIds.lapisOre);
                    }
                }

                if(random() < 0.1/300) {
                    let y = 40;
                    let cobvsmoss = random() < 0.01 ? blockIds.mossyCobble : blockIds.mossyCobble;
                    for(let xxx = 3;xxx<10;xxx++) {
                        for(let zzz = 3;zzz<10;zzz++) {
                                this.setBlock(xxx, y+3, zzz, cobvsmoss); 
                        this.setBlock(xxx, y-3, zzz, cobvsmoss);          
                        for(let hhh = y-3;hhh<y+3;hhh++) {
                            this.setBlock(xxx,hhh, 3, cobvsmoss);
                            this.setBlock(xxx,hhh, 9, cobvsmoss);
                            this.setBlock(3, hhh,zzz, cobvsmoss);
                            this.setBlock(9, hhh,zzz, cobvsmoss);

                        }

                        }
                    }
                    for(let xxx = 4;xxx<9;xxx++) {
                        for(let zzz = 4;zzz<9;zzz++){
                            for(let yyy = y-2;yyy<y+3;yyy++){
                                this.setBlock(xxx,yyy,zzz, blockIds.air, true)
                            }
                        }
                    }
                    this.setBlock(6, y-2, 6, blockIds.coalBlock);
                }
            }
        }

        this.populated = true;
        }
        genMesh() {
            let start = win.performance.now()
            let barray = bigArray
            let index = 0
            for (let i = 0; i < this.sections.length; i++) {
                index = this.sections[i].genMesh(barray, index)
            }
            let arrayDone = win.performance.now()

            if (!this.buffer) {
                this.buffer = gl.createBuffer()
            }
            let data = barray.slice(0, index)

            let maxY = 0
            let minY = 255
            let y = 0
            for (let i = 1; i < data.length; i += 6) {
                y = data[i]
                maxY = Math.max(maxY, y)
                minY = Math.min(minY, y)
            }
            this.maxY = maxY
            this.minY = minY
            this.faces = data.length / 24
            gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer)
            gl.bufferData(gl.ARRAY_BUFFER, data, gl.DYNAMIC_DRAW)
            this.lazy = false
        }
        tick() {
            if (this.edited) {
                for (let i = 0; i < this.sections.length; i++) {
                    if (this.sections[i].edited) {
                        this.sections[i].tick()
                    }
                }
            }
        }
        load() {
            let chunkX = this.x >> 4
            let chunkZ = this.z >> 4
            let load = null

            for (let i = 0; i < world.loadFrom.length; i++) {
                load = world.loadFrom[i]
                if (load.x === chunkX && load.z === chunkZ) {
                    let y = load.y * 16
                    for (let j in load.blocks) {
                        world.setBlock((j >> 8 & 15) + this.x, (j >> 4 & 15) + y, (j & 15) + this.z, load.blocks[j])
                    }
                    world.loadFrom.splice(i--, 1)
                }
            }
            this.loaded = true
        }
    }

    let analytics = {
        totalTickTime: 0,
        worstFrameTime: 0,
        totalRenderTime: 0,
        totalFrameTime: 0,
        lastUpdate: 0,
        frames: 1,
        displayedTickTime: "0",
        displayedRenderTime: "0",
        displayedFrameTime: "0",
        displayedwFrameTime: 0,
        fps: 0,
    }
    function chunkDist(c) {
        let dx = p.x - c.x
        let dz = p.z - c.z
        if (dx > 16) {
            dx -= 16
        } else if (dx > 0) {
            dx = 0
        }
        if (dz > 16) {
            dz -= 16
        } else if (dz > 0) {
            dz = 0
        }
        return Math.sqrt(dx * dx + dz * dz)
    }
    function sortChunks(c1, c2) { //Sort the list of chunks based on distance from the player
        let dx1 = p.x - c1.x - 8
        let dy1 = p.z - c1.z - 8
        let dx2 = p.x - c2.x - 8
        let dy2 = p.z - c2.z - 8
        return dx1 * dx1 + dy1 * dy1 - (dx2 * dx2 + dy2 * dy2)
    }
    function fillReqs(x, z) {
        // Chunks must all be loaded first.
        var done = true
        for (let i = x - 2; i <= x + 2; i++) {
            for (let j = z - 2; j <= z + 2; j++) {
                let chunk = world.loaded[(i + world.offsetX) * world.lwidth + j + world.offsetZ]
                if (!chunk.generated) {
                    world.generateQueue.push(chunk)
                    done = false
                }
                if (!chunk.populated && i >= x - 1 && i <= x + 1 && j >= z - 1 && j <= z + 1) {
                    world.populateQueue.push(chunk)
                    done = false
                }
            }
        }
        return done
    }
    function maxDist(x, z, x2, z2) {
        let ax = Math.abs(x2 - x)
        let az = Math.abs(z2 - z)
        return Math.max(ax, az)
    }
    function renderFilter(chunk) {
        return maxDist(chunk.x >> 4, chunk.z >> 4, p.cx, p.cz) <= settings.renderDistance
    }

    function debug(message) {
        let ellapsed = performance.now() - debug.start
        if (ellapsed > 30) {
            console.log(message, ellapsed.toFixed(2), "milliseconds")
        }
    }

    let fogDist = 16
    class World {
        constructor() {
            generatedChunks = 0
            fogDist = 16
            p.y = superflat ? 6 : (Math.round(noise(8 * generator.smooth, 8 * generator.smooth) * generator.height) + 2 + generator.extra)

            //Initialize the world's arrays
            this.chunks = []
            this.loaded = []
            this.sortedChunks = []
            this.offsetX = 0
            this.offsetZ = 0
            this.lwidth = 0
            this.chunkGenQueue = []
            this.populateQueue = []
            this.generateQueue = []
            this.meshQueue = []
            this.loadFrom = []
            this.lastChunk = ","
        }
genChunk(chunk) {
        let x = chunk.x >> 4
        let z = chunk.z >> 4
        let trueX = chunk.x
        let trueZ = chunk.z

        if (chunk.generated) {
            return false
        }
        let hide = !loadString
        let smoothness = generator.smooth
        let hilliness = generator.height
        let biomeSmooth = generator.biomeSmooth;
        let gen = 0;
        let biome = 0;
        for (let i = 0; i < 16; i++) {
            for (let k = 0; k < 16; k++) {
                if(gen<120 && !superflat) {
                gen = 100;
                chunk.setBlock(i, gen, k, blockIds.air);

            }
                biome = superflat ? 0 : Math.round(noise((trueX + i) * biomeSmooth, (trueZ + k) * biomeSmooth));
                gen = superflat ? 4 : Math.round(noise((trueX + i) * smoothness, (trueZ + k) * smoothness) * hilliness) + generator.extra
                chunk.tops[k * 16 + i] = gen

            if(biome > 0.25 && !superflat){

            chunk.tops[k * 16 + i] = gen;

            chunk.setBlock(i, gen, k, blockIds.sand);
            chunk.setBlock(i, gen - 1, k, blockIds.sand);
            chunk.setBlock(i, gen - 2, k, blockIds.sand);
            chunk.setBlock(i, gen - 3, k, blockIds.sand);
                            if(gen<60 && !superflat) {
                    gen = 59;
                    chunk.setBlock(i, gen, k, blockIds.Water);
                    chunk.setBlock(i, gen - 1, k, blockIds.Water);
                    chunk.setBlock(i, gen - 2, k, blockIds.gravel);
                    chunk.setBlock(i, gen - 3, k, blockIds.gravel);
                }
            if(gen>120){
                chunk.setBlock(i, gen, k, blockIds.stone);
            }
            if(gen>140){
                chunk.setBlock(i, gen, k, blockIds.sand);
            }
        }

            if(biome < 0.5){
            chunk.tops[k * 16 + i] = gen;

            chunk.setBlock(i, gen, k, blockIds.grass);
            chunk.setBlock(i, gen - 1, k, blockIds.dirt);
            chunk.setBlock(i, gen - 2, k, blockIds.dirt);
            chunk.setBlock(i, gen - 3, k, blockIds.dirt);
                if(gen<60 && !superflat) {
                    gen = 59;

                    chunk.setBlock(i, gen, k, blockIds.Water);
                    chunk.setBlock(i, gen - 1, k, blockIds.Water);
                    chunk.setBlock(i, gen - 2, k, blockIds.gravel);
                    chunk.setBlock(i, gen - 3, k, blockIds.gravel);
                    if (world.getBlock(i, gen, k) === blockIds.Water && world.getBlock(i, gen + 1, k) === blockIds.air) {
                        world.setBlock(i, gen, k, blockIds.Water, false)
                    }
                }
            }

                for (let j = 1; j < gen - 3; j++) {
                    chunk.setBlock(i, j, k, blockIds.stone)
                }
                chunk.setBlock(i, 0, k, blockIds.bedrock)
            }
        }
        chunk.generated = true
    }
        getAdjacentSubchunks(x, y, z) {
            let minChunkX = x - 16 >> 4
            let maxChunkX = x + 16 >> 4
            let minChunkY = y - 16 >> 4
            let maxChunkY = y + 16 >> 4
            let minChunkZ = z - 16 >> 4
            let maxChunkZ = z + 16 >> 4
            let section = null
            let ret = []
            for (x = minChunkX; x <= maxChunkX; x++) {
                for (let y = minChunkY; y <= maxChunkY; y++) {
                    for (z = minChunkZ; z <= maxChunkZ; z++) {
                        if (y < 0) {
                            ret.push(fullSection.blocks)
                        } else if (this.chunks[x] && this.chunks[x][z]) {
                            section = this.chunks[x][z].sections[y] || emptySection
                            ret.push(section.blocks)
                        } else {
                            ret.push(emptySection.blocks)
                        }
                    }
                }
            }
            return ret
        }
        updateBlock(x, y, z, lazy) {
            let chunk = this.chunks[x >> 4] && this.chunks[x >> 4][z >> 4]
            if (chunk && chunk.buffer) {
                chunk.updateBlock(x & 15, y, z & 15, this, lazy)
            }
        }
        getWorldBlock(x, y, z) {
            if (!this.chunks[x >> 4] || !this.chunks[x >> 4][z >> 4]) {
                return blockIds.air
            }
            return this.chunks[x >> 4][z >> 4].getBlock(x & 15, y, z & 15)
        }
        getBlock(x, y, z) {
            let X = (x >> 4) + this.offsetX
            let Z = (z >> 4) + this.offsetZ
            if (y > maxHeight) {
                return blockIds.air
            } else if (y < 0) {
                return blockIds.bedrock
            } else if (X < 0 || X >= this.lwidth || Z < 0 || Z >= this.lwidth) {
                return this.getWorldBlock(x, y, z)
            }
            return this.loaded[X * this.lwidth + Z].getBlock(x & 15, y, z & 15)
        }
        setBlock(x, y, z, blockID, lazy) {
            if (!this.chunks[x >> 4] || !this.chunks[x >> 4][z >> 4]) {
                return
            }
            let chunk = this.chunks[x >> 4][z >> 4]

            let xm = x & 15
            let zm = z & 15
            if (blockID) {
                chunk.setBlock(xm, y, zm, blockID, false, !lazy)
            } else {
                chunk.deleteBlock(xm, y, zm, !lazy)
            }

            if (lazy) {
                return
            }

            //Update the 6 adjacent blocks and 1 changed block
            if (xm && xm !== 15 && zm && zm !== 15) {
                chunk.updateBlock(xm - 1, y, zm, this, lazy)
                chunk.updateBlock(xm + 1, y, zm, this, lazy)
                chunk.updateBlock(xm, y - 1, zm, this, lazy)
                chunk.updateBlock(xm, y + 1, zm, this, lazy)
                chunk.updateBlock(xm, y, zm - 1, this, lazy)
                chunk.updateBlock(xm, y, zm + 1, this, lazy)
            }
            else {
                this.updateBlock(x - 1, y, z, lazy)
                this.updateBlock(x + 1, y, z, lazy)
                this.updateBlock(x, y - 1, z, lazy)
                this.updateBlock(x, y + 1, z, lazy)
                this.updateBlock(x, y, z - 1, lazy)
                this.updateBlock(x, y, z + 1, lazy)
            }

            chunk.updateBlock(xm, y, zm, this, lazy)

            // Update the corner chunks so shadows in adjacent chunks update correctly
            if (xm | zm === 0) { this.updateBlock(x - 1, y, z - 1, lazy); }
            if (xm === 15 && zm === 0) { this.updateBlock(x + 1, y, z - 1, lazy); }
            if (xm === 0 && zm === 15) { this.updateBlock(x - 1, y, z + 1, lazy); }
            if (xm & zm === 15) { this.updateBlock(x + 1, y, z + 1, lazy); }
        }
        spawnBlock(x, y, z, blockID) {
            //Sets a block anywhere without causing block updates around it. Only to be used in world gen.

            let chunkX = x >> 4
            let chunkZ = z >> 4
            if (!this.chunks[chunkX]) {
                this.chunks[chunkX] = []
            }
            let chunk = this.chunks[chunkX][chunkZ]
            if (!chunk) {
                chunk = new Chunk(chunkX * 16, chunkZ * 16)
                this.chunks[chunkX][chunkZ] = chunk
            }
            if (chunk.buffer) {
                //Only used if spawning a block post-gen
                this.setBlock(x, y, z, blockID, true)
            } else if (!chunk.getBlock(x & 15, y, z & 15)) {
                chunk.setBlock(x & 15, y, z & 15, blockID, false)
            }
        }
        tick() {
            let tickStart = win.performance.now()
            let maxChunkX = (p.x >> 4) + settings.renderDistance
            let maxChunkZ = (p.z >> 4) + settings.renderDistance
            let chunk = maxChunkX + "," + maxChunkZ
            if (chunk !== this.lastChunk) {
                this.lastChunk = chunk
                this.loadChunks()
                this.chunkGenQueue.sort(sortChunks)
            }

            if (Key.leftMouse && !Key.control && p.lastBreak < Date.now() - 250 && screen === "play") {
                changeWorldBlock(0)
            }
            if ((Key.rightMouse || Key.leftMouse && Key.control) && p.lastPlace < Date.now() - 250) {
                newWorldBlock()
            }
            if (Key.leftMouse && p.autoBreak && !Key.control) {
                changeWorldBlock(0)
            }

            for (let i = 0; i < this.sortedChunks.length; i++) {
                this.sortedChunks[i].tick()
            }

            do {
                let doneWork = false
                debug.start = performance.now()
                if (this.meshQueue.length) {
                    // Update all chunk meshes.
                    let len = this.meshQueue.length - 1
                    do {
                        this.meshQueue.pop().genMesh()
                    } while(this.meshQueue.length)
                    doneWork = true
                    debug("Meshes")
                }

                if (this.generateQueue.length && !doneWork) {
                    let chunk = this.generateQueue.pop()
                    this.genChunk(chunk)
                    doneWork = true
                }
                if (this.populateQueue.length && !doneWork) {
                    let chunk = this.populateQueue[this.populateQueue.length - 1]
                    if (!chunk.caves) {
                        chunk.carveCaves()
                        debug("Carve caves")
                    } else if (!chunk.populated) {
                        chunk.populate()
                        this.populateQueue.pop()
                    }
                    doneWork = true
                }

                if (this.chunkGenQueue.length && !doneWork) {
                    let chunk = this.chunkGenQueue[0]
                    if (!fillReqs(chunk.x >> 4, chunk.z >> 4)) {}
                    else if (!chunk.loaded) {
                        chunk.load()
                    } else if (!chunk.optimized) {
                        chunk.optimize(this)
                        debug("Optimize")
                    } else if (!chunk.buffer) {
                        chunk.genMesh()
                        debug("Initial mesh")
                    } else {
                        this.chunkGenQueue.shift()
                        generatedChunks++
                    }
                    doneWork = true
                }
                if (!doneWork) {
                    break
                }
            } while(win.performance.now() - tickStart < 5)
        }
        render() {
            initModelView(p)
            gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT)

            p2.x = Math.round(p.x)
            p2.y = Math.round(p.y)
            p2.z = Math.round(p.z)

            renderedChunks = 0

            let dist = (settings.renderDistance) * 16
            if (this.chunkGenQueue.length) {
                this.chunkGenQueue.sort(sortChunks)
                let chunk = this.chunkGenQueue[0]
                dist = Math.min(dist, chunkDist(chunk))
            }
            if (dist !== fogDist) {
                if (fogDist < dist - 0.1) fogDist += (dist - fogDist) / 120
                else if (fogDist > dist + 0.1) fogDist += (dist - fogDist) / 30
                else fogDist = dist
            }
            gl.uniform3f(glCache.uPos, p.x, p.y, p.z)
            gl.uniform1f(glCache.uDist, fogDist)

            let c = this.sortedChunks
            for (let chunk of c) {
                chunk.render()
            }

            gl.uniform3f(glCache.uPos, 0, 0, 0)

            if(hitBox.pos) {
                blockOutlines = true
                blockFill = false
                block2(hitBox.pos[0], hitBox.pos[1], hitBox.pos[2], 0, p)
                blockOutlines = false
                blockFill = true
            }
        }
        loadChunks() {
            let renderDistance = settings.renderDistance + 2
            let cx = p.x >> 4
            let cz = p.z >> 4
            p.cx = cx
            p.cz = cz
            let minChunkX = cx - renderDistance
            let maxChunkX = cx + renderDistance
            let minChunkZ = cz - renderDistance
            let maxChunkZ = cz + renderDistance

            this.offsetX = -minChunkX
            this.offsetZ = -minChunkZ
            this.lwidth = renderDistance * 2 + 1
            this.chunkGenQueue.length = 0

            if (this.loaded.length > this.lwidth * this.lwidth) {
                this.loaded.length = this.lwidth * this.lwidth
            }

            let i = 0
            for (let x = minChunkX; x <= maxChunkX; x++) {
                for (let z = minChunkZ; z <= maxChunkZ; z++) {
                    let chunk
                    if (!this.chunks[x]) {
                        this.chunks[x] = []
                    }
                    if (!this.chunks[x][z]) {
                        chunk = new Chunk(x * 16, z * 16)
                        if (maxDist(cx, cz, x, z) <= settings.renderDistance) {
                            this.chunkGenQueue.push(chunk)
                        }
                        this.chunks[x][z] = chunk
                    }
                    chunk = this.chunks[x][z]
                    if (!chunk.buffer && !this.chunkGenQueue.includes(chunk) && maxDist(cx, cz, x, z) <= settings.renderDistance) {
                        this.chunkGenQueue.push(chunk)
                    }
                    this.loaded[i++] = chunk
                }
            }
            this.sortedChunks = this.loaded.filter(renderFilter)
            this.sortedChunks.sort(sortChunks)
        }
        getSaveString() {
            let edited = []
            for (let x in this.chunks) {
                for (let z in this.chunks[x]) {
                    let chunk = this.chunks[x][z]
                    if (chunk.edited) {
                        for (let y = 0; y < chunk.sections.length; y++) {
                            if (chunk.sections[y].edited) {
                                edited.push([ chunk.sections[y], chunk.cleanSections[y] ])
                            }
                        }
                    }
                }
            }

            let pallete = {}
            for (let chunks of edited) {
                let changes = false
                chunks[0].blocks.forEach((id, i) => {
                    if (id !== chunks[1][i]) {
                        pallete[id] = true
                        changes = true
                    }
                })
                if (!changes) {
                    chunks[0].edited = false
                }
            }

            let blocks = Object.keys(pallete).map(n => Number(n))
            pallete = {}
            blocks.forEach((block, index) => pallete[block] = index)

            let rnd = Math.round
            let options = p.flying | superflat << 1 | p.spectator << 2 | caves << 3 | trees << 4

            let str = world.name + ";" + worldSeed.toString(36) + ";"
                + rnd(p.x).toString(36) + "," + rnd(p.y).toString(36) + "," + rnd(p.z).toString(36) + ","
                + (p.rx * 100 | 0).toString(36) + "," + (p.ry * 100 | 0).toString(36) + "," + options.toString(36) + ";"
                + version + ";"
                + blocks.map(b => b.toString(36)).join(",") + ";"

            for (let i = 0; i < edited.length; i++) {
                if (!edited[i][0].edited) {
                    continue
                }
                let real = edited[i][0]
                let blocks = real.blocks
                let original = edited[i][1]
                str += (real.x / 16).toString(36) + "," + (real.y / 16).toString(36) + "," + (real.z / 16).toString(36) + ","
                for (let j = 0; j < original.length; j++) {
                    if (blocks[j] !== original[j]) {
                        str += (pallete[blocks[j]] << 12 | j).toString(36) + ","
                    }
                }
                str = str.substr(0, str.length - 1); //Remove trailing comma
                str += ";"
            }
            if (str.match(/;$/)) str = str.substr(0, str.length - 1)
            return str
        }
        loadSave(str) {
            let data = str.split(";")
            if (!str.includes("Alpha")) {
                return this.loadOldSave(str)
            }

            this.name = data.shift()
            worldSeed = parseInt(data.shift(), 36)
            seedHash(worldSeed)
            caveNoise = openSimplexNoise(worldSeed)
            noiseSeed(worldSeed)

            let playerData = data.shift().split(",")
            p.x = parseInt(playerData[0], 36)
            p.y = parseInt(playerData[1], 36)
            p.z = parseInt(playerData[2], 36)
            p.rx = parseInt(playerData[3], 36) / 100
            p.ry = parseInt(playerData[4], 36) / 100
            let options = parseInt(playerData[5], 36)
            p.flying = options & 1
            p.spectator = options >> 2 & 1
            superflat = options >> 1 & 1
            caves = options >> 3 & 1
            trees = options >> 4 & 1

            let version = data.shift()
            this.version = version

            // if (version.split(" ")[1].split(".").join("") < 70) {
            //  alert("This save code is for an older version. 0.7.0 or later is needed")
            // }

            let pallete = data.shift().split(",").map(n => parseInt(n, 36))
            this.loadFrom = []

            for (let i = 0; data.length; i++) {
                let blocks = data.shift().split(",")
                this.loadFrom.push({
                    x: parseInt(blocks.shift(), 36),
                    y: parseInt(blocks.shift(), 36),
                    z: parseInt(blocks.shift(), 36),
                    blocks: [],
                })
                for (let j = 0; j < blocks.length; j++) {
                    let block = parseInt(blocks[j], 36)
                    let index = block & 0xffffff
                    let pid = block >> 12
                    this.loadFrom[i].blocks[index] = pallete[pid]
                }
            }
        }
        loadOldSave(str) {
            let data = str.split(";");
            worldSeed = parseInt(data.shift(), 36);
            this.id = Date.now()
            this.name = "Old World " + (Math.random() * 1000 | 0)
            seedHash(worldSeed);
            caveNoise = openSimplexNoise(worldSeed);
            noiseSeed(worldSeed);
            let playerData = data.shift().split(",");
            p.x = parseInt(playerData[0], 36);
            p.y = parseInt(playerData[1], 36);
            p.z = parseInt(playerData[2], 36);
            p.rx = parseInt(playerData[3], 36) / 100;
            p.ry = parseInt(playerData[4], 36) / 100;
            let editCount = parseInt(data.shift(), 36);

            this.loadFrom = [];

            let coords = data.shift().split(",").map(function(n) {
                return parseInt(n, 36);
            });
            for (let j = 0; j < coords.length; j += 3) {
                this.loadFrom.push({
                    x: coords[j],
                    y: coords[j + 1],
                    z: coords[j + 2],
                    blocks: [],
                })
            }

            for (let i = 0; data.length > 0; i++) {
                let blocks = data.shift().split(",");
                for (let j = 0; j < blocks.length; j++) {
                    let block = parseInt(blocks[j], 36);
                    let index = block >> 8;
                    let id = block & 0x7f | (block & 0x80) << 1;
                    this.loadFrom[i].blocks[index] = id;
                }
            }
        }
    }

    let defineWorld = function() {
        let tickStart = win.performance.now()
        world.tick()
        analytics.totalTickTime += win.performance.now() - tickStart
        let renderStart = win.performance.now()
        world.render()
        analytics.totalRenderTime += win.performance.now() - renderStart
    }

    let controls = function() {
        move.x = 0
        move.z = 0
        let dt = (win.performance.now() - p.lastUpdate) / 33
        dt = dt > 2 ? 2 : dt

        if(Key.w) move.z += p.speed
        if(Key.s) move.z -= p.speed
        if(Key.a) move.x += p.speed
        if(Key.d) move.x -= p.speed
        if (p.flying) {
            if(Key[" "]) p.velocity.y += 0.06 * dt
            if(Key.shift) p.velocity.y -= 0.06 * dt
        }
        if(Key.arrowleft) p.ry -= 0.1 * dt
        if(Key.arrowright) p.ry += 0.1 * dt
        if(Key.arrowup) p.rx += 0.1 * dt
        if(Key.arrowdown) p.rx -= 0.1 * dt

        if (!p.sprinting && Key.q && !p.sneaking && Key.w) {
            p.FOV(settings.fov + 10, 250)
            p.sprinting = true
        }

        if(p.sprinting) {
            move.x *= p.sprintSpeed
            move.z *= p.sprintSpeed
        }
        if(p.flying) {
            move.x *= p.flySpeed
            move.z *= p.flySpeed
        }
        if (!move.x && !move.z) {
            if (p.sprinting) {
                p.FOV(settings.fov, 100)
            }
            p.sprinting = false
        } else if(Math.abs(move.x) > 0 && Math.abs(move.z) > 0) {
            move.x *= move.ang
            move.z *= move.ang
        }

        //Update the velocity, rather than the position.
        let co = Math.cos(p.ry)
        let si = Math.sin(p.ry)
        let friction = p.onGround ? 1 : 0.3
        p.velocity.x += (co * move.x - si * move.z) * friction * dt
        p.velocity.z += (si * move.x + co * move.z) * friction * dt

        const TAU = Math.PI * 2
        const PI1_2 = Math.PI / 2
        while(p.ry > TAU) p.ry -= TAU
        while(p.ry < 0)   p.ry += TAU
        if(p.rx > PI1_2)  p.rx = PI1_2
        if(p.rx < -PI1_2) p.rx = -PI1_2

        p.setDirection()
    }

    // Mouse sensitivity variable, used for the settings buttons and in the "mmoved" function
    let mouseS = 300

    class Slider {
        constructor(x, y, w, h, scenes, label, min, max, settingName, callback) {
            this.x = x
            this.y = y
            this.h = h
            this.w = Math.max(w, 350)
            this.name = settingName
            this.scenes = Array.isArray(scenes) ? scenes : [scenes]
            this.label = label
            this.min = min
            this.max = max
            this.sliding = false
            this.callback = callback
        }
        draw() {
            if (!this.scenes.includes(screen)) {
                return
            }
            let current = (settings[this.name] - this.min) / (this.max - this.min)

            // Outline
            ctx.beginPath()
            strokeWeight(2)
            stroke(0)
            fill(85)
            ctx.rect(this.x - this.w / 2, this.y - this.h / 2, this.w, this.h)
            ctx.stroke()
            ctx.fill()

            // Slider bar
            let value = Math.round(settings[this.name])
            ctx.beginPath()
            fill(130)
            let x = this.x - (this.w - 10) / 2 + (this.w - 10) * current - 5
            ctx.fillRect(x, this.y - this.h / 2, 10, this.h)

            //Label
            fill(255, 255, 255)
            textSize(16)
            ctx.textAlign = 'center'
            text(`${this.label}: ${value}`, this.x, this.y + this.h / 8)
        }
        click() {
            if (!mouseDown || !this.scenes.includes(screen)) {
                return false
            }

            if (mouseX > this.x - this.w / 2 && mouseX < this.x + this.w / 2 && mouseY > this.y - this.h / 2 && mouseY < this.y + this.h / 2) {
                let current = (mouseX - this.x + this.w / 2) / this.w
                if (current < 0) current = 0
                if (current > 1) current = 1
                this.sliding = true
                settings[this.name] = current * (this.max - this.min) + this.min
                this.callback(current * (this.max - this.min) + this.min)
                this.draw()
            }
        }
        drag() {
            if (!this.sliding || !this.scenes.includes(screen)) {
                return false
            }

            let current = (mouseX - this.x + this.w / 2) / this.w
            if (current < 0) current = 0
            if (current > 1) current = 1
            settings[this.name] = current * (this.max - this.min) + this.min
            this.callback(current * (this.max - this.min) + this.min)
        }
        release() {
            this.sliding = false
        }

        static draw() {
            for (let slider of Slider.all) {
                slider.draw()
            }
        }
        static click() {
            for (let slider of Slider.all) {
                slider.click()
            }
        }
        static release() {
            for (let slider of Slider.all) {
                slider.release()
            }
        }
        static drag() {
            if (mouseDown) {
                for (let slider of Slider.all) {
                    slider.drag()
                }
            }
        }
        static add(x, y, w, h, scenes, label, min, max, defaut, callback) {
            Slider.all.push(new Slider(x, y, w, h, scenes, label, min, max, defaut, callback))
        }
    }
    Slider.all = []
    class Button {
        constructor(x, y, w, h, labels, scenes, callback, disabled, hoverText) {
            this.x = x
            this.y = y
            this.h = h
            this.w = w
            this.index = 0
            this.disabled = disabled || (() => false)
            this.hoverText = !hoverText || typeof hoverText === "string" ? (() => hoverText) : hoverText
            this.scenes = Array.isArray(scenes) ? scenes : [scenes]
            this.labels = Array.isArray(labels) ? labels : [labels]
            this.callback = callback
        }

        mouseIsOver() {
            return mouseX >= this.x - this.w / 2 && mouseX <= this.x + this.w / 2 && mouseY >= this.y - this.h / 2 && mouseY <= this.y + this.h / 2
        }
        draw() {
            if (!this.scenes.includes(screen)) {
                return
            }
            let hovering = this.mouseIsOver()
            let disabled = this.disabled()
            let hoverText = this.hoverText()

            // Outline
            ctx.beginPath()
            if (hovering && !disabled) {
                strokeWeight(7)
                stroke(255)
                cursor(HAND)
            } else {
                strokeWeight(3)
                stroke(0)
            }
            if (disabled) {
                fill(60)
            } else {
                fill(120)
            }
            ctx.rect(this.x - this.w / 2, this.y - this.h / 2, this.w, this.h)
            ctx.stroke()
            ctx.fill()

            //Label
            fill(255)
            textSize(16)
            ctx.textAlign = 'center'
            text(this.labels[this.index], this.x, this.y + this.h / 8)

            if (hovering && hoverText) {
                hoverbox.innerText = hoverText
                hoverbox.classList.remove("hidden")
                if (mouseY < height / 2) {
                    hoverbox.style.bottom = ""
                    hoverbox.style.top = mouseY + 10 + "px"
                } else {
                    hoverbox.style.top = ""
                    hoverbox.style.bottom = height - mouseY + 10 + "px"
                }
                if (mouseX < width / 2) {
                    hoverbox.style.right = ""
                    hoverbox.style.left = mouseX + 10 + "px"
                } else {
                    hoverbox.style.left = ""
                    hoverbox.style.right = width - mouseX + 10 + "px"
                }
            }
        }
        click() {
            if (this.disabled() || !mouseDown || !this.scenes.includes(screen)) {
                return false
            }

            if (this.mouseIsOver()) {
                this.index = (this.index + 1) % this.labels.length
                this.callback(this.labels[this.index])
                return true
            }
        }

        static draw() {
            hoverbox.classList.add("hidden")
            for (let button of Button.all) {
                button.draw()
            }
        }
        static click() {
            for (let button of Button.all) {
                if (button.click()) {
                    Button.draw()
                    break
                }
            }
        }
        static add(x, y, w, h, labels, scenes, callback, disabled, hoverText) {
            Button.all.push(new Button(x, y, w, h, labels, scenes, callback, disabled, hoverText))
        }
    }
    Button.all = []

    var initEverything
    function initButtons() {
        Button.all = []
        Slider.all = []
        const nothing = () => false
        const always = () => true

        // Main menu buttons
        Button.add(width / 2, height / 2 - 20, 400, 40, "Singleplayer", "main menu", r => changeScene("loadsave menu"))
        Button.add(width / 2, height / 2 + 35, 400, 40, "Multiplayer", "main menu", nothing, always, "Multiplayer is possible at https://willard.fun/minekhan/")
        Button.add(width / 2, height / 2 + 90, 400, 40, "Options", "main menu", r => changeScene("options"))

        // Creation menu buttons
        Button.add(width / 2, 135, 300, 40, ["World Type: Normal", "World Type: Superflat"], "creation menu", r => superflat = r === "World Type: Superflat")
        Button.add(width / 2, 185, 300, 40, ["Trees: On", "Trees: Off"], "creation menu", r => trees = r === "Trees: On", function() {
            if (superflat) {
                this.index = 1
                trees = false
            }
            return superflat
        })
        Button.add(width / 2, 235, 300, 40, ["Caves: On", "Caves: Off"], "creation menu", r => caves = r === "Caves: On", function() {
            if (superflat) {
                this.index = 1
                caves = false
            }
            return superflat
        })
        Button.add(width / 2, 285, 300, 40, "Game Mode: Survival", "creation menu", nothing, always, "Fully Coming Soon\n\nAfter the og maker took too long to make this ( i dont mind, i had a lot of fun making this) we have made our Own!, there is many thing that needs to, like an inventory system, or item drop, or mobs, ETC, but here is a beta!")
        Button.add(width / 2, 335, 300, 40, "Difficulty: Peaceful", "creation menu", nothing, always, "Coming soon\n\nWe are working on it, but its lower on the list due to the possible difficulty.")
        Button.add(width / 2, height - 90, 300, 40, "Create New World", "creation menu", r => {
            world = new World()
            world.id = Date.now()
            let name = boxCenterTop.value || "World"
            let number = ""
            while(true) {
                let match = false
                for (let id in worlds) {
                    if (worlds[id].name === name + number) {
                        match = true
                        break
                    }
                }
                if (match) {
                    number = number ? number + 1 : 1
                } else {
                    name = name + number
                    break
                }
            }
            world.name = name.replace(/;/g, "\u037e")
            win.world = world
            world.loadChunks()
            world.chunkGenQueue.sort(sortChunks)
            changeScene("loading")
        })
        Button.add(width / 2, height - 40, 300, 40, "Cancel", "creation menu", r => changeScene(previousScreen))

        // Loadsave menu buttons
        const selected = () => !selectedWorld || !worlds[selectedWorld]
        let w4 = Math.min(width / 4 - 10, 220)
        let x4 = w4 / 2 + 5
        let w2 = Math.min(width / 2 - 10, 450)
        let x2 = w2 / 2 + 5
        let mid = width / 2
        Button.add(mid - 3 * x4, height - 30, w4, 40, "Edit", "loadsave menu", r => changeScene("editworld"), () => (selected() || !worlds[selectedWorld].edited))
        Button.add(mid - x4, height - 30, w4, 40, "Delete", "loadsave menu", r => {
            if (worlds[selectedWorld] && confirm(`Are you sure you want to deleat this world? ${worlds[selectedWorld].name} will be lost forever! (A long time!)`)) {
                deleteFromDB(selectedWorld)
                window.worlds.removeChild(document.getElementById(selectedWorld))
                delete worlds[selectedWorld]
                selectedWorld = 0
            }
        }, () => (selected() || !worlds[selectedWorld].edited), "Delete the world forever.")
        Button.add(mid + x4, height - 30, w4, 40, "Export", "loadsave menu", r => {
            boxCenterTop.value = worlds[selectedWorld].code
        }, selected, "Export the save code into the text box above for copy/paste.")
        Button.add(mid + 3 * x4, height - 30, w4, 40, "Cancel", "loadsave menu", r => changeScene("main menu"))
        Button.add(mid - x2, height - 75, w2, 40, "Play Selected World", "loadsave menu", r => {
            world = new World()
            win.world = world

            let code
            if (!selectedWorld) {
                code = boxCenterTop.value
            } else {
                let data = worlds[selectedWorld]
                if (data) {
                    code = data.code
                    world.id = data.id
                    world.edited = data.edited
                }
            }

            if (code) {
                try {
                    world.loadSave(code)
                    world.id = world.id || Date.now()
                }
                catch(e) {
                    alert("Unable to load save")
                    return
                }
                changeScene("loading")
            }
        }, () => !(!selectedWorld && boxCenterTop.value) && !worlds[selectedWorld])
        Button.add(mid + x2, height - 75, w2, 40, "Create New World", "loadsave menu", r => changeScene("creation menu"))

        Button.add(mid, height / 2, w2, 40, "Save", "editworld", r => {
            let w = worlds[selectedWorld]
            w.name = boxCenterTop.value.replace(/;/g, "\u037e")
            let split = w.code.split(";")
            split[0] = w.name
            w.code = split.join(";")
            saveToDB(w.id, w).then(success => {
                initWorldsMenu()
                changeScene("loadsave menu")
            }).catch(e => console.error(e))
        })
        Button.add(mid, height / 2 + 50, w2, 40, "Back", "editworld", r => changeScene(previousScreen))

        // Pause buttons
        Button.add(width / 2, 225, 300, 40, "Resume", "pause", play)
        Button.add(width / 2, 275, 300, 40, "Options", "pause", r => changeScene("options"))
        Button.add(width / 2, 325, 300, 40, "Save", "pause", save, nothing, () => `Save the world to your computer/browser. Doesn't work in incognito.\n\nLast saved ${timeString(Date.now() - world.edited)}.`)
        Button.add(width / 2, 375, 300, 40, "Get Save Code", "pause", r => {
            savebox.classList.remove("hidden")
            saveDirections.classList.remove("hidden")
            savebox.value = world.getSaveString()
        })
        Button.add(width / 2, 425, 300, 40, "Exit Without Saving", "pause", r => {
            savebox.value = world.getSaveString()
            initWorldsMenu()
            changeScene("main menu")
        })

        // Options buttons
        Button.add(width / 2, 455, width / 3, 40, "Back", "options", r => changeScene(previousScreen))

        // Comingsoon menu buttons
        Button.add(width / 2, 395, width / 3, 40, "Back", "comingsoon menu", r => changeScene(previousScreen))

        Button.add(width / 2 - 220, 195, width / 3, 50, "Respawn", "dead",  r => {
            changeScene("play");
            p.health = 17;
            p.x = 8;
            p.z = 8;
            play();
            p.y = superflat ? 6 : (Math.round(noise(8 * generator.smooth, 8 * generator.smooth) * generator.height) + 2 + generator.extra);
        });

        let settingsState = true;
        let mouseSense = 100;

        // Multiplayer buttons
        Button.add(width / 2, 395, width / 3, 40, "¯\\_(ツ)_/¯", "multiplayer menu", r => changeScene("main menu"))

        // Settings Sliders
        Slider.add(width/2, 245, width / 3, 40, "options", "Render Distance", 1, 62, "renderDistance", val => settings.renderDistance = Math.round(val))
        Slider.add(width/2, 305, width / 3, 40, "options", "FOV", 30, 110, "fov", val => {
            p.FOV(val)
            if (world) {
                p.setDirection()
                world.render()
            }
        })
        Slider.add(width/2, 365, width / 3, 40, "options", "Mouse Sensitivity", 30, 400, "mouseSense", val => settings.mouseSense = val)
    }
    function initTextures() {
        let textureSize = 256
        let scale = 1 / 16
        let texturePixels = new Uint8Array(textureSize * textureSize * 4)
        textureMap = {}
        textureCoords = []

        setPixel = function(textureNum, x, y, r, g, b, a) {
            let texX = textureNum & 15
            let texY = textureNum >> 4
            let offset = (texY * 16 + y) * 1024 + texX * 64 + x * 4
            texturePixels[offset] = r
            texturePixels[offset + 1] = g
            texturePixels[offset + 2] = b
            texturePixels[offset + 3] = a !== undefined ? a : 255
        }
        getPixels = function(str) {
            // var w = parseInt(str.substr(0, 2), 36)
            // var h = parseInt(str.substr(2, 2), 36)
            var colors = []
            var pixels = []
            var dCount = 0
            for (;str[4 + dCount] === "0"; dCount++) {}
            var ccount = parseInt(str.substr(4+dCount, dCount+1), 36)
            for (var i = 0; i < ccount; i++) {
                var num = parseInt(str.substr(5 + 2*dCount + i * 7, 7), 36)
                colors.push([ num >>> 24 & 255, num >>> 16 & 255, num >>> 8 & 255, num & 255 ])
            }
            for (let i = 5 + 2*dCount + ccount * 7; i < str.length; i++) {
                let num = parseInt(str[i], 36)
                pixels.push(colors[num][0], colors[num][1], colors[num][2], colors[num][3])
            }
            return pixels
        };

        {
            // Specify the texture coords for each index
            const s = scale
            for (let i = 0; i < 256; i++) {
                let texX = i & 15
                let texY = i >> 4
                let offsetX = texX * s
                let offsetY = texY * s
                textureCoords.push(new Float32Array([ offsetX, offsetY, offsetX + s, offsetY, offsetX + s, offsetY + s, offsetX, offsetY + s ]))
            }

            // Set all of the textures into 1 big tiled texture
            let n = 0
            for (let i in textures) {
                if (typeof textures[i] === "function") {
                    textures[i](n)
                } else if (typeof textures[i] === "string") {
                    let pix = getPixels(textures[i])
                    for (let j = 0; j < pix.length; j += 4) {
                        setPixel(n, j >> 2 & 15, j >> 6, pix[j], pix[j+1], pix[j+2], pix[j+3])
                    }
                }
                textureMap[i] = n
                n++
            }

            //Set the hitbox texture to 1 pixel
            let arr = new Float32Array(192)
            for (let i = 0; i < 192; i += 2) {
                arr[i] = textureCoords[textureMap.hitbox][0] + 0.01
                arr[i + 1] = textureCoords[textureMap.hitbox][1] + 0.01
            }
            textureCoords[textureMap.hitbox] = arr
        }

        // Big texture with everything in it
        tex = gl.createTexture()
        gl.activeTexture(gl.TEXTURE0)
        gl.bindTexture(gl.TEXTURE_2D, tex)
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, textureSize, textureSize, 0, gl.RGBA, gl.UNSIGNED_BYTE, texturePixels)
        gl.generateMipmap(gl.TEXTURE_2D)
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
        gl.uniform1i(glCache.uSampler, 0)

        // Dirt texture for the background
        let dirtPixels = new Uint8Array(getPixels(textures.dirt))
        dirtTexture = gl.createTexture()
        gl.activeTexture(gl.TEXTURE1)
        gl.bindTexture(gl.TEXTURE_2D, dirtTexture)
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, dirtPixels)
        gl.generateMipmap(gl.TEXTURE_2D)
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT)
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT)

        genIcons()
    }
    function drawIcon(x, y, id) {
        id = id < 0xff ? (id | blockMode) : id
        x =  x / (3 * height) - 0.1666 * width / height
        y = y / (3 * height) - 0.1666
        initModelView(null, x, y, 0, 0, 0)

        let buffer = blockIcons[id]
        gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
        gl.vertexAttribPointer(glCache.aVertex, 3, gl.FLOAT, false, 24, 0)
        gl.vertexAttribPointer(glCache.aTexture, 2, gl.FLOAT, false, 24, 12)
        gl.vertexAttribPointer(glCache.aShadow, 1, gl.FLOAT, false, 24, 20)
        gl.drawElements(gl.TRIANGLES, blockIcons.lengths[id], gl.UNSIGNED_INT, 0)
    }

    function hotbar() {
        FOV(90)

        for(let i = 0; i < inventory.hotbar.length; i ++) {
            if(inventory.hotbar[i]) {
                let x = width / 2 - inventory.hotbar.length / 2 * inventory.size + (i + 0.5) * inventory.size + 25
                let y = height - inventory.size
                drawIcon(x, y, inventory.hotbar[i])
            }
        }
    }
    function hud() {
        if (p.spectator) {
            return
        }

        hotbar()

        let s = inventory.size
        let x = width / 2 + 0.5
        let y = height / 2 + 0.5

        // Crosshair
        if (!p.spectator) {
            ctx.lineWidth = 1
            ctx.strokeStyle = "white"
            ctx.beginPath()
            ctx.moveTo(x - 10, y)
            ctx.lineTo(x + 10, y)
            ctx.moveTo(x, y - 10)
            ctx.lineTo(x, y + 10)
            ctx.stroke()
        }

        //Hotbar
        x = width / 2 - 9 / 2 * s + 0.5 + 25
        y = height - s * 1.5 + 0.5

        ctx.strokeStyle = "black"
        ctx.lineWidth = 2
        ctx.beginPath()
        ctx.moveTo(x, y)
        ctx.lineTo(x + s * 9, y)
        ctx.moveTo(x, y + s)
        ctx.lineTo(x + s * 9, y + s)
        for(let i = 0; i <= 9; i++) {
            ctx.moveTo(x + i * s, y)
            ctx.lineTo(x + i * s, y + s)
        }
        ctx.stroke()

        let heart = document.getElementById('heartimg');
        for(let i = 0;i < Math.floor(p.health/2);i++) {
            ctx.drawImage(heart, width / 2 - i*20, height / 2 + 280, 15, 15);
        }

        ctx.strokeStyle = "white"
        ctx.lineWidth = 2
        ctx.beginPath()

        ctx.strokeRect(width / 2 - 9 / 2 * s + inventory.hotbarSlot * s + 25, height - s * 1.5, s, s)

        let str = "Average Frame Time: " + analytics.displayedFrameTime + "ms\n"
        + "Worst Frame Time: " + analytics.displayedwFrameTime + "ms\n"
        + "Render Time: " + analytics.displayedRenderTime + "ms\n"
        + "Tick Time: " + analytics.displayedTickTime + "ms\n"
        + "Rendered Chunks: " + renderedChunks.toLocaleString() + " / " + world.loaded.length + "\n"
        + "Generated Chunks: " + generatedChunks.toLocaleString() + "\n"
        + "FPS: " + analytics.fps

        if (p.autoBreak) {
            text("Super breaker enabled", 5, height - 89, 12);

        Text("Health: "+Math.floor(p.health), 5, height - 103, 12);
        }

        ctx.textAlign = 'right'
        text(p2.x + ", " + p2.y + ", " + p2.z, width - 10, 15, 0)
        ctx.textAlign = 'left'
        text(str, 5, height - 77, 12)
    }
    function drawInv() {
        let x = 0
        let y = 0
        let s = inventory.size
        let s2 = s / 2
        let perRow = 13

        gl.clearColor(0, 0, 0, 0)
        gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT)
        ctx.fillStyle = "rgb(127, 127, 127)"
        ctx.fillRect(0, 0, canvas.width, canvas.height)
        FOV(90)

        // Draw the grid
        ctx.lineWidth = 1
        ctx.strokeStyle = "black"
        ctx.beginPath()
        for (y = 0; y < 10; y++) {
            ctx.moveTo(50.5 - s2, 50.5 - s2 + y * s)
            ctx.lineTo(50.5 - s2 + s * perRow, 50.5 - s2 + y * s)
        }
        y--
        for (x = 0; x < perRow + 1; x++) {
            ctx.moveTo(50.5 - s2 + s * x, 50.5 - s2)
            ctx.lineTo(50.5 - s2 + s * x, 50.5 - s2 + y * s)
        }

        // Hotbar
        x = width / 2 - inventory.hotbar.length / 2 * s + 0.5 + 25
        y = height - s * 1.5 + 0.5
        ctx.moveTo(x, y)
        ctx.lineTo(x + s * 9, y)
        ctx.moveTo(x, y + s)
        ctx.lineTo(x + s * 9, y + s)
        for(let i = 0; i <= inventory.hotbar.length; i ++) {
            ctx.moveTo(x + i * s, y)
            ctx.lineTo(x + i * s, y + s)
        }
        ctx.stroke()

        let overHot = (mouseX - x) / s | 0
        if (mouseX < x + 9 * s && mouseX > x && mouseY > y && mouseY < y + s) {
            x += s * overHot
            ctx.lineWidth = 2
            ctx.strokeStyle = "white"
            ctx.beginPath()
            ctx.strokeRect(x, y, s, s)
        }

        //Box highlight in inv
        let overInv = Math.round((mouseY - 50) / s) * perRow + Math.round((mouseX - 50) / s)
        if (overInv >= 0 && overInv < BLOCK_COUNT - 1 && mouseX < 50 - s2 + perRow * s && mouseX > 50 - s2) {
            x = overInv % perRow * s + 50 - s2
            y = (overInv / perRow | 0) * s + 50 - s2
            ctx.lineWidth = 2
            ctx.strokeStyle = "white"
            ctx.beginPath()
            ctx.strokeRect(x, y, s, s)
        }

        if (inventory.holding) {
            drawIcon(mouseX, mouseY, inventory.holding)
        }
        for (let i = 1; i < BLOCK_COUNT; i++) {
            x = (i - 1) % perRow * s + 50
            y = ((i - 1) / perRow | 0) * s + 50
            drawIcon(x, y, i)
        }

        hotbar()
        //hud()
        ctx.drawImage(gl.canvas, 0, 0)
    }
    function clickInv() {
        let s = inventory.size
        let s2 = s / 2
        let perRow = 13
        let over = Math.round((mouseY - 50) / s) * perRow + Math.round((mouseX - 50) / s)
        let x = width / 2 - 9 / 2 * s + 25
        let y = height - s * 1.5
        let overHot = (mouseX - x) / s | 0
        if (mouseX < x + 9 * s && mouseX > x && mouseY > y && mouseY < y + s) {
            let temp = inventory.hotbar[overHot]
            inventory.hotbar[overHot] = inventory.holding
            inventory.holding = temp
        } else if (over >= 0 && over < BLOCK_COUNT - 1 && mouseX < 50 - s2 + perRow * s && mouseX > 50 - s2) {
            inventory.holding = over + 1
        } else {
            inventory.holding = 0
        }

        drawScreens.inventory()
    }

    let unpauseDelay = 0
    function mmoved(e) {
        let mouseS = settings.mouseSense / 30000
        p.rx -= e.movementY * mouseS
        p.ry += e.movementX * mouseS

        while(p.ry > Math.PI*2) {
            p.ry -= Math.PI*2
        }
        while(p.ry < 0) {
            p.ry += Math.PI*2
        }
        if(p.rx > Math.PI / 2) {
            p.rx = Math.PI / 2
        }
        if(p.rx < -Math.PI / 2) {
            p.rx = -Math.PI / 2
        }
    }
    function trackMouse(e) {
        cursor("")
        mouseX = e.x
        mouseY = e.y
        drawScreens[screen]()
        Button.draw()
        Slider.draw()
        Slider.drag()
    }
    document.onmousemove = trackMouse
    document.onpointerlockchange = function() {
        if (doc.pointerLockElement === canvas) {
            doc.onmousemove = mmoved
        } else {
            doc.onmousemove = trackMouse
            if (screen === "play" && !freezeFrame) {
                changeScene("pause")
                unpauseDelay = Date.now() + 1000
            }
        }
        for (let key in Key) {
            Key[key] = false
        }
    }
    canvas.onmousedown = function(e) {
        mouseX = e.x
        mouseY = e.y
        mouseDown = true
        let block, index
        switch(e.button) {
            case 0:
                Key.leftMouse = true
                break
            case 1:
                Key.middleMouse = true
                if (!hitBox.pos) break
                updateHUD = true
                block = world.getBlock(hitBox.pos[0], hitBox.pos[1], hitBox.pos[2]) & 0x3ff
                index = inventory.hotbar.indexOf(block)
                if (index >= 0) {
                    inventory.hotbarSlot = index
                } else {
                    inventory.hotbar[inventory.hotbarSlot] = block
                }
                break
            case 2:
                Key.rightMouse = true
                break
        }
        if(screen === "play") {
            if (doc.pointerLockElement !== canvas) {
                getPointer()
                p.lastBreak = Date.now()
            } else {
                place = false
                if(e.button === 0) {
                    if(Key.control) {
                        place = true
                    } else {
                        changeWorldBlock(0)
                    }
                }
                holding = inventory.hotbar[inventory.hotbarSlot]
                if(e.button === 2 && holding) {
                    place = true
                }
                if(place) {
                    newWorldBlock()
                }
            }
        }else if(screen === 'dead'){
                releasePointer()
                freezeFrame = true
        } else if (screen === "inventory") {
            clickInv()
        }

        Button.click()
        Slider.click()
    }
    canvas.onmouseup = function(e) {
        switch(e.button) {
            case 0:
                Key.leftMouse = false
                break
            case 1:
                Key.middleMouse = false
                break
            case 2:
                Key.rightMouse = false
                break
        }
        mouseDown = false
        Slider.release()
    }
    canvas.onkeydown = function(e) {
        let k = e.key.toLowerCase()
        if (k === " ") {
            e.preventDefault()
        }
        if (e.repeat || Key[k]) {
            return
        }
        Key[k] = true

        if (k === "t") {
            initTextures()
        }

        if (k === "enter") {
            blockMode = blockMode === CUBE ? SLAB : (blockMode === SLAB ? STAIR : CUBE)
            updateHUD = true
        }

        if (screen === "play") {
            if(k === "p") {
                releasePointer()
                changeScene("pause")
            }

            if(k === "") {
                p.autoBreak = !p.autoBreak
                updateHUD = true
            }

            if (k === " " && liquid == true) {
                if (Date.now() < p.lastJump) {
                    p.velocity.y += p.jumpSpeed + 1;
                } else {
                    p.lastJump = Date.now()
                }
            }

            if (k === "z") {
                p.FOV(10, 300)
            }

            if (k === "shift" && !p.flying) {
                p.sneaking = true
                if (p.sprinting) {
                    p.FOV(settings.fov, 100)
                }
                p.sprinting = false
                p.speed = 0.03
                p.bottomH = 1.32
            }

            if (k === "") {
                p.spectator = !p.spectator
                p.flying = true
                p.onGround = false
                updateHUD = true
            }

            if (k === "e") {
                changeScene("inventory")
                releasePointer()
            }

            if (k === ";") {
                releasePointer()
                freezeFrame = true
            }

            if(Number(k)) {
                inventory.hotbarSlot = Number(k) - 1
                holding = inventory.hotbar[inventory.hotbarSlot]
                updateHUD = true
            }
        } else if (screen === "pause") {
            if(k === "p") {
                play()
            }
        } else if (screen === "inventory") {
            if (k === "e") {
                play()
            }
            if (k === "enter") {
                drawScreens.inventory()
            }
        }
    }
    canvas.onkeyup = function(e) {
        let k = e.key.toLowerCase()
        Key[k] = false
        if(k === "escape" && (screen === "pause" || screen === "inventory" || screen === "options" && previousScreen === "pause") && Date.now() > unpauseDelay) {
            play()
        }
        if (screen === "play") {
            if (k === "z") {
                p.FOV(settings.fov, 300)
            }

            if (k === "shift" && p.sneaking) {
                p.sneaking = false
                p.speed = 0.075
                p.bottomH = 1.62
                // p.y += 0.3
            }
        }
    }
    canvas.onblur = function() {
        for (let key in Key) {
            Key[key] = false
        }
        mouseDown = false
        Slider.release()
    }
    canvas.oncontextmenu = function(e) {
        e.preventDefault()
    }
    window.onbeforeunload = e => { 
        if (screen === "play" && Key.control) {
            releasePointer()
            e.preventDefault()
            e.returnValue = "Q is the sprint button; Ctrl + W closes the page."
            return true
        }
    }
    canvas.onwheel = e => {
        e.preventDefault()
        e.stopPropagation()
        if (e.deltaY > 0) {
            inventory.hotbarSlot++
        } else if (e.deltaY < 0) {
            inventory.hotbarSlot--
        }
        if (inventory.hotbarSlot > 8) {
            inventory.hotbarSlot = 0
        } else if (inventory.hotbarSlot < 0) {
            inventory.hotbarSlot = 8
        }

        updateHUD = true
        holding = inventory.hotbar[inventory.hotbarSlot]
    }
    document.onwheel = e => {} // Shouldn't do anything, but it helps with a Khan Academy bug somewhat
    window.onresize = e => {
        width = window.innerWidth
        height = window.innerHeight
        canvas.height = height
        canvas.width = width
        gl.canvas.height = height
        gl.canvas.width = width
        gl.viewport(0, 0, width, height)
        initButtons()
        initBackgrounds()
        inventory.size = 40 * Math.min(width, height) / 600
        genIcons()
        use3d()
        p.FOV(p.currentFov + 0.0001)

        if (screen === "play") {
            play()
        } else {
            drawScreens[screen]();
            Button.draw();
            Slider.draw();

        }
    }

    function use2d() {
        gl.disableVertexAttribArray(glCache.aTexture)
        gl.disableVertexAttribArray(glCache.aShadow)
        gl.disableVertexAttribArray(glCache.aVertex)
        gl.useProgram(program2D)

        gl.enableVertexAttribArray(glCache.aVertex2)
        gl.enableVertexAttribArray(glCache.aTexture2)
        gl.enableVertexAttribArray(glCache.aShadow2)
    }
    function use3d() {
        gl.disableVertexAttribArray(glCache.aTexture2)
        gl.disableVertexAttribArray(glCache.aShadow2)
        gl.disableVertexAttribArray(glCache.aVertex2)
        gl.useProgram(program3D)

        gl.enableVertexAttribArray(glCache.aVertex)
        gl.enableVertexAttribArray(glCache.aTexture)
        gl.enableVertexAttribArray(glCache.aShadow)
    }

    let maxLoad = 1
    function startLoad() {
        // Runs when the loading screen is opened; cache the player's position
        p2.x = p.x
        p2.y = p.y
        p2.z = p.z
        maxLoad = world.loadFrom.length + 9
    }
    function initWebgl() {
        if (!win.gl) {
            let canv = document.createElement('canvas')
            canv.width = ctx.canvas.width
            canv.height = ctx.canvas.height
            canv.style.position = "absolute"
            canv.style.zIndex = -1
            canv.style.top = "0px"
            canv.style.left = "0px"
            gl = canv.getContext("webgl", { preserveDrawingBuffer: true, antialias: false, premultipliedAlpha: false })
            let ext = gl.getExtension('OES_element_index_uint')
            if (!ext) {
                alert("Please use a supported browser, or update your current browser.")
            }
            gl.viewport(0, 0, canv.width, canv.height)
            gl.enable(gl.DEPTH_TEST)
            gl.enable(gl.BLEND)
            gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
            win.gl = gl
        } else {
            gl = win.gl
        }

        if (!document.body.contains(gl.canvas)) {
            document.body.append(gl.canvas)
        }

        modelView = new Float32Array(16)
        glCache = {}
        program3D = createProgramObject(gl, vertexShaderSrc3D, fragmentShaderSrc3D)
        program2D = createProgramObject(gl, vertexShaderSrc2D, fragmentShaderSrc2D)

        gl.useProgram(program2D)
        glCache.uSampler2 = gl.getUniformLocation(program2D, "uSampler")
        glCache.aTexture2 = gl.getAttribLocation(program2D, "aTexture")
        glCache.aVertex2 = gl.getAttribLocation(program2D, "aVertex")
        glCache.aShadow2 = gl.getAttribLocation(program2D, "aShadow")

        gl.useProgram(program3D)
        glCache.uSampler = gl.getUniformLocation(program3D, "uSampler")
        glCache.uPos = gl.getUniformLocation(program3D, "uPos")
        glCache.uDist = gl.getUniformLocation(program3D, "uDist")
        glCache.aShadow = gl.getAttribLocation(program3D, "aShadow")
        glCache.aTexture = gl.getAttribLocation(program3D, "aTexture")
        glCache.aVertex = gl.getAttribLocation(program3D, "aVertex")

        gl.uniform1f(glCache.uDist, 1000)

        //Send the block textures to the GPU
        initTextures()
        initShapes()

        // These buffers are only used for drawing the main menu blocks
        sideEdgeBuffers = {}
        for (let side in shapes.cube.verts) {
            let edgeBuffer = gl.createBuffer()
            gl.bindBuffer(gl.ARRAY_BUFFER, edgeBuffer)
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(shapes.cube.verts[side][0]), gl.STATIC_DRAW)
            sideEdgeBuffers[side] = edgeBuffer
        }
        texCoordsBuffers = []
        for (let t in textureCoords) {
            let buff = gl.createBuffer()
            gl.bindBuffer(gl.ARRAY_BUFFER, buff)
            gl.bufferData(gl.ARRAY_BUFFER, textureCoords[t], gl.STATIC_DRAW)
            texCoordsBuffers.push(buff)
        }

        //Bind the Vertex Array Object (VAO) that will be used to draw everything
        indexBuffer = gl.createBuffer()
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer)
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexOrder, gl.STATIC_DRAW)

        //Tell it not to render the insides of blocks
        gl.enable(gl.CULL_FACE)
        gl.cullFace(gl.BACK)

        gl.lineWidth(2)
        blockOutlines = false
        gl.enable(gl.POLYGON_OFFSET_FILL)
        gl.polygonOffset(1, 1)
        gl.clearColor(sky[0], sky[1], sky[2], 1.0)
        gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT)
    }
    function initBackgrounds() {
        // Home screen background
        use3d()
        gl.clearColor(sky[0], sky[1], sky[2], 1.0)
        gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT)
        FOV(100)
        const HALF_PI = Math.PI / 2
        initModelView(null, 0, 0.5, 0, -HALF_PI / 25, -HALF_PI / 3)
        gl.disableVertexAttribArray(glCache.aShadow)
        gl.vertexAttrib1f(glCache.aShadow, 1.0)

        let blocks = [
[7,4,1,7], [7,4,2,7], [7,4,3,7], [7,4,4,7], [7,5,1,7], [7,5,2,7], 
[7,5,3,7], [6,4,0,7], [6,4,1,7], [6,4,2,7], [6,4,3,7], [6,4,4,7], 
[6,5,0,7], [6,5,1,7], [6,5,2,7], [6,5,3,7], [6,5,4,7], [6,6,3,7], 
[6,6,4,7], [6,7,3,7], [5,0,-1,1], [5,0,0,1], [5,0,1,1], [5,0,2,1], 
[5,1,2,29], [5,2,2,29], [5,3,2,29], [5,4,2,29], [5,5,2,29], [5,6,2,29], 
[5,4,0,7], [5,4,1,7], [5,4,3,7], [5,4,4,7], [5,5,0,7], [5,5,1,7], 
[5,5,3,7], [5,5,4,7], [5,6,1,7], [5,6,3,7], [5,7,1,7], [5,7,2,7], 
[5,7,3,7], [4,-1,-1,1], [4,-1,0,1], [4,-1,1,1], [4,-1,2,1], [4,0,3,1], 
[4,0,4,1], [4,0,5,1], [4,0,6,1], [4,0,7,1], [4,0,8,1], [4,0,9,1], 
[4,0,10,1], [4,4,0,7], [4,4,1,7], [4,4,2,7], [4,4,3,7], [4,4,4,7], 
[4,5,0,7], [4,5,1,7], [4,5,2,7], [4,5,3,7], [4,5,4,7], [4,6,1,7], 
[4,6,2,7], [4,6,3,7], [4,7,4,7], [3,-1,-1,1], [3,-1,0,1], [3,-1,1,1], 
[3,-1,2,1], [3,-1,3,1], [3,-1,4,1], [3,0,5,1], [3,0,6,1], [3,0,7,1], 
[3,0,8,1], [3,0,9,1], [3,0,10,1], [3,4,1,7], [3,4,2,7], [3,4,3,7], 
[3,4,4,7], [3,5,1,7], [3,5,2,7], [3,5,3,7], [2,-1,-1,1], [2,-1,0,1], 
[2,-1,1,1], [2,-1,2,1], [2,-1,3,1], [2,-1,4,1], [2,-1,5,1], [2,-1,6,1], 
[2,-1,7,1], [2,0,8,1], [2,0,9,1], [2,0,10,1], [1,-2,-1,1], [1,-2,0,1], 
[1,-2,1,1], [1,-2,2,1], [1,-2,3,1], [1,-1,4,1], [1,-1,5,1], [1,-1,6,1], 
[1,-1,7,1], [1,-1,8,1], [1,-1,9,1], [1,-1,10,1], [0,-2,-1,1], [0,-2,0,1], 
[0,-2,1,1], [0,-2,2,1], [0,-2,3,1], [0,-2,4,1], [0,-2,5,1], [0,-1,6,1], 
[0,-1,7,1], [0,-1,8,1], [0,-1,9,1], [0,-1,10,1], [-1,-2,-1,1], 
[-1,-2,0,1], [-1,-2,1,1], [-1,-2,2,1], [-1,-2,3,1], [-1,-2,4,1], 
[-1,-2,5,1], [-1,-2,6,1], [-1,-2,7,1], [-1,-1,8,1], [-1,-1,9,1], 
[-1,-1,10,1], [-2,-2,-1,1], [-2,-2,0,1], [-2,-2,1,1], [-2,-2,2,1], 
[-2,-2,3,1], [-2,-2,4,1], [-2,-2,5,1], [-2,-2,6,1], [-2,-2,7,1], 
[-2,-2,8,1], [-2,-2,9,1], [-2,-1,10,1], [-3,-2,-1,1], [-3,-2,0,1], 
[-3,-2,1,1], [-3,-2,2,1], [-3,-2,3,1], [-3,-2,4,1], [-3,-2,5,1], 
[-3,-2,6,1], [-3,-2,7,1], [-3,-2,8,1], [-3,-2,9,1], [-3,-2,10,1], 
[-3,-2,11,1], [-3,-2,12,1], [-4,-2,-1,1], [-4,-2,0,1], [-4,-2,1,1], 
[-4,-2,2,1], [-4,-2,3,1], [-4,-2,4,1], [-4,-2,5,1], [-4,-2,6,1], 
[-4,-2,7,1], [-4,-2,8,1], [-4,-2,9,1], [-4,-2,10,1], [-4,-2,11,1], 
[-4,-2,12,1], [-5,-2,-1,1], [-5,-2,0,1], [-5,-2,1,1], [-5,-2,2,1], 
[-5,-2,3,1], [-5,-2,4,1], [-5,-2,5,1], [-5,-2,6,1], [-5,-2,7,1], [-5,-2,8,1], 
[-5,-2,9,1], [-5,-2,10,1], [-5,-2,11,1], [-5,-2,12,1], [-6,-2,-1,1], 
[-6,-2,0,1], [-6,-2,1,1], [-6,-2,2,1], [-6,-2,3,1], [-6,-2,4,1], 
[-6,-2,5,1], [-6,-2,6,1], [-6,-2,7,1], [-6,-2,8,1], [-6,-2,9,1], 
[-6,-2,10,1], [-6,-2,11,1], [-7,-2,3,1], [-7,-2,4,1], [-7,-2,5,1], 
[-7,-2,6,1], [-7,-2,7,1], [-7,-2,8,1], [-7,-2,9,1], [-8,-2,2,1], [-8,-2,3,1], 
[-8,-2,4,1], [-8,-2,5,1], [-8,-2,6,1], [-8,-2,7,1], [-8,-2,8,1]
        ]

        for (let i = 0; i < blocks.length; i += 1) {
            block2(blocks[i][0], blocks[i][1], blocks[i][2], blocks[i][3])
        }

        gl.enableVertexAttribArray(glCache.aShadow)
        let pixels = new Uint8Array(width * height * 4)
        gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels)
        mainbg = ctx.createImageData(width, height)
        let w = width * 4
        for (let i = 0; i < pixels.length; i += 4) {
            let x = i % w
            let y = height - Math.floor(i / w) - 1
            let j = y * w + x
            mainbg.data[j] = pixels[i]
            mainbg.data[j + 1] = pixels[i + 1]
            mainbg.data[j + 2] = pixels[i + 2]
            mainbg.data[j + 3] = pixels[i + 3]
        }

        // Dirt background
        use2d()
        let aspect = width / height
        let stack = height / 96
        let bright = 0.4
        if (dirtBuffer) {
            gl.deleteBuffer(dirtBuffer)
        }
        dirtBuffer = gl.createBuffer()
        gl.bindBuffer(gl.ARRAY_BUFFER, dirtBuffer)
        let bgCoords = new Float32Array([
            -1, -1, 0, stack, bright,
            1, -1, stack * aspect, stack, bright,
            1, 1, stack * aspect, 0, bright,
            -1, 1, 0, 0, bright
        ])
        gl.bufferData(gl.ARRAY_BUFFER, bgCoords, gl.STATIC_DRAW)
        gl.uniform1i(glCache.uSampler2, 1)
        gl.clearColor(0, 0, 0, 1)
        gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT)
        gl.vertexAttribPointer(glCache.aVertex2, 2, gl.FLOAT, false, 20, 0)
        gl.vertexAttribPointer(glCache.aTexture2, 2, gl.FLOAT, false, 20, 8)
        gl.vertexAttribPointer(glCache.aShadow2, 1, gl.FLOAT, false, 20, 16)
        gl.drawArrays(gl.TRIANGLE_FAN, 0, 4)
        pixels = new Uint8Array(width * height * 4)
        gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels)
        dirtbg = ctx.createImageData(width, height)
        dirtbg.data.set(pixels)
    }
    function initPlayer() {
        p = new Camera()
        p.speed = 0.075
        p.health = 20;
        p.velocity = new PVector(0, 0, 0)
        p.pos = new Float32Array(3)
        p.sprintSpeed = 1.5
        p.flySpeed = 2.5
        p.x = 8
        p.y = superflat ? 6 : (Math.round(noise(8 * generator.smooth, 8 * generator.smooth) * generator.height) + 2 + generator.extra)
        p.z = 8
        p.previousX = 8
        p.previousY = 70
        p.previousZ = 8
        p.w = 3 / 8
        p.bottomH = 1.62
        p.topH = 0.18
        p.onGround = false
        p.jumpSpeed = 0.3
        p.sprinting = false
        p.maxYVelocity = 1.5
        p.gravityStength = -0.032
        p.lastUpdate = win.performance.now()
        p.lastBreak = Date.now()
        p.lastPlace = Date.now()
        p.lastJump = Date.now()
        p.autoBreak = false
        p.flying = false
        p.sneaking = false
        p.spectator = false

        win.player = p
        win.p2 = p2
    }
    function initWorldsMenu() {
        while (window.worlds.firstChild) {
            window.worlds.removeChild(window.worlds.firstChild)
        }
        selectedWorld = 0
        window.boxCenterTop.value = ""

        const deselect = () => {
            let elem = document.getElementsByClassName("selected")
            if (elem && elem[0]) {
                elem[0].classList.remove("selected")
            }
        }

        function addWorld(name, version, size, id, edited) {
            let div = doc.createElement("div")
            div.className = "world"
            div.onclick = e => {
                deselect()
                div.classList.add("selected")
                selectedWorld = id
            }
            let br = "<br>"
            div.id = id
            div.innerHTML = "<strong>" + name + "</strong>" + br

            if (edited){
                let str = (new Date(edited).toLocaleDateString(undefined, {
                    year: "numeric",
                    month: "short",
                    day: "numeric",
                    hour: "numeric",
                    minute: "2-digit"
                }))
                div.innerHTML += str + br
            }
            div.innerHTML += version + br
            div.innerHTML += `${size.toLocaleString()} bytes used`

            window.worlds.appendChild(div)
        }

        worlds = {}
        if (loadString) {
            try {
                let tempWorld = new World()
                tempWorld.loadSave(loadString)
                let now = Date.now()
                addWorld(`${tempWorld.name} (Pre-loaded)`, tempWorld.version, loadString.length, now)
                worlds[now] = {
                    code: loadString,
                    id: now
                }
            }
            catch(e) {
                console.log("Unable to load hardcoded save.")
                console.error(e)
            }
        }
        loadFromDB().then(res => {
            if(res && res.length) {
                let index = res.findIndex(obj => obj.id === "settings")
                if (index >= 0) {
                    Object.assign(settings, res[index].data) // Stored data overrides any hardcoded settings
                    p.FOV(settings.fov)
                    res.splice(index, 1)
                }
            }

            if (res && res.length) {
                res = res.map(d => d.data).filter(d => d && d.code).sort((a, b) => b.edited - a.edited)
                for (let data of res) {
                    addWorld(data.name, data.version, (data.code.length + 60), data.id, data.edited)
                    worlds[data.id] = data
                }
            }
            window.worlds.onclick = Button.draw
            window.boxCenterTop.onkeyup = Button.draw
        }).catch(e => console.error(e))

        superflat = false
        trees = true
        caves = true
    }

    function initEverything() {
        console.log("Initializing world.")

        worldSeed = Math.random() * 2000000000 | 0
        seedHash(worldSeed)
        caveNoise = openSimplexNoise(worldSeed)
        noiseSeed(worldSeed)

        generatedChunks = 0

        initPlayer()
        initWebgl()

        if (win.location.origin === "https://www.kasandbox.org" && (loadString || MineKhan.toString().length !== 183240)) {
            // Prevent Ctrl F
            message.innerHTML = '.oot lanigiro eht tuo kcehc ot>rb<erus eb ,siht ekil uoy fI>rb<.dralliW yb >a/<nahKeniM>"wen_"=tegrat "8676731005517465/cm/sc/gro.ymedacanahk.www//:sptth"=ferh a< fo>rb<ffo-nips a si margorp sihT'.split("").reverse().join("")
        }

        initBackgrounds()

        drawScreens[screen]()
        Button.draw()
        Slider.draw()

        p.FOV(settings.fov)
        initWorldsMenu()
        initButtons()
    }

    // Define all the scene draw functions
    (function() {
        function title() {
            let title = "MINEKHAN"
            let subtext = "JAVASCRIPT EDITION WARNING: BUGS"
            let font = "VT323,monospace"
            strokeWeight(1)
            ctx.textAlign = 'center'

            ctx.font = "bold 120px " + font
            fill(30)
            text(title, width / 2, 158)
            fill(40)
            text(title, width / 2, 155)
            ctx.font = "bold 121px " + font
            fill(50)
            text(title, width / 2, 152)
            fill(70)
            text(title, width / 2, 150)
            fill(90)
            ctx.font = "bold 122px " + font
            text(title, width / 2, 148)
            fill(110)
            text(title, width / 2, 145)

            ctx.font = "bold 32px " + font
            fill(50)
            text(subtext, width / 2-1, 180)
            text(subtext, width / 2+1, 180)
            text(subtext, width / 2, 179)
            text(subtext, width / 2, 181)
            ctx.font = "bold 32px " + font
            fill(150)
            text(subtext, width / 2, 180)
        }
        const clear = () => ctx.clearRect(0, 0, canvas.width, canvas.height)
        const dirt = () => ctx.putImageData(dirtbg, 0, 0)

        drawScreens["main menu"] = () => {
            ctx.putImageData(mainbg, 0, 0)
            title()
            fill(220)
            ctx.font = "20px VT323"
            ctx.textAlign = 'left'
            text("Minecraft " + version, width - (width - 2), height - 2)
        }

        drawScreens.play = () => {
            controls()
            runGravity()
            resolveContactsAndUpdatePosition()

            if(p.health < 20) {
                p.health+=0.04;
            }

            if(p.health > 20) {
                p.health = 20;
            }

            if (updateHUD) {
                clear()
                gl.clearColor(0, 0, 0, 0)
                gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT)
                hud()
                ctx.drawImage(gl.canvas, 0, 0)
                updateHUD = false
                freezeFrame = false

                gl.clearColor(sky[0], sky[1], sky[2], 1.0)
            }
            defineWorld()
        }

        drawScreens.loading = () => {
            // This is really stupid, but it basically works by teleporting the player around to each chunk I'd like to load.
            // If chunks loaded from a save aren't generated, they're deleted from the save, so this loads them all.

            let sub = maxLoad - world.loadFrom.length - 9
            let standing = true
            if (world.loadFrom.length) {
                let load = world.loadFrom[0]
                p.x = load.x * 16
                p.y = load.y * 16
                p.z = load.z * 16
                standing = false
            } else {
                p.x = p2.x
                p.y = p2.y
                p.z = p2.z

                let cx = p.x >> 4
                let cz = p.z >> 4

                for (let x = cx - 1; x <= cx + 1; x++) {
                    for (let z = cz - 1; z <= cz + 1; z++) {
                        if (!world.chunks[x] || !world.chunks[x][z] || !world.chunks[x][z].buffer) {
                            standing = false
                        } else {
                            sub++
                        }
                    }
                }
            }

            if (standing) {
                play()
                return
            }

            world.tick()

            let progress = Math.round(100 * sub / maxLoad)
            dirt()
            fill(255)
            textSize(30)
            ctx.textAlign = "center"
            text(`Loading... ${progress}% complete (${sub} / ${maxLoad})`, width / 2, height / 2)
        }

        drawScreens.inventory = drawInv

        drawScreens.pause = () => {
            strokeWeight(1)
            clear()
            ctx.drawImage(gl.canvas, 0, 0)

            textSize(60)
            fill(0, 0, 0)
            ctx.textAlign = 'center'
            text("Paused", width / 2, 60)
        }

        drawScreens.options = () => {
            clear()
        }
        drawScreens["creation menu"] = () => {
            dirt()
            ctx.textAlign = 'center'
            textSize(20)
            fill(255)
            text("Create New World", width / 2, 20)
        }
        drawScreens["loadsave menu"] = () => {
            dirt()
            ctx.textAlign = 'center'
            textSize(20)
            fill(255)
            text("Select World", width / 2, 20)
        }
        drawScreens.editworld = dirt
    })()

    // Give the font time to load and redraw the homescreen
    setTimeout(e => {
        drawScreens[screen]()
        Button.draw()
        Slider.draw()
    }, 100)

    let debugMenu = false
    function gameLoop() {
        let frameStart = win.performance.now()
        if (!gl) {
            initEverything()
            releasePointer()
        }

        if (screen === "play" || screen === "loading") {
            drawScreens[screen]()
        }

        if (Date.now() - analytics.lastUpdate > 50 && analytics.frames) {
            analytics.displayedTickTime = (analytics.totalTickTime / analytics.frames).toFixed(1)
            analytics.displayedRenderTime = (analytics.totalRenderTime / analytics.frames).toFixed(1)
            analytics.displayedFrameTime = (analytics.totalFrameTime / analytics.frames).toFixed(1)
            analytics.fps = Math.round(analytics.frames * 1000 / (Date.now() - analytics.lastUpdate))
            analytics.displayedwFrameTime = analytics.worstFrameTime.toFixed(1)
            analytics.frames = 0
            analytics.totalRenderTime = 0
            analytics.totalTickTime = 0
            analytics.totalFrameTime = 0
            analytics.worstFrameTime = 0
            analytics.lastUpdate = Date.now()
            updateHUD = true
        }

        analytics.frames++
        analytics.totalFrameTime += win.performance.now() - frameStart
        analytics.worstFrameTime = Math.max(win.performance.now() - frameStart, analytics.worstFrameTime)
        win.raf = requestAnimationFrame(gameLoop)
    }
    return gameLoop
}

init = MineKhan()
if (window.parent.raf) {
    window.cancelAnimationFrame(window.parent.raf)
    console.log("Canceled", window.parent.raf)
}
init()

    </script>
    </body>
</html>
Hacker1254 commented 3 years ago

wut Please do❗

Hacker1254 commented 3 years ago

I see, no i mean like by using this image

Hacker1254 commented 3 years ago

instead of two spin offs

ghost commented 3 years ago

Did it work?

Hacker1254 commented 3 years ago

no?

Hacker1254 commented 3 years ago

image

ghost commented 3 years ago

It still didn't work. That's 'cause you're using khan.

ghost commented 3 years ago

Just use http://www.csgnetwork.com/htmlcodetest.html

Hacker1254 commented 3 years ago

i see image the white backgrounds are easy to fix i think

ghost commented 3 years ago

Ye. But It still worked!

ghost commented 3 years ago

Hooray!

Hacker1254 commented 3 years ago

hmm even having the image to base36 it doesnt work Eh

Hacker1254 commented 3 years ago

use this code as the heart img code

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOAAAADgCAYAAAAaLWrhAAAUP0lEQVR4Xu2dXWxcRxXHZ9e7/oyTuM1HE6c4UQGl4qGoqmisygRR0RZVKRICqQXxUNGSF0Ag6AOpGrUvQdAHpFJUFZACVSQiVPGAEMWAhFJDG/qhglBRSZWipMH5aJzabrzrXe8HM2vf6Obm7t65987cMzP3v1KU2J45c87vnP+dude7J4zhBQIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIg4BqBgmsBefEcPnz4gaNHj36Vf932/fGH64+9GwfxfTG/26vbz3rNCdry1i7yHxT27t37hwMHDjypIy8vvvjibY8++uiPh4eHL/vi8vgEGcSpjTjxyoTmrS3+LkxOTv7lwQcffHZiYuKizGSbxpRscjaOr2fPnt0xPT19Z5w5JozduXPn27r8uHDhwg0zMzN7dNnXZXdsbGyuXq8P6LJPaVdcdV19xbmCu8rAlbg6O6ErwfjjcFmAtuZL9XHOVg658BsCzEWarQ/S2YsSBGh9bSIAmwm4LEBbr5q2+q1TB84ycVmAOgsCtkFACQEIUAlGGAGBZAQgwGTcMCtbAt6bBbJdNYPVIMAMIMdcwtn7nZgccjEcAsxFmhGkqQQgQFMzA79yQcBlAeIol4sStjtIlwVod2bgvZ+AsxdTCDBfhW5rIeMpaL7qlDRaJ9/1n5IoBJgSIKaDQFoCtu7ePePGETRtWWB+FgSiOhNk4YOWNSBALVhhVDEBZ4/lzgmwUqn4+4koroNMzOksNp22dcIRdepcrQpgzvWE4Q2HvHuFzt82BdhYLWHtIrGUiZP3gDblItYVdqjZ6BMT1sWaRTt4ni+/fqWuLSfrG7VOgLYxKVeWNmwolVadd+yl/WrbixdvHfjl48ePf2ptjHeF63bD7b8Chh0zrzyqHmi2ygOzp2697uL5W2zL16VNW/9R2z7xeq2vuLYhRkbQ68jtMWtxJmzg4rmdA+dn7xlpypqOXDuTAZzJ2+yG8b8vlvsrawt6TZq82K869cRwqrBt27bTvOXhM1QtD0kFuH///mf562sxgEkP/f4nbmGfH1vHNhTL0nOoBy60VtgLF+bZt954U4srDwwW2ffuvYcNXF6wisv0QoUdfOkVdkoDlampqVePHDlyLxfgexrMR5rUdtyJXFnzgKGVBruOXxcHl5euuakSl0vSK09I7MKnvnI/K7X17k5D9Trb2mixYmtJcwbUmG8VS2yQrTCNTUFJS4FagJncWIctksnCMWqwzQsty5dp8YfFnpEySFE4+Wg3y0JOspa/y6yzHWeTgKGZQ/o2N2oBkl59aPIdvmqhpffoGVw1o93FJMRG+pLtuYcIQfBRWfDrrN3yX3WuPL7L+AiadcxJ1svo6pzRMuEEqHfATC7ENtwDJilQzFFCINdHUCUEZYyE7Toy81wdQ3rZdxVqgriod8AELmMKCLhDAAJ0J5eIxEICEKCFSYPL7hCgFiBuRdypJUSSgAC1ABO4jCkgoJwA2UYAASrPJQxaRoBMfIITtQBJg7esUOCugwSoBeggUoRkGYEWpb/UAsQOSJl9rC0I4J0wqAMQICZAthFQ74DE3LE8CHQIkAmQ+tMQmQauq7tr8B3l/k/cZxog1GQdAeodMJNPQ3hZ0SUGf0egtGsU0xqwrgSNcJiMOukOyDt1dVoH6no1+8rsck19N7uR0mqjJ/+HaIMZ9L6m/uyhn21pucVa9aou3FrtauwJMzLUonsQGrkD8daBXzp79uyONbqep2k+3dNZk/ftLM6+/NfP8daBd4gGSqpft+/bx26+9TbVZlkf761Ze/NfrP23GTa4Uo+0H+fYK/rCvLW8zF5ZqrBGQf218WPFPvbx60eZaFGY9SfwI0F1GeAxee1yhdU1fGj54uDgpQ1Tn356ZePYvM8Ff9vDKI102z0L9Xq9/Pjjjx/qFXuUcXb33Xf/eXp6+s6kAHvNE60Dv7B1M9tYU3tVFjvf4P6vsw2fuauzfHtIXSvaQvUyW/jTH9n5Z37EtvMOY6pfouB4T1DVZjv2hPCCO7eWhRQb1cVE1MnrrTb79vQxdkKxzwExd7Uuc5mNFGlc38WiniRGG43OTiIge1dlr0NY0qu0/9ipUnxx45S98vkvoSLmQUUnoji7r8rYVNtSycTv23LnalTS2fIwEoWMABWVQ3dfOgrnhXdF6QkbFF3zNFLhzhdJssuAJCJIMqdTS0mdzOE8U1jJCDCz9Kh4FKXChuqAwwQV5WdSEar2Hfb0EjBKgGmfGPp//6YXm7z1KKGFWUoyR9hJOk8+GnNG+mslbd1QRiVzt29VXq1yljLzWNsIAkbtgCb+7kxllsLeMaPSfjdbNu8Q3WJK83uwLJjLriGzA8raUjrOtp3M/4ujMBD+ezpdb7/3fAiu77+wmfLwIapYgrHY4ndUXMGfGylAG8XXC7y/eKhio1o3bkF2u3ipsGOiDaOOoCYCkvEpqrijfi6zhsyYqHWifi6zRlZjXDliRvEycgeMcho/BwFXCMgI0NXjtys5RBwWE5ARoMXhwXUQMJsABGh2fuCd4wQgQMcTjPDMJgABmp0feOc4AQjQ8QQjPLMJQIBm5wfeOU4AAnQ8wQjPbAIQoNn5gXeOE4AAHU8wwjObgLPvBRXdy0QDJeWv5WqnMxpeIKCCgJMCFH07RetA8WqW1HaU9NoSXhfyzmYXP3enoshgozsBJwUowhV9O2dfmtGSeyE+r8WflgVgNDcEnBSg18Zue27SiEBtJeCkAKmSYdPn7agYYd2rCTj1FLRbS4ask47Pb2VN3Nz1qtVqz3JwSoAmpQEiNCkb5vri/BG015PJpM1vo3q84ChqbsGb5hl2QNMyAn9yRcCpHTBs5+m1GyXdqYLzwnZZ/E4wVzpKHKxTOyDVQ5hugkycFUzMDQGnBEiZtbi7L6WvWNscAk4eQYPHv6yPg34xZr22OaUFT2QIOLUDRrVmlwGCMSCQJQGZHdD6X2lRBpD0QU+WRYC19BEYGhrqWQIyAtTnnWLL3SKFCBSDhjllBJw6giqjAkMgkBEBCDAj0FgGBMII5FKAlPeEKMN4BMIerLmUv1wKUJSAS0mMV9JujHYlfzIPYbTHquMhSa/fv+lYz42yNi8K19/gYMQOqF3h5tUVPAKBDgEjBKgjF97/ww5x66ALm6oIGCHAq/474qLMqbh7+O3AfBw3VZWKW3ZMqYt01Z4yJ5X5CjtdLrKtLb+hRkqrq/P7+/vYuiL2v5QwjZguLqrvV5aV+zJ3YZ5d3DDGKLu8kgrwvUqFnTlTZ+/XqtJwG31lVmquRI7fvWucsUHS8CJ9xAA5AkJ8b/33f6GDZevBm+wfXysPsdP1S3JOaBpFUqHeHlfnQa309TEBJc4r7vg4tjHWXALd8h63HrzxjbUbsLRnrjTESO4BSVSfhhLmOk2Ash5JBOh0NhEcCMQgAAHGgIWhIKCaAIkAKc/cqgHCHgikIUAiQMozdxpYmAsCqgmQCFB1ELAHArYSgABtzRz8doIAiQBxD+hE7SAIBQRIBIh7QAWZgwknCJAI0AlyCAIEFBCQEaApbxxXEC5MgIBZBGQEqNxj3AMqRwqDlhIgESDuAS2tFritnICMAHEEVY4dBkFglYCMAMEKBK4igI85qysIEgHiHlBdAmEpPQHKeiQRIO4B0xcNpQXck6ijLyNAJ04cKoOg+p941aUdlvwEKDcEyrW1VUGwZ4xK8QmnsQNoS13uDJMIUJy5L6+hrvKeMOXSqhuiP4x4lZvNxIkQNsrNEptbvMzGhjd17MgIptBqsGBLw15OiPF5fwlefg5x+AXZ9ebfYKKBktfDxT93Nd/J6kXMrRcKZndFm5ycPMYDFkdVsZGIBoJez1uvruN0D/dOb+2x5cpAZX5h4vjc3M5iQ20xX1/qZyvVJrs+dis7OT9Ey8ORUvmq4sujGIPiS986MJz/iXqTvdGssUa7zOYaopWXutfC+tGFyb1T/9w9OOzve9irr7N3oPIfrML+V/JCpVIZnJmZ6ems6tNZLDLffOjhnz718589LPa/db5dMfhvYVTsmOL7Yf8W3wvO+caOG9medkuqhWEsp/ngHZs3sBu3bMq9AD1uXt/Obq0D4/L1jxcdzF5rN9nzs+fYuzHy752wutWM+P48/3PXHXe8euTIkc9u2bVrLo2fSeeSHEE9Z2t9xStnBw+YJzBvjMz3/XPENVSAFVfK2sBgUi5d54n7y9pVjYSVL2GVQXEF91/+47YIjAq2yY+5jXaRLa1dhMPqIknNXLkFKhZXqsUiWUZJBejBlDv4RaUKP8+KQFB0WawrrtQa62T14QPBS+bXEARuYUnTCfT6799M9z3gn/dsg8Rt6h2Q9B6UhLgDi8o8VbYoTNJf62IHtKhS4KoWAqSbAASoJacwCgJyBCBAOU4Y5TYBsl2QWoBkgbtdT4jOFgLUArSFE/wEAS0EIEAtWGHUIgKkD3WpBYgjqEWVClfVE6AWoPqIYBEELCJALUDS7d+iPMFVRwlQC9BRrAgLBOQIQIBynDDKXQKkpzBqAZIGn7SmBqipJXUc88IIkD4IRCkZUJRZvRvYv05WaxqAV8YFMhFCgDLpCYxR/YFcimNAmjXJqjVBriSnpMEhuUT4MAgwFT51k7OoAK/Ried1mjUdEiHpYQCfB0ypoW6deVKa1To97Ydp0whXa2AWGqcWoLZctnirw8X+fja01upQVW5Ey8PN3BjvZ8MGWEmq5WGctbNod9jifVZUvgQL8RKtA6tlDd0deJe7BZUOG2RLbSZiBrZnz56ZhYWFMT5N5nQU1g5OrBjWJnGg9c7J207OntvpuVRqrfC+kuUrHga/Fj/wfy/s52LM+pVqp3/pCPdmMHbbw2hAouXhuqKeA57XvUxHd5XzXIP/qS+xxfZQZJDdOHdj/k6pOL/vvn0vrQyPfMCNe60xu63TDZ6ok+DPiuPj46LZmraNIAqGnkxHrbr281OnTq12zl19hYkw6F+v05MHuPP3r3/y9Hee/uGTB1Rfj0e48akbtrK9wyNsY8Xfs00y6IhhouXhhzZt7FSKyqrQ2TpQ7HxCfL89f5Gd5n6rZC6aMX3yvn2/f/i7jzwxMTFxMqRW/MKKup8LrXdu96Ka7MW3QroD6gz80KFD86fi84icsZGPWObHW9HRex3vWan6pfoJa5h/qlsHimOn2PmE+HQw5zvf0rZt297l9ULSu1N1jv328BRUJ92EtsVupXL3S+hG7Gkqd77A4uLYSXpaiw1DcgIEKAkKw0gJ2Hg9kgIGAUphwiBiAmEPUIhdUrM8BKiGI6yAQCICLgvQyXuGRFl2Y5KT+XRZgM7eN7ihJ0QhCLgsQGQYBIwnAAEanyI46DIBlwXo5D2Dy8WYx9hcFiDuAd2paGcvpi4L0J3yQyTOEoAAnU0tArOBAARoQ5bgo7MEIEBnU4vAbCAAAdqQJfjoLAEI0NnUIjAbCECANmQJPkZ90t1aQhCgtamD4y4QgABdyCJisJYAaU8YzdS0vBNm3ud0U3F7v75WQzMS3g9trY+Nat+F46KBEl7xCDgrQN7EZ/b+++8/uoajmxhlRXrlHqRcWRopnTv3keOzsx/dzIpqTxCFIrupztj2/gFWm1+Ol8nI0Q12ot5k/27U2CLvYsbaos2Kmtccb8+4sH50kXcvO8YbKHmt4vxd7vycu7WXDDrjMS/s3r37zf7+/poab82y4ux77ETLw3q9PpACd5g4O9/73dFffeWRxw7+YDyF8bCpoqnR7YNF9sWtN7KRWlWpdbHzvdZusudnz7ElxbuVaJrL+3a+wFsHHuQXvjP8S1mRiRijarAgxMc7or2nFIghxpzdAXW2PDx8+PC74rCosgWfSISweXNpuNP4V3XrwBpvpdhoFpnoQiu2KNWHXbHzcebv8D+XDKltK9xQe4SyImQznVQtiGCUQtRzDX6+1fdytnGSPmT4RHxStrL3jknt2zoPXGJmDjtgTGBrw6PuW5JZxazcEYAAc5dybQHjCJoALQSYABqmhBLA8TNBYUCACaDxKTiCJuOGWQECECBKAgQICUCAhPAdWxpH0AQJhQATQLNxSrmJd2qamDcI0MSswKfcEIAAc5NqBGoiAQjQxKzAp9wQgABzk2oEaiIBCNDErMCn3BCAAJOlGo/ck3HDLPwiHjWgiYCzncs08eqYxQ6oky5sg0AEAQgQJaKKAHbABCQhwATQ+BS8GftabmCSoJac7QmTgIUxU6q8fUSZ93BR+RItKcRrXqVRiDA1TbVZTu2OHQZGR0cXp6amXg54638y6v27264QbNPnjSuNLi5sO1mt7ujvLyuFUS8UROvAD+666cMnqsXiCjcu+hL6WweK9eLsYv7OZ4Xx8fHTa/aU+u26sTjAXWcRKz7e9nBzDwF6Pwry9b72BHiNUH/z3C8feuKxg4e2xPImerBoqjm5d+rlp37x3L1cgOLWQ/gS1q9TVojB2Nqutg6Mppt8BHbAhOx0FRtveXhGHBN1HBV3Dw5Xt+za9X7CkDFNAwE8hNEANYnJSqWSxWkEbyBIkhyNcyBAjXDjmB4eHg4eS+NMlx0LAcqSymgcBJgR6BjLQCQxYNk+FAI0L4NZHEXNizqnHkGA+Uo8dlfD8g0BGpYQze5AgJoBxzUPAcYlhvEgoJAABKgQJkyBQFwCEGBcYvrH45ion7ExK0CAxqQCjuSRAASYx6wjZmMIQIDGpCITR3C8zQSz/CIQoDyrrEZCJFmRNmAdCNCAJMCF/BKAAPObe0RuAAEI0IAkwIX8EoAA85t7RG4AAQjQgCRk6AI+aZEhbJmlIEAZSu6MgQANyyUEaFhCuDs6RYJ8G5ZvJMSwhHB3tP1f0rzvzJB54cIjEDCMABcKLoyG5QTugAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgIBlBP4PGi0Qg8gxpNoAAAAASUVORK5CYII=

ghost commented 3 years ago

Ok.

Hacker1254 commented 3 years ago

but for now, lets just stick with the khan heart

ghost commented 3 years ago

Ok.

ghost commented 3 years ago

I got the infinite jumping working.

Hacker1254 commented 3 years ago

YES!