Compare commits

..

2 Commits

Author SHA1 Message Date
d748e242db manual fix 2026-04-28 14:09:45 +08:00
e2fd0809a5 PBR 2026-04-28 01:09:17 +08:00
6 changed files with 151 additions and 70 deletions

View File

@ -1,7 +1,6 @@
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
import os import os
import asyncio import asyncio
from loguru import logger
import sqlparse import sqlparse
from pathlib import Path from pathlib import Path
from typing import List, Dict, Any, Optional, Union, TYPE_CHECKING from typing import List, Dict, Any, Optional, Union, TYPE_CHECKING
@ -11,20 +10,10 @@ import aiosqlite
if TYPE_CHECKING: if TYPE_CHECKING:
from . import DatabaseManager from . import DatabaseManager
# 全局数据库管理器实例
_global_db_manager: Optional["DatabaseManager"] = None _global_db_manager: Optional["DatabaseManager"] = None
async def try_close_connection(conn: aiosqlite.Connection) -> bool:
try:
await conn.close()
return True
except Exception as e:
logger.error("有的连接关闭失败了")
logger.exception(e)
return False
def get_global_db_manager() -> "DatabaseManager": def get_global_db_manager() -> "DatabaseManager":
"""获取全局数据库管理器实例""" """获取全局数据库管理器实例"""
global _global_db_manager global _global_db_manager
@ -35,10 +24,16 @@ def get_global_db_manager() -> "DatabaseManager":
return _global_db_manager return _global_db_manager
def close_global_db_manager() -> None:
"""关闭全局数据库管理器实例"""
global _global_db_manager
if _global_db_manager is not None:
# 注意这个函数应该在async环境中调用close_all_connections
_global_db_manager = None
class DatabaseManager: class DatabaseManager:
""" """异步数据库管理器"""
异步数据库管理器
"""
def __init__(self, db_path: Optional[Union[str, Path]] = None, pool_size: int = 5): def __init__(self, db_path: Optional[Union[str, Path]] = None, pool_size: int = 5):
""" """
@ -61,7 +56,6 @@ class DatabaseManager:
async def _get_connection(self) -> aiosqlite.Connection: async def _get_connection(self) -> aiosqlite.Connection:
"""从连接池获取连接""" """从连接池获取连接"""
async with self._lock: async with self._lock:
# 尝试从池中获取现有连接 # 尝试从池中获取现有连接
while self._connection_pool: while self._connection_pool:
@ -73,7 +67,10 @@ class DatabaseManager:
return conn return conn
except: except:
# 连接已失效,关闭它 # 连接已失效,关闭它
await try_close_connection(conn) try:
await conn.close()
except:
pass
# 如果连接池为空,创建新连接 # 如果连接池为空,创建新连接
conn = await aiosqlite.connect(self.db_path) conn = await aiosqlite.connect(self.db_path)
@ -89,31 +86,16 @@ class DatabaseManager:
self._connection_pool.append(conn) self._connection_pool.append(conn)
else: else:
# 池已满,直接关闭连接 # 池已满,直接关闭连接
await try_close_connection(conn) try:
await conn.close()
except:
pass
@asynccontextmanager @asynccontextmanager
async def get_conn(self): async def get_conn(self):
"""
从 db 中获取一个 Connection
"""
conn = await self._get_connection() conn = await self._get_connection()
yield conn
try: await self._return_connection(conn)
yield conn
# 只有当一切正常时才归还数据库连接
await self._return_connection(conn)
except Exception as e:
logger.error("有模块使用一个连接时出现了错误")
logger.exception(e)
try:
await conn.rollback()
await conn.close()
except Exception as e:
logger.error("在 Rollback 和关闭时也出现了问题")
logger.exception(e)
async def query( async def query(
self, query: str, params: Optional[tuple] = None self, query: str, params: Optional[tuple] = None
@ -208,14 +190,42 @@ class DatabaseManager:
else: else:
await self.execute_script(script) await self.execute_script(script)
async def execute_many(self, command: str, seq_of_params: List[tuple]) -> None:
"""执行多条非查询语句"""
conn = await self._get_connection()
try:
await conn.executemany(command, seq_of_params)
await conn.commit()
except Exception as e:
await conn.rollback()
raise Exception(f"数据库批量执行失败: {str(e)}") from e
finally:
await self._return_connection(conn)
async def execute_many_values_by_sql_file(
self, file_path: Union[str, Path], seq_of_params: List[tuple]
) -> None:
"""从 SQL 文件中读取一条语句,但是被不同值同时执行"""
path = str(file_path) if isinstance(file_path, Path) else file_path
with open(path, "r", encoding="utf-8") as f:
command = f.read()
await self.execute_many(command, seq_of_params)
async def close_all_connections(self) -> None: async def close_all_connections(self) -> None:
"""关闭所有连接""" """关闭所有连接"""
async with self._lock: async with self._lock:
# 关闭池中的连接
for conn in self._connection_pool: for conn in self._connection_pool:
await try_close_connection(conn) try:
await conn.close()
except:
pass
self._connection_pool.clear() self._connection_pool.clear()
# 关闭正在使用的连接
for conn in self._in_use.copy(): for conn in self._in_use.copy():
await try_close_connection(conn) try:
await conn.close()
except:
pass
self._in_use.clear() self._in_use.clear()

View File

@ -32,7 +32,7 @@ class PermManager:
def __init__(self, db: DatabaseManager) -> None: def __init__(self, db: DatabaseManager) -> None:
self.db = db self.db = db
async def get_permission_info(self, entities: _EntityLike, key: str): async def check_has_permission_info(self, entities: _EntityLike, key: str):
entities = await _to_entity_chain(entities) entities = await _to_entity_chain(entities)
key = key.removesuffix("*").removesuffix(".") key = key.removesuffix("*").removesuffix(".")
key_split = key.split(".") key_split = key.split(".")
@ -52,7 +52,7 @@ class PermManager:
return None return None
async def check_has_permission(self, entities: _EntityLike, key: str) -> bool: async def check_has_permission(self, entities: _EntityLike, key: str) -> bool:
res = await self.get_permission_info(entities, key) res = await self.check_has_permission_info(entities, key)
if res is None: if res is None:
return False return False
return res[2] return res[2]

View File

@ -43,12 +43,15 @@ class PermRepo:
Raises: Raises:
AssertionError: 如果创建后无法获取实体 ID。 AssertionError: 如果创建后无法获取实体 ID。
""" """
await self.conn.execute( try:
s("create_entity.sql"), await self.conn.execute(
(entity.platform, entity.entity_type, entity.external_id), s("create_entity.sql"),
) (entity.platform, entity.entity_type, entity.external_id),
await self.conn.commit() )
await self.conn.commit()
except Exception:
await self.conn.rollback()
raise
eid = await self._get_entity_id_or_none(entity) eid = await self._get_entity_id_or_none(entity)
assert eid is not None assert eid is not None
return eid return eid
@ -116,8 +119,12 @@ class PermRepo:
value: 要设置的配置值True/False/None value: 要设置的配置值True/False/None
""" """
eid = await self.get_entity_id(entity) eid = await self.get_entity_id(entity)
await self.conn.execute(s("update_perm_info.sql"), (eid, config_key, value)) try:
await self.conn.commit() await self.conn.execute(s("update_perm_info.sql"), (eid, config_key, value))
await self.conn.commit()
except Exception:
await self.conn.rollback()
raise
async def get_entity_id_batch( async def get_entity_id_batch(
self, entities: list[PermEntity] self, entities: list[PermEntity]
@ -136,11 +143,15 @@ class PermRepo:
# s("create_entity.sql"), # s("create_entity.sql"),
# (entity.platform, entity.entity_type, entity.external_id), # (entity.platform, entity.entity_type, entity.external_id),
# ) # )
await self.conn.executemany( try:
s("create_entity.sql"), await self.conn.executemany(
[(e.platform, e.entity_type, e.external_id) for e in entities], s("create_entity.sql"),
) [(e.platform, e.entity_type, e.external_id) for e in entities],
await self.conn.commit() )
await self.conn.commit()
except Exception:
await self.conn.rollback()
raise
val_placeholders = ", ".join(["(?, ?, ?)"] * len(entities)) val_placeholders = ", ".join(["(?, ?, ?)"] * len(entities))
params = [] params = []
for e in entities: for e in entities:

View File

@ -1,16 +1,16 @@
# 指令介绍 # 指令介绍
简易 Raymarch 小玩具 简易 Raymarch 小玩具
用法march `<scene>` 用法march `[scene]`
march torus(1.0, 0.2).color(1.0, 0.2, 0.2) torus(1.0, 0.2).rot(90, 0, 0).color(0.2, 0.2, 1.0) camera(4.0).pos(4, 0, 0).lookat(0) march torus(1.0, 0.2).color(1.0, 0.2, 0.2) torus(1.0, 0.2).rot(90, 0, 0).color(0.2, 0.2, 1.0) camera(4.0).pos(4, 0, 0).lookat(0)
# 主要语法 # 主要语法
`<scene>` ::= `<scene>` "." `<op>` |`<obj>` `[scene]` ::= `[scene]` "." `[op]` |`[obj]`
`<obj>` ::= `<obj_ty>` | `<obj_ty>` "(" <args> ")" `[obj]` ::= `[obj_ty]` | `[obj_ty]` "(" [args] ")"
`<op>` ::= `<op_ty>` | `<op_ty>` "(" `<args>` ")" `[op]` ::= `[op_ty]` | `[op_ty]` "(" `[args]` ")"
`<args>` ::= `<args>` "," `<arg>` | `<arg>` `[args]` ::= `[args]` "," `[arg]` | `[arg]`
其中 `obj_ty`、`op_ty` 分别为物体类型(如 `cube`、`sphere`、`torus` 等)与变换类型(如 `pos`、`rot`)。 其中 `obj_ty`、`op_ty` 分别为物体类型(如 `cube`、`sphere`、`torus` 等)与变换类型(如 `pos`、`rot`)。
@ -37,4 +37,4 @@
`rounded`:圆角 `rounded`:圆角
# 特殊说明 # 特殊说明
`<op_ty>` 不包含 scale。非正交的变换会破坏 SDF 的性质。 `[op_ty]` 不包含 scale。非正交的变换会破坏 SDF 的性质。

View File

@ -2,6 +2,7 @@
const float EPS = 0.001; const float EPS = 0.001;
const int MAX_ITER = 128; const int MAX_ITER = 128;
const float INF = 1e10; const float INF = 1e10;
const float PI = 3.14159;
uniform vec2 u_resolution; uniform vec2 u_resolution;
out vec4 fragColor; out vec4 fragColor;
@ -68,17 +69,76 @@ vec3 nrm(vec3 p) {
)); ));
} }
float saturate(float x) {
return clamp(x, 0.0, 1.0);
}
float ggx_distribution(vec3 n, vec3 h, float roughness) {
float alpha = roughness * roughness;
float alpha2 = alpha * alpha;
float NdotH = saturate(dot(n, h));
float denom = NdotH * NdotH * (alpha2 - 1.0) + 1.0;
return alpha2 / max(PI * denom * denom, EPS);
}
float geometry_schlick_ggx(float NdotX, float roughness) {
float r = roughness + 1.0;
float k = r * r / 8.0;
return NdotX / max(NdotX * (1.0 - k) + k, EPS);
}
float geometry_smith(vec3 n, vec3 v, vec3 l, float roughness) {
float NdotV = saturate(dot(n, v));
float NdotL = saturate(dot(n, l));
return geometry_schlick_ggx(NdotV, roughness) * geometry_schlick_ggx(NdotL, roughness);
}
vec3 fresnel_schlick(vec3 f0, float cos_theta) {
return f0 + (1.0 - f0) * pow(1.0 - saturate(cos_theta), 5.0);
}
vec3 tonemap_aces(vec3 c) {
const float a = 2.51;
const float b = 0.03;
const float c1 = 2.43;
const float d = 0.59;
const float e = 0.14;
return clamp((c * (a * c + b)) / (c * (c1 * c + d) + e), 0.0, 1.0);
}
vec4 materialColor(int obj_id) { vec4 materialColor(int obj_id) {
<COLOR_BLOCK> <COLOR_BLOCK>
return vec4(1.0); return vec4(1.0);
} }
vec4 color(vec3 p, int obj_id) { vec4 color(vec3 p, vec3 r, int obj_id) {
vec3 normal = nrm(p); vec3 light_col = vec3(1.0);
vec3 light_dir = normalize(vec3(0.5, 0.8, -0.6)); vec4 albedo = materialColor(obj_id);
float light = 0.2 + 0.8 * max(dot(normal, light_dir), 0.0); vec3 N = nrm(p);
vec4 base = materialColor(obj_id); vec3 V = normalize(-r);
return vec4(base.rgb * light, base.a); vec3 L = normalize(vec3(0.5, 0.8, -0.6));
vec3 H = normalize(V + L);
float roughness = 0.45;
float metallic = 0.02;
float NdotL = saturate(dot(N, L));
float NdotV = saturate(dot(N, V));
float D = ggx_distribution(N, H, roughness);
float G = geometry_smith(N, V, L, roughness);
vec3 F0 = mix(vec3(0.04), albedo.rgb, metallic);
vec3 F = fresnel_schlick(F0, dot(V, H));
vec3 kD = (1.0 - F) * (1.0 - metallic);
vec3 diffuse = kD * albedo.rgb / PI;
vec3 specular = D * G * F / max(4.0 * NdotL * NdotV, EPS);
float hemi = N.y * 0.5 + 0.5;
vec3 sky_ambient = vec3(0.60, 0.72, 0.92);
vec3 ground_ambient = vec3(0.18, 0.16, 0.14);
vec3 ambient = mix(ground_ambient, sky_ambient, hemi) * (diffuse + 0.25 * F0) * 0.35;
vec3 col = ambient + (diffuse + specular) * light_col * NdotL;
col = tonemap_aces(col);
col = pow(col, vec3(1.0 / 2.2));
return vec4(col, 1.0);
} }
vec4 march(vec3 p, vec3 r) { vec4 march(vec3 p, vec3 r) {
@ -87,7 +147,7 @@ vec4 march(vec3 p, vec3 r) {
for(int i = 0; i < MAX_ITER; ++i) { for(int i = 0; i < MAX_ITER; ++i) {
qry = sd(p); qry = sd(p);
if(qry.value < EPS){ if(qry.value < EPS){
col = color(p, qry.obj_id); col = color(p, r, qry.obj_id);
break; break;
} }
p += qry.value * r; p += qry.value * r;

View File

@ -77,7 +77,7 @@ async def get_permission(
perm: str, perm: str,
event: Event, event: Event,
): ):
data = await pm.get_permission_info(ec, perm) data = await pm.check_has_permission_info(ec, perm)
obj_s = f"{ec[0].platform}.{ec[0].entity_type}.{ec[0].external_id}" obj_s = f"{ec[0].platform}.{ec[0].entity_type}.{ec[0].external_id}"