mirror of
https://github.com/zhenyan121/Cubed.git
synced 2026-06-17 16:17:02 +08:00
feat: grass (#11)
* feat: add grass texture and update grass_block texture * feat: add block data * feat: add blocks_tool * feat: add sync info and change function in blocks_tools * feat: add check and new function * refactor: make block texture loading data-driven * feat: add rendering for grass * feat: passable grass * feat: random grass place * fix: memory leak in TextureManager::load_cross_plane_texture
This commit is contained in:
403
scripts/blocks_tool.py
Normal file
403
scripts/blocks_tool.py
Normal file
@@ -0,0 +1,403 @@
|
||||
import argparse
|
||||
import copy
|
||||
import sys
|
||||
from functools import singledispatch
|
||||
from pathlib import Path
|
||||
from pprint import pprint
|
||||
from typing import Any
|
||||
|
||||
import pytomlpp
|
||||
from loguru import logger
|
||||
|
||||
VERSION = "0.0.1"
|
||||
DATA_PATH = "assets/data/block"
|
||||
TEXTURE_PATH = "assets/texture/block"
|
||||
|
||||
work_path = Path(__file__).parent.parent
|
||||
data_path = work_path / DATA_PATH
|
||||
texture_path = work_path / TEXTURE_PATH
|
||||
|
||||
|
||||
def collect_blocks() -> list[dict[str, Any]]:
|
||||
blocks: list[dict[str, Any]] = []
|
||||
for block in data_path.rglob("*.toml"):
|
||||
if not block.is_file():
|
||||
continue
|
||||
if block.name == "template.toml":
|
||||
continue
|
||||
|
||||
blocks.append(pytomlpp.loads(block.read_text(encoding="utf-8")))
|
||||
blocks.sort(key=lambda x: x["id"])
|
||||
return blocks
|
||||
|
||||
|
||||
def save_data(blocks: list[dict[str, Any]]):
|
||||
for block in blocks:
|
||||
block_path: Path = data_path / (block["name"] + ".toml")
|
||||
if not block_path.is_file():
|
||||
logger.warning(
|
||||
f"Block: {block_path} is not Exists and Will Create A New One"
|
||||
)
|
||||
block_path.write_text(pytomlpp.dumps(block))
|
||||
|
||||
|
||||
def sync_template_value():
|
||||
blocks = collect_blocks()
|
||||
template_path = data_path / "template.toml"
|
||||
if not template_path.is_file():
|
||||
logger.error("Template.toml is not Exists!")
|
||||
return
|
||||
template_block = pytomlpp.loads(template_path.read_text(encoding="utf-8"))
|
||||
add_count = 0
|
||||
for key, value in template_block.items():
|
||||
for block in blocks:
|
||||
if key not in block:
|
||||
block[key] = value
|
||||
add_count += 1
|
||||
save_data(blocks)
|
||||
logger.info(f"Synced {add_count} template fields to blocks")
|
||||
|
||||
|
||||
@singledispatch
|
||||
def show_data_info(arg: Any):
|
||||
logger.error("No Match show_data_info")
|
||||
|
||||
|
||||
@show_data_info.register(type(None))
|
||||
def _(arg: None):
|
||||
blocks = collect_blocks()
|
||||
print("Please Input Block Name or Id, Input exit or e to Exit")
|
||||
while True:
|
||||
input_str = input("Name or Id: ")
|
||||
try:
|
||||
id = int(input_str)
|
||||
if id >= len(blocks) or id < 0:
|
||||
print(f"Id: {id} Not Find, Input e or exit to Exit")
|
||||
continue
|
||||
pprint(blocks[id])
|
||||
except ValueError:
|
||||
if input_str.lower() == "exit" or input_str.lower() == "e":
|
||||
break
|
||||
find = False
|
||||
for block in blocks:
|
||||
if block["name"] == input_str:
|
||||
pprint(block)
|
||||
find = True
|
||||
break
|
||||
if not find:
|
||||
print(f"Name: {input_str} Not Find, Input e or exit to Exit")
|
||||
|
||||
|
||||
@show_data_info.register(int)
|
||||
def _(id: int):
|
||||
blocks = collect_blocks()
|
||||
if id >= len(blocks) or id < 0:
|
||||
logger.error(f"ID: {id} is Not Invaild!")
|
||||
return
|
||||
pprint(blocks[id])
|
||||
|
||||
|
||||
@show_data_info.register(str)
|
||||
def _(name: str):
|
||||
blocks = collect_blocks()
|
||||
find = False
|
||||
for block in blocks:
|
||||
if block["name"] == name:
|
||||
pprint(block)
|
||||
find = True
|
||||
break
|
||||
if not find:
|
||||
logger.error(f"Block Name: {name} Not Find")
|
||||
|
||||
|
||||
def change_key(block: dict[str, Any], key: str, value: str):
|
||||
if type(block[key]) is str:
|
||||
block[key] = value
|
||||
elif type(block[key]) is int:
|
||||
try:
|
||||
v = int(value)
|
||||
block[key] = v
|
||||
except ValueError:
|
||||
logger.error("The Value Is Not A Int")
|
||||
return False
|
||||
elif type(block[key]) is bool:
|
||||
if value.lower() == "true" or value.lower() == "t":
|
||||
block[key] = True
|
||||
elif value.lower() == "false" or value.lower() == "f":
|
||||
block[key] = False
|
||||
else:
|
||||
logger.error("The Value Is Not A Bool")
|
||||
return False
|
||||
elif type(block[key]) is float:
|
||||
try:
|
||||
v = float(value)
|
||||
block[key] = v
|
||||
except ValueError:
|
||||
logger.error("The Value Is Not A Float")
|
||||
return False
|
||||
else:
|
||||
logger.error("Unkown Key Type")
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def handle_change(block: dict[str, Any]) -> dict[str, Any]:
|
||||
print("Please Input Block Key, Input exit or e to Exit")
|
||||
while True:
|
||||
key = input("Key: ")
|
||||
if key.lower() == "exit" or key.lower() == "e":
|
||||
break
|
||||
if key not in block:
|
||||
logger.error("The Key Is Not Exists!")
|
||||
continue
|
||||
value = input("Value: ")
|
||||
old_name = block[key]
|
||||
if change_key(block, key, value):
|
||||
print("Change Success")
|
||||
if key == "name":
|
||||
old_path: Path = data_path / (old_name + ".toml")
|
||||
try:
|
||||
old_path.unlink()
|
||||
except FileNotFoundError:
|
||||
logger.warning(
|
||||
f"Name Change But Old File {old_name}.toml is Not Exists!"
|
||||
)
|
||||
else:
|
||||
print("Change Fail")
|
||||
pprint(block)
|
||||
return block
|
||||
|
||||
|
||||
@singledispatch
|
||||
def change_data(arg: Any):
|
||||
logger.error("Not Match change")
|
||||
|
||||
|
||||
@change_data.register(int)
|
||||
def _(id: int):
|
||||
blocks = collect_blocks()
|
||||
if id >= len(blocks) or id < 0:
|
||||
logger.error(f"ID: {id} is Invaild!")
|
||||
return
|
||||
pprint(blocks[id])
|
||||
blocks[id] = handle_change(blocks[id])
|
||||
save_data(blocks)
|
||||
|
||||
|
||||
@change_data.register(str)
|
||||
def _(name: str):
|
||||
blocks = collect_blocks()
|
||||
find = False
|
||||
for i, block in enumerate(blocks):
|
||||
if block["name"] == name:
|
||||
pprint(block)
|
||||
blocks[i] = handle_change(block)
|
||||
save_data(blocks)
|
||||
find = True
|
||||
break
|
||||
if not find:
|
||||
logger.error(f"Block Name: {name} Not Find")
|
||||
|
||||
|
||||
@change_data.register(type(None))
|
||||
def _(arg: None):
|
||||
blocks = collect_blocks()
|
||||
print("Please Input Block Name or Id, Input exit or e to Exit")
|
||||
while True:
|
||||
input_str = input("Name or Id: ")
|
||||
try:
|
||||
id = int(input_str)
|
||||
if id >= len(blocks) or id < 0:
|
||||
print(f"Id: {id} Not Find, Input e or exit to Exit")
|
||||
continue
|
||||
pprint(blocks[id])
|
||||
blocks[id] = handle_change(blocks[id])
|
||||
save_data(blocks)
|
||||
except ValueError:
|
||||
if input_str.lower() == "exit" or input_str.lower() == "e":
|
||||
break
|
||||
find = False
|
||||
for i, block in enumerate(blocks):
|
||||
if block["name"] == input_str:
|
||||
pprint(block)
|
||||
blocks[i] = handle_change(block)
|
||||
save_data(blocks)
|
||||
find = True
|
||||
break
|
||||
if not find:
|
||||
print(f"Name: {input_str} Not Find, Input e or exit to Exit")
|
||||
|
||||
|
||||
def show_data_list():
|
||||
blocks = collect_blocks()
|
||||
for block in blocks:
|
||||
print(f"id: {block['id']} name: {block['name']}")
|
||||
|
||||
|
||||
def check_path():
|
||||
|
||||
logger.info(f"Work Path {work_path.resolve()}")
|
||||
logger.info(f"Script Dir {sys.path[0]}")
|
||||
data_exists = True
|
||||
if not data_path.exists():
|
||||
logger.error(f"Blocks Data Path {data_path} not Exists!")
|
||||
data_exists = False
|
||||
else:
|
||||
logger.info(f"Blocks Data Path {data_path}")
|
||||
texture_exists = True
|
||||
if not texture_path.exists():
|
||||
logger.error(f"Blocks Texture Path {texture_path} not Exists!")
|
||||
texture_exists = False
|
||||
else:
|
||||
logger.info(f"Blocks Texture Path {texture_path}")
|
||||
return data_exists and texture_exists
|
||||
|
||||
|
||||
def check_integrity():
|
||||
find_error = False
|
||||
errors = 0
|
||||
if check_path():
|
||||
blocks = collect_blocks()
|
||||
template_path = data_path / "template.toml"
|
||||
if not template_path.is_file():
|
||||
logger.error("Template.toml is not Exists!")
|
||||
find_error = True
|
||||
errors += 1
|
||||
return
|
||||
template_block = pytomlpp.loads(template_path.read_text(encoding="utf-8"))
|
||||
n = len(blocks)
|
||||
for i in range(n):
|
||||
if "id" not in blocks[i]:
|
||||
logger.error(f"Id: {i} not Exists!")
|
||||
find_error = True
|
||||
errors += 1
|
||||
continue
|
||||
if blocks[i]["id"] != i:
|
||||
logger.error(
|
||||
f"Id Error, Block {blocks[i].get('name', 'Unknow')} Id Should Be {i} Instead of {blocks[i]['id']}"
|
||||
)
|
||||
find_error = True
|
||||
errors += 1
|
||||
for key, value in template_block.items():
|
||||
if key not in blocks[i]:
|
||||
logger.error(
|
||||
f"Key Error, Block {blocks[i].get('name', 'Unknow')} Key {key} not Exists!"
|
||||
)
|
||||
find_error = True
|
||||
errors += 1
|
||||
continue
|
||||
if type(blocks[i][key]) is not type(value):
|
||||
logger.error(
|
||||
f"Value Type Error, Block {blocks[i].get(key, 'Unknow')} The Type Should Be {type(value)}, Instead of {type(blocks[i][key])}"
|
||||
)
|
||||
find_error = True
|
||||
errors += 1
|
||||
if find_error:
|
||||
logger.error(f"Find {errors} Errors")
|
||||
else:
|
||||
logger.info("No Error")
|
||||
|
||||
|
||||
def add_new_block():
|
||||
blocks = collect_blocks()
|
||||
template_path = data_path / "template.toml"
|
||||
if not template_path.is_file():
|
||||
logger.error("Template.toml is not Exists, Can't Create A New Block!")
|
||||
return
|
||||
template_block = pytomlpp.loads(template_path.read_text(encoding="utf-8"))
|
||||
new_block = copy.deepcopy(template_block)
|
||||
num = len(blocks)
|
||||
logger.info(f"New Block Id is {num}")
|
||||
new_block["id"] = num
|
||||
for key in template_block:
|
||||
if key == "id":
|
||||
continue
|
||||
nvalue = input(f"Input {key}: ")
|
||||
if not change_key(new_block, key, nvalue):
|
||||
logger.error(f"Add Key {key} Value {nvalue} Fail")
|
||||
return
|
||||
new_block_path: Path = data_path / (new_block["name"] + ".toml")
|
||||
new_block_path.write_text(pytomlpp.dumps(new_block))
|
||||
logger.info("Successfully Add New Block!")
|
||||
pprint(new_block)
|
||||
|
||||
|
||||
def handle_args(args: argparse.Namespace):
|
||||
if args.version:
|
||||
print(f"Blocks Tools: {VERSION}")
|
||||
print(f"Python: {sys.version}")
|
||||
if args.path:
|
||||
check_path()
|
||||
if args.list:
|
||||
show_data_list()
|
||||
if args.sync:
|
||||
sync_template_value()
|
||||
if args.info:
|
||||
if args.info == "EMPTY":
|
||||
show_data_info(None)
|
||||
else:
|
||||
try:
|
||||
id = int(args.info)
|
||||
show_data_info(id)
|
||||
except ValueError:
|
||||
show_data_info(args.info)
|
||||
if args.change:
|
||||
if args.change == "EMPTY":
|
||||
change_data(None)
|
||||
else:
|
||||
try:
|
||||
id = int(args.change)
|
||||
change_data(id)
|
||||
except ValueError:
|
||||
change_data(args.change)
|
||||
if args.check:
|
||||
check_integrity()
|
||||
if args.new:
|
||||
add_new_block()
|
||||
|
||||
|
||||
def init_parser(parser: argparse.ArgumentParser):
|
||||
parser.add_argument(
|
||||
"-v", "--version", action="store_true", help="Show Blocks Tools Version"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--path", action="store_true", help="Check Blcoks Data and Texture Path"
|
||||
)
|
||||
parser.add_argument("-l", "--list", action="store_true", help="Show Blocks List")
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--sync",
|
||||
action="store_true",
|
||||
help="Sync Template.toml Value to Other Toml, Only New Value Will Add",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
"--info",
|
||||
nargs="?",
|
||||
const="EMPTY",
|
||||
help="Show Block Data, If Provide Id Will Print the Corresponding Blcok Data, You Can Input Id or Name",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c", "--change", nargs="?", const="EMPTY", help="Change Block Data"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-C", "--check", action="store_true", help="Check The Block Data Integrity"
|
||||
)
|
||||
parser.add_argument("-n", "--new", action="store_true", help="Add A New Block")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Block Manage Tool")
|
||||
|
||||
init_parser(parser)
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
parser.print_help()
|
||||
exit(0)
|
||||
|
||||
args = parser.parse_args()
|
||||
handle_args(args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user