Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix memory leak between the RLBot library and rlbot-rust #29

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rlbot"
version = "0.6.1"
version = "0.7.0"
edition = "2018"
authors = ["John Simon <john@whatisaph.one>"]
description = "RLBot bindings for Rust"
Expand Down
32 changes: 16 additions & 16 deletions examples/gravity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,38 +16,38 @@ fn main() -> Result<(), Box<dyn Error>> {
let mut packets = rlbot.packeteer();
let mut i = 0;
loop {
let packet = packets.next_flatbuffer()?;
let packet = packets.next()?;

// Check that the game is not showing a replay.
// Also don't set state on every frame, that can make it laggy.
if packet.gameInfo().unwrap().isRoundActive() && i % 8 == 0 {
if packet.game_info.is_round_active && i % 8 == 0 {
rlbot.set_game_state(&get_desired_state(packet))?;
}
i += 1;
}
}

fn get_desired_state(packet: rlbot::flat::GameTickPacket<'_>) -> rlbot::DesiredGameState {
let ball_phys = packet.ball().unwrap().physics().unwrap();
let ball_loc = ball_phys.location().unwrap();
let ball_loc = Point3::new(ball_loc.x(), ball_loc.y(), ball_loc.z());
fn get_desired_state(packet: rlbot::GameTickPacket) -> rlbot::DesiredGameState {
let ball_phys = &packet.ball.as_ref().unwrap().physics;
let ball_loc = &ball_phys.location;
let ball_loc = Point3::new(ball_loc.x, ball_loc.y, ball_loc.z);

let mut desired_game_state = rlbot::DesiredGameState::new();

for i in 0..packet.players().unwrap().len() {
let car = packet.players().unwrap().get(i);
let car_phys = car.physics().unwrap();
let car_loc = car_phys.location().unwrap();
let v = car_phys.velocity().unwrap();
for i in 0..packet.players.len() {
let car = packet.players.get(i).unwrap();
let car_phys = &car.physics;
let car_loc = &car_phys.location;
let v = &car_phys.velocity;
let a = gravitate_towards_ball(&ball_loc, car_loc);

// Note: You can ordinarily just use `na::Vector3::new(x, y, z)` here. There's a
// cargo build oddity which prevents that from working in code inside the
// `examples/` directory.
let new_velocity = rlbot::Vector3Partial::new()
.x(v.x() + a.x)
.y(v.y() + a.y)
.z(v.z() + a.z);
.x(v.x + a.x)
.y(v.y + a.y)
.z(v.z + a.z);

let physics = rlbot::DesiredPhysics::new().velocity(new_velocity);
let car_state = rlbot::DesiredCarState::new().physics(physics);
Expand All @@ -59,8 +59,8 @@ fn get_desired_state(packet: rlbot::flat::GameTickPacket<'_>) -> rlbot::DesiredG

/// Generate an acceleration to apply to the car towards the ball, as if the
/// ball exerted a large gravitational force
fn gravitate_towards_ball(ball_loc: &Point3<f32>, car_loc: &rlbot::flat::Vector3) -> Vector3<f32> {
let car_loc = Point3::new(car_loc.x(), car_loc.y(), car_loc.z());
fn gravitate_towards_ball(ball_loc: &Point3<f32>, car_loc: &rlbot::Vector3) -> Vector3<f32> {
let car_loc = Point3::new(car_loc.x, car_loc.y, car_loc.z);
let ball_delta = ball_loc - car_loc;
let distance = ball_delta.norm();
let k = 1_000_000.0;
Expand Down
40 changes: 20 additions & 20 deletions examples/gravity_flatbuffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#![warn(clippy::all)]

use na::{Unit, Vector3};
use rlbot::flat;
use rlbot::{flat, GameTickPacket, PlayerInfo};
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
Expand All @@ -17,11 +17,11 @@ fn main() -> Result<(), Box<dyn Error>> {
let mut packets = rlbot.packeteer();
let mut i = 0;
loop {
let packet = packets.next_flatbuffer()?;
let packet = packets.next()?;

// Check that the game is not showing a replay.
// Also don't set state on every frame, that can make it laggy.
if packet.gameInfo().unwrap().isRoundActive() && i % 8 == 0 {
if packet.game_info.is_round_active && i % 8 == 0 {
let desired_state = get_desired_state(&packet);
rlbot
.interface()
Expand All @@ -31,27 +31,27 @@ fn main() -> Result<(), Box<dyn Error>> {
}
}

fn get_desired_state<'a>(packet: &flat::GameTickPacket<'_>) -> flatbuffers::FlatBufferBuilder<'a> {
let ball = packet.ball().expect("Missing ball");
let ball_phys = ball.physics().expect("Missing ball physics");
let flat_ball_loc = ball_phys.location().expect("Missing ball location");
let ball_loc = Vector3::new(flat_ball_loc.x(), flat_ball_loc.y(), flat_ball_loc.z());
let cars = packet.players().expect("Missing players");
fn get_desired_state<'a>(packet: &GameTickPacket) -> flatbuffers::FlatBufferBuilder<'a> {
let ball = packet.ball.as_ref().expect("Missing ball");
let ball_phys = &ball.physics;
let ball_loc = &ball_phys.location;
let ball_loc = Vector3::new(ball_loc.x, ball_loc.y, ball_loc.z);
let cars = &packet.players;

let mut builder = flatbuffers::FlatBufferBuilder::new_with_capacity(1024);

let mut car_offsets = Vec::with_capacity(cars.len());
let mut i = 0;
while i < cars.len() {
let car = cars.get(i);
let car_phys = car.physics().expect("Missing player physics");
let v = car_phys.velocity().expect("Missing player velocity");
let a = gravitate_towards_ball(&ball_loc, &car);
let car = cars.get(i).expect("Missing car");
let car_phys = &car.physics;
let v = &car_phys.velocity;
let a = gravitate_towards_ball(&ball_loc, car);

let new_velocity = flat::Vector3Partial::create(&mut builder, &flat::Vector3PartialArgs {
x: Some(&flat::Float::new(v.x() + a.x)),
y: Some(&flat::Float::new(v.y() + a.y)),
z: Some(&flat::Float::new(v.z() + a.z)),
x: Some(&flat::Float::new(v.x + a.x)),
y: Some(&flat::Float::new(v.y + a.y)),
z: Some(&flat::Float::new(v.z + a.z)),
});

let physics = flat::DesiredPhysics::create(&mut builder, &flat::DesiredPhysicsArgs {
Expand Down Expand Up @@ -80,10 +80,10 @@ fn get_desired_state<'a>(packet: &flat::GameTickPacket<'_>) -> flatbuffers::Flat

/// Generate an acceleration to apply to the car towards the ball, as if the
/// ball exerted a large gravitational force
fn gravitate_towards_ball(ball_loc: &Vector3<f32>, car: &flat::PlayerInfo<'_>) -> Vector3<f32> {
let car_phys = car.physics().expect("Missing player physics");
let flat_car_loc = car_phys.location().expect("Missing player location");
let car_loc = Vector3::new(flat_car_loc.x(), flat_car_loc.y(), flat_car_loc.z());
fn gravitate_towards_ball(ball_loc: &Vector3<f32>, car: &PlayerInfo) -> Vector3<f32> {
let car_phys = &car.physics;
let car_loc = &car_phys.location;
let car_loc = Vector3::new(car_loc.x, car_loc.y, car_loc.z);
let ball_delta = ball_loc - car_loc;
let distance = ball_delta.norm();
let k = 1_000_000.0;
Expand Down
4 changes: 2 additions & 2 deletions examples/rendering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ fn main() -> Result<(), Box<dyn Error>> {

let mut packets = rlbot.packeteer();
loop {
let packet = packets.next_flatbuffer()?;
let mut total_ms = (packet.gameInfo().unwrap().secondsElapsed() * 1000.0) as i32;
let packet = packets.next()?;
let mut total_ms = (packet.game_info.seconds_elapsed * 1000.0) as i32;
let ms = total_ms % 1000;
total_ms -= ms;
let sec = total_ms / 1000 % 60;
Expand Down
28 changes: 14 additions & 14 deletions examples/simple_flatbuffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#![warn(clippy::all)]

use na::Vector2;
use rlbot::flat;
use rlbot::{flat, GameTickPacket};
use std::{error::Error, f32::consts::PI};

fn main() -> Result<(), Box<dyn Error>> {
Expand All @@ -18,12 +18,12 @@ fn main() -> Result<(), Box<dyn Error>> {

let mut packets = rlbot.packeteer();
loop {
let packet = packets.next_flatbuffer()?;
let packet = packets.next()?;

// check that match is started and not showing a replay.
// `packets.next_flatbuffer()` sleeps until the next packet is
// available, so this loop will not roast your CPU :)
if packet.gameInfo().unwrap().isRoundActive() {
if packet.game_info.is_round_active {
let input = get_input(&packet);
rlbot
.interface()
Expand All @@ -32,21 +32,21 @@ fn main() -> Result<(), Box<dyn Error>> {
}
}

fn get_input<'a>(packet: &flat::GameTickPacket<'_>) -> flatbuffers::FlatBufferBuilder<'a> {
let ball = packet.ball().expect("Missing ball");
let ball_phys = ball.physics().expect("Missing ball physics");
let flat_ball_loc = ball_phys.location().expect("Missing ball location");
let ball_loc = Vector2::new(flat_ball_loc.x(), flat_ball_loc.y());
fn get_input<'a>(packet: &GameTickPacket) -> flatbuffers::FlatBufferBuilder<'a> {
let ball = packet.ball.as_ref().expect("Missing ball");
let ball_phys = &ball.physics;
let ball_loc_3d = &ball_phys.location;
let ball_loc = Vector2::new(ball_loc_3d.x, ball_loc_3d.y);

let car = packet.players().expect("Missing players").get(0);
let car_phys = car.physics().expect("Missing player physics");
let flat_car_loc = car_phys.location().expect("Missing player location");
let car_loc = Vector2::new(flat_car_loc.x(), flat_car_loc.y());
let car = packet.players.get(0).expect("Missing player info");
let car_phys = &car.physics;
let car_loc_3d = &car_phys.location;
let car_loc = Vector2::new(car_loc_3d.x, car_loc_3d.y);

let offset = ball_loc - car_loc;
let desired_yaw = f32::atan2(offset.y, offset.x);
let flat_car_rot = car_phys.rotation().expect("Missing player rotation");
let steer = desired_yaw - flat_car_rot.yaw();
let car_rot = &car_phys.rotation;
let steer = desired_yaw - car_rot.yaw;

let player_index = 0;
let controller_state_args = flat::ControllerStateArgs {
Expand Down
45 changes: 36 additions & 9 deletions src/dll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type UpdateLiveDataPacketFlatbuffer = extern "C" fn() -> ByteBuffer;
type UpdateLiveDataPacket = extern "C" fn(pLiveData: *mut LiveDataPacket) -> RLBotCoreStatus;
type UpdateRigidBodyTickFlatbuffer = extern "C" fn() -> ByteBuffer;
type UpdateRigidBodyTick = extern "C" fn(rigidBodyTick: *mut RigidBodyTick) -> RLBotCoreStatus;
type Free = extern "C" fn(ptr: *mut ::std::os::raw::c_void);
pub(crate) type Free = extern "C" fn(ptr: *mut ::std::os::raw::c_void);
type SetGameState = extern "C" fn(
gameStateData: *mut ::std::os::raw::c_void,
size: ::std::os::raw::c_int,
Expand Down Expand Up @@ -57,11 +57,11 @@ type GetBallPredictionStruct =
static INITIALIZED: AtomicBool = AtomicBool::new(false);

pub struct RLBotCoreInterface {
pub update_field_info_flatbuffer: UpdateFieldInfoFlatbuffer,
update_field_info_flatbuffer_raw: UpdateFieldInfoFlatbuffer,
pub update_field_info: UpdateFieldInfo,
pub update_live_data_packet_flatbuffer: UpdateLiveDataPacketFlatbuffer,
update_live_data_packet_flatbuffer_raw: UpdateLiveDataPacketFlatbuffer,
pub update_live_data_packet: UpdateLiveDataPacket,
pub update_rigid_body_tick_flatbuffer: UpdateRigidBodyTickFlatbuffer,
update_rigid_body_tick_flatbuffer_raw: UpdateRigidBodyTickFlatbuffer,
pub update_rigid_body_tick: UpdateRigidBodyTick,
pub free: Free,
pub set_game_state: SetGameState,
Expand All @@ -74,11 +74,38 @@ pub struct RLBotCoreInterface {
pub update_player_input_flatbuffer: UpdatePlayerInputFlatbuffer,
pub render_group: RenderGroup,
pub is_initialized: IsInitialized,
pub get_ball_prediction: GetBallPrediction,
get_ball_prediction_raw: GetBallPrediction,
pub get_ball_prediction_struct: GetBallPredictionStruct,
}

impl RLBotCoreInterface {
fn copy_and_free_byte_buffer<T>(&self, f: T) -> Option<Vec<u8>>
where
T: Fn() -> ByteBuffer,
{
let byte_buffer = f();
let ret = byte_buffer.into();
(self.free)(byte_buffer.ptr);

ret
}

pub fn update_field_info_flatbuffer(&self) -> Option<Vec<u8>> {
self.copy_and_free_byte_buffer(|| (self.update_field_info_flatbuffer_raw)())
}

pub fn update_live_data_packet_flatbuffer(&self) -> Option<Vec<u8>> {
self.copy_and_free_byte_buffer(|| (self.update_live_data_packet_flatbuffer_raw)())
}

pub fn update_rigid_body_tick_flatbuffer(&self) -> Option<Vec<u8>> {
self.copy_and_free_byte_buffer(|| (self.update_rigid_body_tick_flatbuffer_raw)())
}

pub fn get_ball_prediction(&self) -> Option<Vec<u8>> {
self.copy_and_free_byte_buffer(|| (self.get_ball_prediction_raw)())
}

pub fn load(rlbot_dll_directory: Option<&Path>) -> io::Result<RLBotCoreInterface> {
if INITIALIZED.swap(true, Ordering::SeqCst) {
panic!("RLBot can only be initialized once");
Expand All @@ -96,12 +123,12 @@ impl RLBotCoreInterface {

unsafe {
Ok(RLBotCoreInterface {
update_field_info_flatbuffer: *library.get(b"UpdateFieldInfoFlatbuffer")?,
update_field_info_flatbuffer_raw: *library.get(b"UpdateFieldInfoFlatbuffer")?,
update_field_info: *library.get(b"UpdateFieldInfo")?,
update_live_data_packet_flatbuffer: *library
update_live_data_packet_flatbuffer_raw: *library
.get(b"UpdateLiveDataPacketFlatbuffer")?,
update_live_data_packet: *library.get(b"UpdateLiveDataPacket")?,
update_rigid_body_tick_flatbuffer: *library
update_rigid_body_tick_flatbuffer_raw: *library
.get(b"UpdateRigidBodyTickFlatbuffer")?,
update_rigid_body_tick: *library.get(b"UpdateRigidBodyTick")?,
free: *library.get(b"Free")?,
Expand All @@ -114,7 +141,7 @@ impl RLBotCoreInterface {
update_player_input_flatbuffer: *library.get(b"UpdatePlayerInputFlatbuffer")?,
render_group: *library.get(b"RenderGroup")?,
is_initialized: *library.get(b"IsInitialized")?,
get_ball_prediction: *library.get(b"GetBallPrediction")?,
get_ball_prediction_raw: *library.get(b"GetBallPrediction")?,
get_ball_prediction_struct: *library.get(b"GetBallPredictionStruct")?,
})
}
Expand Down
20 changes: 19 additions & 1 deletion src/ffi_impls.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::ffi;
use crate::{ffi, ffi::ByteBuffer};
use std::ptr;

impl ffi::LiveDataPacket {
/// Yields the [`PlayerInfo`](ffi::PlayerInfo) for each player in the match.
Expand Down Expand Up @@ -75,3 +76,20 @@ impl ffi::PlayerConfiguration {
}
}
}

impl From<ByteBuffer> for Option<Vec<u8>> {
fn from(byte_buffer: ByteBuffer) -> Self {
let len = byte_buffer.size as usize;
if len == 0 || byte_buffer.ptr.is_null() {
return None;
}

let mut buf = Vec::with_capacity(len);
unsafe {
ptr::copy_nonoverlapping(byte_buffer.ptr as *const u8, buf.as_mut_ptr(), len);
buf.set_len(len);
}

Some(buf)
}
}
Loading