Skip to content

Commit

Permalink
Create Native Bindings (#93)
Browse files Browse the repository at this point in the history
* Add bindings

* Increase progress frequency

* Update progress frequency
  • Loading branch information
WorkingRobot authored Nov 29, 2024
1 parent 1070113 commit be9a02e
Show file tree
Hide file tree
Showing 9 changed files with 350 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[workspace]
resolver = "2"
members = ["simulator", "solvers", "game_data", "raphael-cli"]
members = ["simulator", "solvers", "game_data", "raphael-cli", "bindings"]

[package]
name = "raphael-xiv"
Expand Down
16 changes: 16 additions & 0 deletions bindings/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "bindings"
version = "0.1.0"
edition = "2021"

[lib]
name = "raphael_bindings"
crate-type = ["cdylib"]

[dependencies]
simulator = { path = "../simulator" }
solvers = { path = "../solvers" }

[build-dependencies]
cbindgen = "0.27.0"
csbindgen = "1.9.3"
82 changes: 82 additions & 0 deletions bindings/NativeMethods.g.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// <auto-generated>
// This code is generated by csbindgen.
// DON'T CHANGE THIS DIRECTLY.
// </auto-generated>
#pragma warning disable CS8500
#pragma warning disable CS8981
using System;
using System.Runtime.InteropServices;


namespace Raphael
{
public static unsafe partial class NativeMethods
{
const string __DllName = "raphael_bindings";



[DllImport(__DllName, EntryPoint = "solve", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern void solve(SolveArgs* args);


}

[StructLayout(LayoutKind.Sequential)]
public unsafe partial struct SolveArgs
{
public delegate* unmanaged[Cdecl]<bool*, void> on_start;
public delegate* unmanaged[Cdecl]<Action*, nuint, void> on_finish;
public delegate* unmanaged[Cdecl]<Action*, nuint, void> on_suggest_solution;
public delegate* unmanaged[Cdecl]<nuint, void> on_progress;
public ulong action_mask;
public ushort progress;
public ushort quality;
public ushort base_progress;
public ushort base_quality;
public short cp;
public sbyte durability;
public byte job_level;
[MarshalAs(UnmanagedType.U1)] public bool adversarial;
[MarshalAs(UnmanagedType.U1)] public bool backload_progress;
[MarshalAs(UnmanagedType.U1)] public bool unsound_branch_pruning;
}


public enum Action : byte
{
BasicSynthesis,
BasicTouch,
MasterMend,
Observe,
TricksOfTheTrade,
WasteNot,
Veneration,
StandardTouch,
GreatStrides,
Innovation,
WasteNot2,
ByregotsBlessing,
PreciseTouch,
MuscleMemory,
CarefulSynthesis,
Manipulation,
PrudentTouch,
AdvancedTouch,
Reflect,
PreparatoryTouch,
Groundwork,
DelicateSynthesis,
IntensiveSynthesis,
TrainedEye,
HeartAndSoul,
PrudentSynthesis,
TrainedFinesse,
RefinedTouch,
QuickInnovation,
ImmaculateMend,
TrainedPerfection,
}


}
64 changes: 64 additions & 0 deletions bindings/bindings.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#include <cstdarg>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <ostream>
#include <new>

enum class Action : uint8_t {
BasicSynthesis,
BasicTouch,
MasterMend,
Observe,
TricksOfTheTrade,
WasteNot,
Veneration,
StandardTouch,
GreatStrides,
Innovation,
WasteNot2,
ByregotsBlessing,
PreciseTouch,
MuscleMemory,
CarefulSynthesis,
Manipulation,
PrudentTouch,
AdvancedTouch,
Reflect,
PreparatoryTouch,
Groundwork,
DelicateSynthesis,
IntensiveSynthesis,
TrainedEye,
HeartAndSoul,
PrudentSynthesis,
TrainedFinesse,
RefinedTouch,
QuickInnovation,
ImmaculateMend,
TrainedPerfection,
};

struct SolveArgs {
void (*on_start)(bool*);
void (*on_finish)(const Action*, size_t);
void (*on_suggest_solution)(const Action*, size_t);
void (*on_progress)(size_t);
uint64_t action_mask;
uint16_t progress;
uint16_t quality;
uint16_t base_progress;
uint16_t base_quality;
int16_t cp;
int8_t durability;
uint8_t job_level;
bool adversarial;
bool backload_progress;
bool unsound_branch_pruning;
};

extern "C" {

void solve(const SolveArgs *args);

} // extern "C"
24 changes: 24 additions & 0 deletions bindings/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use cbindgen::Config;
use std::env;

fn main() {
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();

cbindgen::Builder::new()
.with_config(Config {
usize_is_size_t: true,
..Default::default()
})
.with_crate(crate_dir)
.generate()
.expect("Unable to generate C++ bindings")
.write_to_file("bindings.h");

csbindgen::Builder::default()
.input_extern_file("src/lib.rs")
.csharp_dll_name("raphael_bindings")
.csharp_namespace("Raphael")
.csharp_class_accessibility("public")
.generate_csharp_file("NativeMethods.g.cs")
.expect("Unable to generate C# bindings");
}
150 changes: 150 additions & 0 deletions bindings/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
use simulator::{ActionMask, Settings, SimulationState};
use solvers::{AtomicFlag, MacroSolver};

#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct SolveArgs {
pub on_start: extern "C" fn(*mut bool),
pub on_finish: extern "C" fn(*const Action, usize),
pub on_suggest_solution: Option<extern "C" fn(*const Action, usize)>,
pub on_progress: Option<extern "C" fn(usize)>,
pub action_mask: u64,
pub progress: u16,
pub quality: u16,
pub base_progress: u16,
pub base_quality: u16,
pub cp: i16,
pub durability: i8,
pub job_level: u8,
pub adversarial: bool,
pub backload_progress: bool,
pub unsound_branch_pruning: bool,
}

// repr should be identical to simulator::Action
#[repr(u8)]
pub enum Action {
BasicSynthesis,
BasicTouch,
MasterMend,
Observe,
TricksOfTheTrade,
WasteNot,
Veneration,
StandardTouch,
GreatStrides,
Innovation,
WasteNot2,
ByregotsBlessing,
PreciseTouch,
MuscleMemory,
CarefulSynthesis,
Manipulation,
PrudentTouch,
AdvancedTouch,
Reflect,
PreparatoryTouch,
Groundwork,
DelicateSynthesis,
IntensiveSynthesis,
TrainedEye,
HeartAndSoul,
PrudentSynthesis,
TrainedFinesse,
RefinedTouch,
QuickInnovation,
ImmaculateMend,
TrainedPerfection,
}

// This should produce an error if simulator::Action is changed
impl From<simulator::Action> for Action {
fn from(value: simulator::Action) -> Self {
match value {
simulator::Action::BasicSynthesis => Self::BasicSynthesis,
simulator::Action::BasicTouch => Self::BasicTouch,
simulator::Action::MasterMend => Self::MasterMend,
simulator::Action::Observe => Self::Observe,
simulator::Action::TricksOfTheTrade => Self::TricksOfTheTrade,
simulator::Action::WasteNot => Self::WasteNot,
simulator::Action::Veneration => Self::Veneration,
simulator::Action::StandardTouch => Self::StandardTouch,
simulator::Action::GreatStrides => Self::GreatStrides,
simulator::Action::Innovation => Self::Innovation,
simulator::Action::WasteNot2 => Self::WasteNot2,
simulator::Action::ByregotsBlessing => Self::ByregotsBlessing,
simulator::Action::PreciseTouch => Self::PreciseTouch,
simulator::Action::MuscleMemory => Self::MuscleMemory,
simulator::Action::CarefulSynthesis => Self::CarefulSynthesis,
simulator::Action::Manipulation => Self::Manipulation,
simulator::Action::PrudentTouch => Self::PrudentTouch,
simulator::Action::AdvancedTouch => Self::AdvancedTouch,
simulator::Action::Reflect => Self::Reflect,
simulator::Action::PreparatoryTouch => Self::PreparatoryTouch,
simulator::Action::Groundwork => Self::Groundwork,
simulator::Action::DelicateSynthesis => Self::DelicateSynthesis,
simulator::Action::IntensiveSynthesis => Self::IntensiveSynthesis,
simulator::Action::TrainedEye => Self::TrainedEye,
simulator::Action::HeartAndSoul => Self::HeartAndSoul,
simulator::Action::PrudentSynthesis => Self::PrudentSynthesis,
simulator::Action::TrainedFinesse => Self::TrainedFinesse,
simulator::Action::RefinedTouch => Self::RefinedTouch,
simulator::Action::QuickInnovation => Self::QuickInnovation,
simulator::Action::ImmaculateMend => Self::ImmaculateMend,
simulator::Action::TrainedPerfection => Self::TrainedPerfection,
}
}
}

impl From<SolveArgs> for Settings {
fn from(value: SolveArgs) -> Self {
Self {
max_cp: value.cp,
max_durability: value.durability,
max_progress: value.progress,
max_quality: value.quality,
base_progress: value.base_progress,
base_quality: value.base_quality,
job_level: value.job_level,
allowed_actions: ActionMask::from_bits(value.action_mask),
adversarial: value.adversarial,
}
}
}

#[no_mangle]
pub extern "C" fn solve(args: &SolveArgs) {
let flag = AtomicFlag::new();
(args.on_start)(flag.as_ptr());

let settings = Settings::from(*args);
let solution_callback: Box<dyn Fn(&[simulator::Action])> =
if let Some(cb) = args.on_suggest_solution {
Box::new(move |actions| {
cb(actions.as_ptr() as *const Action, actions.len());
})
} else {
Box::new(|_| {})
};
let progress_callback: Box<dyn Fn(usize)> = if let Some(cb) = args.on_progress {
Box::new(move |progress| {
cb(progress);
})
} else {
Box::new(|_| {})
};

let state = SimulationState::new(&settings);

let mut solver = MacroSolver::new(
settings,
args.backload_progress,
args.unsound_branch_pruning,
solution_callback,
progress_callback,
flag.clone(),
);

let actions = solver.solve(state).unwrap_or_default();
(args.on_finish)(actions.as_ptr() as *const Action, actions.len());
}
8 changes: 8 additions & 0 deletions simulator/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ pub struct ActionMask {
}

impl ActionMask {
pub const fn from_bits(mask: u64) -> Self {
Self { mask }
}

pub const fn to_bits(self) -> u64 {
self.mask
}

pub const fn none() -> Self {
Self { mask: 0 }
}
Expand Down
2 changes: 1 addition & 1 deletion solvers/src/macro_solver/solver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ impl<'a> MacroSolver<'a> {
}

popped += 1;
if popped % (1 << 14) == 0 {
if popped % (1 << 10) == 0 {
(self.progress_callback)(popped);
}

Expand Down
4 changes: 4 additions & 0 deletions solvers/src/utils/atomic_flag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ impl AtomicFlag {
}
}

pub fn as_ptr(&self) -> *mut bool {
self.flag.as_ptr()
}

pub fn set(&self) {
self.flag.store(true, Ordering::Relaxed);
}
Expand Down

0 comments on commit be9a02e

Please sign in to comment.