KeineAhnung4u / Craftnite OBJ Loader

// ==UserScript==
// @name         Craftnite OBJ Loader
// @namespace    https://openuserjs.org/user/KeineAhnung4u
// @version      1.0
// @description  Upload and process OBJ files in Craftnite.io
// @author       KeineAhnung4u
// @match        https://craftnite.io/*
// @grant        none
// @license Artistic-2.0
// @copyright 2025, KeineAhnung4u (https://openuserjs.org/users/KeineAhnung4u)
// ==/UserScript==

(function() {
    'use strict';

    // Craftnite blok renkleri (HSV formatında) - SİZ EKLEYECEKSİNİZ!
    const blockColorsHSV = {
        64256: [255, 255, 255], // white
        64257: [255, 165, 0],   // orange
        64258: [255, 0, 255],   // magenta
        64259: [173, 216, 230], // light blue
        64260: [255, 255, 0],   // yellow
        64261: [0, 255, 0],     // lime
        64262: [255, 192, 203], // pink
        64263: [128, 128, 128], // gray
        64264: [192, 192, 192], // silver
        64265: [0, 255, 255],   // cyan
        64266: [128, 0, 128],   // purple
        64267: [0, 0, 255],     // blue
        64268: [139, 69, 19],   // brown
        64269: [0, 128, 0],     // green
        64270: [255, 0, 0],     // red
        64271: [0, 0, 0],       // black

        // stone
        256: [128, 128, 128],   // stone
        257: [153, 101, 21],    // granite
        258: [255, 255, 255],   // granite smooth
        259: [196, 196, 196],   // diorite
        260: [240, 240, 240],   // diorite smooth
        261: [169, 169, 169],   // andesite
        262: [211, 211, 211],   // andesite smooth
        1024: [105, 105, 105],  // cobblestone
        11008: [128, 128, 128], // double stone slab
        11013: [128, 128, 128], // stonebrick
        25089: [128, 128, 128], // stonebrick mossy
        25090: [128, 128, 128], // stonebrick cracked
        25091: [128, 128, 128], // stonebrick carved

        // glass
        5120: [255, 255, 255],  // glass
        24320: [255, 255, 255], // glass white
        24321: [255, 165, 0],   // glass orange
        24322: [255, 0, 255],   // glass magenta
        24323: [173, 216, 230], // glass light blue
        24324: [255, 255, 0],   // glass yellow
        24325: [0, 255, 0],     // glass lime
        24326: [255, 192, 203], // glass pink
        24327: [128, 128, 128], // glass gray
        24328: [192, 192, 192], // glass silver
        24329: [0, 255, 255],   // glass cyan
        24330: [128, 0, 128],   // glass purple
        24331: [0, 0, 255],     // glass blue
        24332: [139, 69, 19],   // glass brown
        24333: [0, 128, 0],     // glass green
        24334: [255, 0, 0],     // glass red
        24335: [0, 0, 0],       // glass black

        // other blocks
        38912: [255, 0, 0],     // redstone block
        10496: [255, 215, 0],   // gold block
        10752: [192, 192, 192], // iron block

        // AK47 colors
        55793: [255, 255, 0],   // item-ak47-yellow
        55794: [128, 0, 128],   // item-ak47-purple
        55795: [0, 0, 255],     // item-ak47-blue
        55796: [0, 255, 0],     // item-ak47-green
        55797: [128, 128, 128], // item-ak47-grey

        // Shotgun colors
        55798: [255, 255, 0],   // item-shotgun-yellow
        55799: [128, 0, 128],   // item-shotgun-purple
        55800: [0, 0, 255],     // item-shotgun-blue
        55801: [0, 255, 0],     // item-shotgun-green
        55802: [128, 128, 128], // item-shotgun-grey

        // Sniper colors
        55803: [255, 255, 0],   // item-sniper-yellow
        55804: [128, 0, 128],   // item-sniper-purple
        55805: [0, 0, 255],     // item-sniper-blue
        55806: [0, 255, 0],     // item-sniper-green
        55807: [128, 128, 128], // item-sniper-grey

        // hardened_clay_stained colors
        40704: [255, 255, 255], // hardened_clay_stained_white
        40705: [255, 165, 0],   // hardened_clay_stained_orange
        40706: [255, 0, 255],   // hardened_clay_stained_magenta
        40707: [173, 216, 230], // hardened_clay_stained_light_blue
        40708: [255, 255, 0],   // hardened_clay_stained_yellow
        40709: [0, 255, 0],     // hardened_clay_stained_lime
        40710: [255, 192, 203], // hardened_clay_stained_pink
        40711: [128, 128, 128], // hardened_clay_stained_gray
        40712: [192, 192, 192], // hardened_clay_stained_silver
        40713: [0, 255, 255],   // hardened_clay_stained_cyan
        40714: [128, 0, 128],   // hardened_clay_stained_purple
        40715: [0, 0, 255],     // hardened_clay_stained_blue
        40716: [139, 69, 19],   // hardened_clay_stained_brown
        40717: [0, 128, 0],     // hardened_clay_stained_green
        40718: [255, 0, 0],     // hardened_clay_stained_red
        40719: [0, 0, 0],       // hardened_clay_stained_black

        // wool_colored colors
        8960: [255, 255, 255],  // wool_colored_white
        8961: [255, 165, 0],    // wool_colored_orange
        8962: [255, 0, 255],    // wool_colored_magenta
        8963: [173, 216, 230],  // wool_colored_light_blue
        8964: [255, 255, 0],    // wool_colored_yellow
        8965: [0, 255, 0],      // wool_colored_lime
        8966: [255, 192, 203],  // wool_colored_pink
        8967: [128, 128, 128],  // wool_colored_gray
        8968: [192, 192, 192],  // wool_colored_silver
        8969: [0, 255, 255],    // wool_colored_cyan
        8970: [128, 0, 128],    // wool_colored_purple
        8971: [0, 0, 255],      // wool_colored_blue
        8972: [139, 69, 19],    // wool_colored_brown
        8973: [0, 128, 0],      // wool_colored_green
        8974: [255, 0, 0],      // wool_colored_red
        8975: [0, 0, 0],        // wool_colored_black

        // other blocks
        5632: [0, 0, 255],      // lapis block
        22272: [255, 0, 0],     // netherrack
        4352: [139, 69, 19],    // log oak
        4353: [160, 82, 45],    // log spruce
        4354: [233, 194, 166],  // log birch
        4355: [210, 105, 30],   // log jungle
        31232: [0, 0, 0],       // dragon egg
        55296: [255, 255, 255], // bone block
        52736: [255, 255, 255], // end bricks
        30976: [255, 255, 255], // end stone
        11273: [255, 255, 160], // sandstone top
        45824: [244, 164, 96],  // red sandstone normal
        3073: [244, 164, 96],   // red sand
        3072: [194, 178, 128],  // sand
        54528: [255, 69, 0],    // magma
        54784: [255, 0, 0],     // nether wart block
        42240: [0, 255, 0],     // slime
        2816: [255, 69, 0],     // lava
        20224: [173, 216, 230], // ice
        20480: [255, 255, 255], // snow
        14592: [0, 255, 255],   // diamond block
        12544: [0, 0, 0],       // obsidian
        32256: [139, 69, 19],   // wooden slab
        22016: [255, 165, 0]    // pumpkin

    };

    // RGB'den HSV'ye dönüştürme fonksiyonu
    function rgbToHsv(r, g, b) {
        let max = Math.max(r, g, b), min = Math.min(r, g, b);
        let h, s, v = max;

        let d = max - min;
        s = max == 0 ? 0 : d / max;

        if (max == min) {
            h = 0; // achromatic
        } else {
            switch (max) {
                case r: h = (g - b) / d + (g < b ? 6 : 0); break;
                case g: h = (b - r) / d + 2; break;
                case b: h = (r - g) / d + 4; break;
            }
            h /= 6;
        }

        return [h * 255, s * 255, v]; // 0-255 aralığına ölçeklendir
    }

    // HSV'ye göre en yakın blok ID'sini bulma fonksiyonu (toleranslı)
    function getBlockIdByColorHSV(h, s, v, tolerance = 30) {
        let closestBlockId = null;
        let closestDistance = Infinity;

        for (const [blockId, colorHSV] of Object.entries(blockColorsHSV)) {
            const [ch, cs, cv] = colorHSV;
            const distance = Math.sqrt(
                Math.pow(h - ch, 2) + Math.pow(s - cs, 2) + Math.pow(v - cv, 2)
            );

            if (distance < closestDistance) {
                closestDistance = distance;
                closestBlockId = parseInt(blockId);
            }
        }
        return closestDistance <= tolerance ? closestBlockId : null;
    }

    // Koordinatları iç pozisyona çeviren fonksiyon
    function coordsToInsidePos(worldCoords, chunkCoords) {
        const [chunkX, chunkY, chunkZ] = chunkCoords;
        const x = Math.floor(worldCoords.x / 5) - chunkX * 32;
        const y = Math.floor(worldCoords.y / 5) - chunkY * 32;
        const z = Math.floor(worldCoords.z / 5) - chunkZ * 32;
        return x + y * 32 + z * 32 * 32;
    }

    // Blok ekleme fonksiyonu
    function addBlock(x, y, z, blockId) {
        let pkt = new a234(); // Buradaki a234 sınıfının tanımını kontrol edin. Craftnite'a özel bir sınıf olabilir.
        pkt.i = Math.floor(x / 160);
        pkt.e = Math.floor(y / 160);
        pkt.o = Math.floor(z / 160);
        pkt.v = coordsToInsidePos({ x: x, y: y, z: z }, [pkt.i, pkt.e, pkt.o]);
        pkt.u = blockId;
        G.socket.send(pkt.a614()); // Buradaki a614 fonksiyonunun tanımını kontrol edin. Craftnite'a özel bir fonksiyon olabilir.
    }

    // Yükleme yüzdesi göstergesini oluşturma fonksiyonu
    function createProgressIndicator() {
        const progressIndicator = document.createElement('div');
        progressIndicator.id = 'progress-indicator';
        progressIndicator.style.position = 'fixed';
        progressIndicator.style.top = '10px';
        progressIndicator.style.left = '50%';
        progressIndicator.style.transform = 'translateX(-50%)';
        progressIndicator.style.padding = '10px';
        progressIndicator.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
        progressIndicator.style.color = 'white';
        progressIndicator.style.fontSize = '16px';
        progressIndicator.style.zIndex = '1000';
        progressIndicator.style.display = 'none';
        document.body.appendChild(progressIndicator);
    }

    // Yüzdeyi güncelleme fonksiyonu
    function updateProgress(percentage) {
        const progressIndicator = document.getElementById('progress-indicator');
        progressIndicator.style.display = 'block';
        progressIndicator.innerText = `Loading: ${percentage.toFixed(2)}%`;

        if (percentage >= 100) {
            setTimeout(() => {
                progressIndicator.style.display = 'none';
            }, 1000);
        }
    }

    // OBJ verilerini ayrıştırma fonksiyonu
    function parseOBJData(content) {
        const lines = content.split('\n').filter(line => line.trim() !== '');
        const vertices = [];
        const faces = [];

        lines.forEach(line => {
            const parts = line.trim().split(/\s+/);
            if (parts[0] === 'v') {
                const [x, y, z] = parts.slice(1).map(parseFloat);
                vertices.push([x, y, z]);
            } else if (parts[0] === 'f') {
                const indices = parts.slice(1).map(index => {
                    const parsedIndex = parseInt(index) - 1;
                    if (isNaN(parsedIndex) || parsedIndex < 0 || parsedIndex >= vertices.length) {
                        console.error("Invalid vertex index found:", index);
                        return null;
                    }
                    return parsedIndex;
                }).filter(index => index !== null);
                if (indices.length >= 3) {
                    faces.push(indices);
                } else {
                    console.warn("Yüzde 3'ten az köşe var. Atlanıyor:", indices);
                }
            }
        });

        return { vertices, faces };
    }

    // OBJ boyutlarını hesaplama fonksiyonu
    function calculateObjectBounds(vertices) {
        let minX = Infinity, maxX = -Infinity;
        let minY = Infinity, maxY = -Infinity;
        let minZ = Infinity, maxZ = -Infinity;

        for (const [x, y, z] of vertices) {
            minX = Math.min(minX, x);
            maxX = Math.max(maxX, x);
            minY = Math.min(minY, y);
            maxY = Math.max(maxY, y);
            minZ = Math.min(minZ, z);
            maxZ = Math.max(maxZ, z);
        }

        return {
            width: maxX - minX,
            height: maxY - minY,
            depth: maxZ - minZ
        };
    }

    // Dosya yükleme ve okuma (scaleFactor burada tanımlanıyor)
    const input = document.createElement('input');
    input.type = 'file';
    input.accept = '.obj';
    input.style.display = 'none';

    input.addEventListener('change', function(event) {
        const file = event.target.files[0];
        const reader = new FileReader();

        reader.onload = function(e) {
            const content = e.target.result;
            console.log('OBJ file content:', content);

            const { vertices, faces } = parseOBJData(content);
            const playerPos = GAME.a865.player.position.clone();

            const objBounds = calculateObjectBounds(vertices);
            const centerX = playerPos.x - objBounds.width / 2;
            const centerY = playerPos.y - objBounds.height / 2;
            const centerZ = playerPos.z - objBounds.depth / 2;

            const centeredPlayerPos = { x: centerX, y: centerY, z: centerZ };

            // Ölçeklendirme faktörünü al ve işleme başla
            const scaleInput = prompt("Please enter the scale factor for the OBJ file:", "1.0");
            const scaleFactor = parseFloat(scaleInput);

            if (isNaN(scaleFactor) || scaleFactor <= 0) {
                alert("Invalid scale factor! Please enter a positive number.");
                return;
            }

            processOBJ(content, centeredPlayerPos, scaleFactor, vertices, faces);
        };

        reader.readAsText(file);
    });

    function processOBJ(content, playerPos, scaleFactor, vertices, faces) {
        const scaledVertices = vertices.map(([x, y, z]) => [
            playerPos.x + x * scaleFactor,
            playerPos.y + y * scaleFactor,
            playerPos.z + z * scaleFactor
        ]);

        placeBlocksInChunks(scaledVertices, faces);
    }

    function placeBlocksInChunks(vertices, faces) {
        let chunkIndex = 0;
        const chunkSize = 5;

        const intervalId = setInterval(() => {
            if (chunkIndex >= faces.length) {
                clearInterval(intervalId);
                console.log("OBJ file processing complete.");
                return;
            }

            const chunk = faces.slice(chunkIndex, chunkIndex + chunkSize);
            chunk.forEach(face => {
                face.forEach(vertexIndex => {
                    if (isNaN(vertexIndex) || vertexIndex < 0 || vertexIndex >= vertices.length) {
                        console.error("Invalid vertex index found:", vertexIndex);
                        return;
                    }

                    const vertex = vertices[vertexIndex];
                    if (vertex) {
                        const [x, y, z] = vertex;
                        const r = Math.abs(x % 256);
                        const g = Math.abs(y % 256);
                        const b = Math.abs(z % 256);
                        const [h, s, v] = rgbToHsv(r, g, b);

                        const blockId = getBlockIdByColorHSV(h, s, v);

                        if (blockId) {
                            addBlock(x, y, z, blockId);
                        } else {
                            const defaultBlockId = 64263; // Gri blok ID'si (veya istediğiniz bir varsayılan renk)
                            addBlock(x, y, z, defaultBlockId);
                            console.log("Eşleşen renk bulunamadı:", r, g, b, "Varsayılan renk kullanıldı:", defaultBlockId);
                        }

                    } else {
                        console.error("Vertex index out of bounds:", vertexIndex);
                    }
                });
            });

            chunkIndex += chunkSize;

            const percentage = Math.min(100, (chunkIndex / faces.length) * 100);
            updateProgress(percentage);
        }, 70);
    }

    // Dosya yükleme düğmesi ekleyin
    const button = document.createElement('button');
    button.innerText = 'Upload OBJ File';
    button.style.position = 'fixed';
    button.style.right = '0';
    button.style.bottom = '50%';
    button.style.padding = '10px 20px';
    button.style.fontSize = '16px';
    button.style.cursor = 'pointer';
    button.style.zIndex = '1000';
    button.addEventListener('click', function() {
        input.click();
    });

    document.body.appendChild(button);

    // Yükleme yüzdesi göstergesini oluştur
    createProgressIndicator();

    // ... (diğer fonksiyonlar aynı kalır)

})();