game_pathfinding 0.1.3

一个寻路库,包含AStar和Recast,目前还在开发阶段
Documentation
use crate::map::{IndexType, MapManager};
use tokio::runtime::{Runtime, Builder};
use std::os::raw::c_char;
use std::sync::{Once};

pub mod astar;
pub mod errors;
pub mod id;
pub mod map;
pub mod rmacro;

#[repr(C)] // 指定结构体的布局方式为C语言兼容的方式
pub struct CPoint {
    pub x: i32,
    pub y: i32,
}

#[repr(C)]
pub struct CPointArray {
    length: usize,
    data: *const (i32,i32),
    cap: usize,
}

static TOKIO_RUNTIME_INIT: Once = Once::new();
static mut TOKIO_RUNTIME: Option<Runtime> = None;

//修改static mut的操作再call_once中是安全的,所以使用unsafe包裹即可
pub fn init_tokio_runtime(worker_threads: usize) {
    TOKIO_RUNTIME_INIT.call_once(|| {
        unsafe {
            TOKIO_RUNTIME = Some(
                Builder::new_multi_thread()
                    .worker_threads(worker_threads)
                    .build()
                    .unwrap()
            );
        }
    });
}

pub fn get_tokio_runtime() -> &'static Runtime {
    unsafe {
        TOKIO_RUNTIME.as_ref().unwrap()
    }
}

//初始化tokio的runtime
#[no_mangle]
pub extern "C" fn init_runtime(worker_threads: usize) {
    init_tokio_runtime(worker_threads);
}

#[no_mangle]
pub extern "C" fn find_path(map_id:i64, start_point:&CPoint, end_point:&CPoint) -> CPointArray {
    get_tokio_runtime().block_on(async {
        find(map_id, start_point, end_point).await
    })
}

#[no_mangle]
pub extern "C" fn set_walkable(map_id:i64, point:&CPoint, walkable:i32) -> bool {
    get_tokio_runtime().block_on(async {
        set_walkable_impl(map_id, point, walkable).await
    })
}

#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn load_map(c_string: *const c_char) -> i64 {
    let map_file = unsafe { std::ffi::CStr::from_ptr(c_string) };
    get_tokio_runtime().block_on(async {
        load_map_from_file(map_file.to_string_lossy().into_owned()).await
    })
}

#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn load_map_from_string(file_content: *const c_char) -> i64 {
    let map_file_content = unsafe { std::ffi::CStr::from_ptr(file_content) };
    get_tokio_runtime().block_on(async {
        load_map_from_file_content(map_file_content.to_string_lossy().into_owned()).await
    })
}

#[no_mangle]
pub extern "C" fn free_path(path: CPointArray) {
    free_array(path);
}

async fn load_map_from_file(map_file: String) -> i64 {
    let map = MapManager::get_instance();

    //获取写锁与创建新地图
    let map_id = map.write().await.new_astar().await;

    let _ = match map.read().await.load_from_file(&map_id, map_file.to_string()).await {
        Ok(_) => map_id,
        Err(err) => {println!("{}", err);0}
    }; map_id
}

async fn load_map_from_file_content(file_content: String) -> i64 {
    let map = MapManager::get_instance();

    //获取写锁
    let map_id = map.write().await.new_astar().await;

    let _ = match map.read().await.load_from_string(&map_id, file_content).await {
        Ok(_) => map_id,
        Err(err) => {println!("{}", err);0}
    }; map_id
}

async fn find(map_id:i64, start_point:&CPoint, end_point:&CPoint) -> CPointArray {
    let map = MapManager::get_instance();

    //获取读锁
    let result = map.read().await.find_path(&map_id,(start_point.x, start_point.y), (end_point.x, end_point.y)).await;

    match result {
        Ok(v) => convert_to_c_array(v),
        Err(_e) => CPointArray{ data: &(0, 0), length: 0, cap: 0 },
    }
}

async fn set_walkable_impl(map_id:i64, point:&CPoint, walkable:i32) -> bool {
    let map = MapManager::get_instance();
    //获取写锁
    let result = map.write().await.set_walkable(&map_id, (point.x, point.y), walkable).await;
    result.is_ok()
}

fn convert_to_c_array(mut vec: Vec<(IndexType, IndexType)>) -> CPointArray {
    let ptr = vec.as_mut_ptr();
    let len = vec.len();
    let cap = vec.capacity();

    // 避免 Rust 销毁 vec
    std::mem::forget(vec);

    CPointArray {
        data: ptr,
        length: len,
        cap
    }
}

fn free_array(vec:CPointArray) {
    let xx:Vec<(IndexType, IndexType)> = unsafe { Vec::from_raw_parts(vec.data as *mut (IndexType, IndexType), vec.length, vec.cap) };
    drop(xx);
}