minegenpls / app.py
dedlepexa's picture
Update app.py
7d86b21 verified
Raw
History Blame Contribute Delete
6.83 kB
import gradio as gr
import spaces
import numpy as np
import noise
import random
import json
import uuid
import os
import tempfile
from fastapi import FastAPI
from fastapi.responses import JSONResponse
# ---------- Генератор мира ----------
def generate_world(seed: int, size: int):
np.random.seed(seed)
random.seed(seed)
scale = 30.0
octaves = 6
persistence = 0.5
lacunarity = 2.0
temp_noise = np.zeros((size, size))
moist_noise = np.zeros((size, size))
for x in range(size):
for z in range(size):
temp_noise[x][z] = noise.pnoise2(x/scale/2+seed, z/scale/2+seed, octaves=4)
moist_noise[x][z] = noise.pnoise2(x/scale/2+seed*2, z/scale/2+seed*2, octaves=4)
temp_noise = (temp_noise - temp_noise.min()) / (temp_noise.max() - temp_noise.min() + 1e-8)
moist_noise = (moist_noise - moist_noise.min()) / (moist_noise.max() - moist_noise.min() + 1e-8)
world_blocks = []
for x in range(size):
for z in range(size):
h = noise.pnoise2(x/scale+seed, z/scale+seed, octaves=octaves,
persistence=persistence, lacunarity=lacunarity)
c = noise.pnoise2(x/scale*2+seed*3, z/scale*2+seed*3, octaves=4)
h = h * 0.7 + c * 0.3
height_norm = (h + 1) / 2.0
t = temp_noise[x][z]
m = moist_noise[x][z]
water_level = 0.45
mountain_threshold = 0.75
y_max = int(10 + height_norm * 15)
for y in range(0, y_max + 1):
if y == 0:
block = "bedrock"
elif y <= 3:
block = "stone"
elif y <= y_max - 4:
block = "dirt"
else:
if height_norm < water_level:
block = "water" if y_max <= 12 else "sand"
elif height_norm > mountain_threshold:
block = "stone" if y == y_max else "dirt"
else:
if t < 0.3 and m < 0.3:
block = "sand" if y == y_max else "sandstone"
elif t < 0.3 and m > 0.6:
block = "grass" if y == y_max else "dirt"
elif t > 0.7 and m > 0.5:
block = "grass" if y == y_max else "dirt"
if y == y_max and random.random() < 0.1:
_make_tree(world_blocks, x, y, z)
else:
block = "grass" if y == y_max else "dirt"
if block != "bedrock":
world_blocks.append({"x": x, "y": y, "z": z, "block": block})
world_blocks.sort(key=lambda b: (b["y"], b["x"], b["z"]))
return world_blocks
def _make_tree(blocks_list, x, y, z, trunk_height=3):
for dy in range(1, trunk_height+1):
blocks_list.append({"x": x, "y": y+dy, "z": z, "block": "wood"})
for dx in (-1, 0, 1):
for dz in (-1, 0, 1):
for dy in range(trunk_height, trunk_height+2):
if abs(dx) == 1 and abs(dz) == 1:
continue
blocks_list.append({"x": x+dx, "y": y+dy, "z": z+dz, "block": "leaves"})
def chunk_world(blocks):
full_json = json.dumps(blocks, ensure_ascii=False)
max_len = 20000
if len(full_json) <= max_len:
return [full_json]
parts = []
start = 0
while start < len(blocks):
chunk_blocks = []
current_len = 1
idx = start
while idx < len(blocks):
block_str = json.dumps(blocks[idx], ensure_ascii=False)
extra = 0 if not chunk_blocks else 1
if current_len + len(block_str) + extra + 1 > max_len:
break
chunk_blocks.append(blocks[idx])
current_len += len(block_str) + (1 if chunk_blocks else 0)
idx += 1
if not chunk_blocks:
raise RuntimeError("Block too large for chunk")
parts.append(json.dumps(chunk_blocks, ensure_ascii=False))
start = idx
return parts
# ---------- GPU только для генерации ----------
@spaces.GPU
def generate(seed: int, size: int):
world = generate_world(seed, size)
chunks = chunk_world(world)
session_id = str(uuid.uuid4())
filepath = os.path.join(tempfile.gettempdir(), f"mc_session_{session_id}.json")
with open(filepath, "w", encoding="utf-8") as f:
json.dump({"chunks": chunks, "total_parts": len(chunks), "seed": seed, "size": size}, f)
return {"session_id": session_id, "total_parts": len(chunks)}
# ---------- Функция получения части (CPU, без очереди) ----------
def get_part(session_id: str, part_num: int):
part_num = int(part_num)
filepath = os.path.join(
tempfile.gettempdir(),
f"mc_session_{session_id}.json"
)
if not os.path.exists(filepath):
return {
"error": "Session not found"
}
with open(filepath, "r", encoding="utf-8") as f:
data = json.load(f)
if part_num < 0 or part_num >= data["total_parts"]:
return {
"error": "Invalid part number",
"max": data["total_parts"]
}
return json.loads(
data["chunks"][part_num]
)
# ---------- Gradio UI ----------
with gr.Blocks() as demo:
gr.Markdown(
"# 🟫 Minecraft World Generator (ZeroGPU)"
)
with gr.Tab("Generate"):
seed_in = gr.Number(
value=42,
label="Seed",
precision=0
)
size_in = gr.Slider(
minimum=20,
maximum=50,
value=35,
step=5,
label="Size"
)
gen_btn = gr.Button(
"Generate World"
)
gen_out = gr.JSON(
label="Result"
)
with gr.Tab("Get Part"):
sess_in = gr.Textbox(
label="Session ID"
)
part_in = gr.Number(
value=0,
label="Part Number",
precision=0
)
get_btn = gr.Button(
"Get Part"
)
part_out = gr.JSON(
label="Chunk"
)
# GPU генерация
gen_btn.click(
fn=generate,
inputs=[
seed_in,
size_in
],
outputs=gen_out
)
# CPU получение блока
get_btn.click(
fn=get_part,
inputs=[
sess_in,
part_in
],
outputs=part_out
)
app = demo.app
@app.get("/api/get_part")
def api_get_part(session_id: str, part_num: int):
return JSONResponse(
content=get_part(session_id, part_num)
)
demo.launch()