MC 建筑转换器 v3.6

纯前端运行 / 无需安装 / 隐私安全 / 离线可用

Pyodide WebAssembly schematic & litematic 24 敏感词规则
0%
正在连接...
正在加载 Pyodide Python 引擎...
拖放 .schematic / .litematic 文件到此处
支持 .schematic  |  .litematic  |  最大 100MB
方块数 -
压缩条目 -
总指令 -
压缩率 -
手动指令 -
下载指令文件
日志
指令
预览
作者: 小逸罒不迷茫  |  B站: space.bilibili.com/3546638146603426
侵权必究  — 本程序源代码严禁转载、修改后分发或用于商业用途
"""'); loadProgress(95,'转换引擎已加载'); loadProgress(100,'就绪'); setTimeout(function(){ var o=document.getElementById('loadOverlay'); o.style.transition='opacity .4s'; o.style.opacity='0'; setTimeout(function(){o.style.display='none'},400); },400); setStatus('已就绪,上传文件开始转换','ready'); ready=true; }catch(e){ loadProgress(100,'失败'); document.getElementById('loadText').textContent='加载失败: '+e.message; document.getElementById('loadText').style.color='#e44'; setStatus('加载失败: '+e.message,'error'); } } init(); // Drag & Drop var dz=document.getElementById('dropZone'),fi=document.getElementById('fileInput'); dz.onclick=function(){if(ready)fi.click()}; fi.onchange=function(){if(fi.files[0])enableBtns()}; dz.ondragover=function(e){e.preventDefault();dz.classList.add('drag')}; dz.ondragleave=function(){dz.classList.remove('drag')}; dz.ondrop=function(e){ e.preventDefault();dz.classList.remove('drag'); if(e.dataTransfer.files[0]){fi.files=e.dataTransfer.files;enableBtns()} }; function enableBtns(){ var ok=fi.files.length>0; document.getElementById('previewBtn').disabled=!ok;document.getElementById('previewBtn').style.opacity=ok?'1':'.4'; document.getElementById('convertBtn').disabled=!ok;document.getElementById('convertBtn').style.opacity=ok?'1':'.4'; } function resetBtns(){ document.getElementById('previewBtn').disabled=true;document.getElementById('previewBtn').style.opacity='.4'; document.getElementById('convertBtn').disabled=true;document.getElementById('convertBtn').style.opacity='.4'; } function resetUI(){ document.getElementById('result').style.display='none'; logEl.style.display='none';logEl.textContent=''; } async function doPreview(){ var file=fi.files[0];if(!file||!ready)return; var ext=file.name.split('.').pop().toLowerCase(); if(!['schematic','litematic'].includes(ext)){log('仅支持 .schematic / .litematic');return} log('正在解析 '+file.name+' 生成预览...'); setStatus('解析中...'); var buf=await file.arrayBuffer(); pyodide.FS.writeFile('/_preview.'+ext,new Uint8Array(buf),{encoding:'binary'}); var r=pyodide.runPython('_gen_preview("/_preview.'+ext+'","'+file.name.replace(/[^a-zA-Z0-9_.-]/g,'_')+'")'); if(r===undefined){log('预览生成失败');return} var u=URL.createObjectURL(new Blob([r],{type:'text/html'})); document.getElementById('previewFrame').src=u; switchTab('prev'); setStatus('预览已生成'); } async function doConvert(){ convert(fi.files[0]); } async function convert(file){ if(!ready){log('引擎还未就绪,请稍候...');return} var ext=file.name.split('.').pop().toLowerCase(); if(!['schematic','litematic'].includes(ext)){log('仅支持 .schematic / .litematic');return} resetUI(); log('文件: '+file.name+' ('+(file.size/1024).toFixed(1)+' KB)'); setStatus('正在转换 '+file.name+'...'); var pr=document.getElementById('progress');pr.style.display='block';pr.firstChild.style.width='30%'; var buf=await file.arrayBuffer(); pyodide.FS.writeFile('/input.'+ext,new Uint8Array(buf),{encoding:'binary'}); pr.firstChild.style.width='60%'; try{ pyodide.runPython(['','import json,traceback,io,sys', 'def _web_run(path):', ' old=sys.stdout;sys.stdout=io.StringIO()', ' try:', ' r=convert_schematic(None,path)', ' finally:', ' sys.stdout=old', ' return json.dumps(r,ensure_ascii=False)', ].join(String.fromCharCode(10))); var r=pyodide.runPython('_web_run("/input.'+ext+'")'); pr.firstChild.style.width='100%'; if(r===undefined){log('Python 返回空,转换失败');return} var d=JSON.parse(r); if(d.error){log('Error: '+d.error);if(d.trace)log(d.trace);setStatus('转换失败','error');return} log(d.log||''); var manual=0; if(d.commands){ var lines=d.commands.split('\n'); for(var l of lines){if(l.startsWith('setblock')||l.startsWith('fill'))manual++} document.getElementById('cmdBox').textContent=d.commands; switchTab('cmd'); } if(d.previewUrl) document.getElementById('previewFrame').src=d.previewUrl; document.getElementById('statBlocks').textContent=d.blocks; document.getElementById('statComp').textContent=d.compressed; document.getElementById('statCmds').textContent=d.total_cmds; document.getElementById('statRatio').textContent=((1-d.compressed/d.blocks)*100).toFixed(1)+'%'; document.getElementById('statManual').textContent=manual; var blob=new Blob([d.commands],{type:'text/plain;charset=utf-8'}); var a=document.getElementById('dlLink'); a.href=URL.createObjectURL(blob); a.download=file.name.replace(/\.[^.]+$/,'')+'_commands.txt'; document.getElementById('result').style.display='block'; pr.style.display='none'; setStatus('转换完成! '+d.total_cmds+' 条指令 (手动setblock: '+manual+')'); }catch(e){log('Error: '+e.message);setStatus('转换出错','error')} finally{pr.style.display='none';dz.style.display='block'} }