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

add remaining rlbot interface functions + one example #3

Merged
merged 1 commit into from
Sep 25, 2018
Merged
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
8 changes: 8 additions & 0 deletions README-crates-io.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ functions in RLBot's core interface require flatbuffers.
cargo run --example simple_flatbuffer
```

#### `examples/gravity`

A fun example showing how to set game state using the low-level interface.

```sh
cargo run --example gravity
```

#### `examples/bot`

This is a full-fledged bot that can run within the Python RLBot framework. It
Expand Down
36 changes: 31 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ functions in RLBot's core interface require flatbuffers.
cargo run --example simple_flatbuffer
```

#### `examples/gravity`

A fun example showing how to set game state using the low-level interface.

```sh
cargo run --example gravity
```

#### `examples/bot`

This is a full-fledged bot that can run within the Python RLBot framework. It
Expand Down Expand Up @@ -163,7 +171,7 @@ After bindgen and its prerequisites are installed and working, run this
delightfully short command:

```sh
rlbot=<path-to-rlbot>
rlbot=<absolute-path-to-rlbot>
bindgen \
"$rlbot"/src/main/cpp/RLBotInterface/RLBotInterface/Interface.hpp \
-o src/ffi.rs \
Expand All @@ -176,6 +184,7 @@ bindgen \
--whitelist-function GameFunctions::SetGameState \
--whitelist-function GameFunctions::StartMatch \
--whitelist-function GameFunctions::UpdateFieldInfo \
--whitelist-function GameFunctions::UpdateFieldInfoFlatbuffer \
--whitelist-function GameFunctions::UpdateLiveDataPacket \
--whitelist-function GameFunctions::UpdateLiveDataPacketFlatbuffer \
--whitelist-function GameFunctions::SendQuickChat \
Expand All @@ -184,15 +193,32 @@ bindgen \
--whitelist-function GameFunctions::UpdatePlayerInputFlatbuffer \
--whitelist-function RenderFunctions::RenderGroup \
-- \
-fdeclspec \
ehsanul marked this conversation as resolved.
Show resolved Hide resolved
-I "$rlbot"/src/main/cpp/RLBotInterface/RLBotMessages
```

It should output errors in white text. Modify RLBot's source to fix the errors.

If on an OS that uses forward slashes (ie not Windows), this can quickly
alleviate some of the pain, run from the `RLBotInterface` directory:

```sh
find . | xargs perl -pi -e 's/([\w\.])\\(\w)/$1\/$2/g'
```

Commenting out includes that may fail to resolve:

```sh
find . | xargs perl -pi -e 's/\#include \<Windows.h\>/\/\/<Windows.h>/g'
```

For any problematic references to boost, it will be easiest to just purge
indiscriminately. Keep running the above command and fixing errors (fun times!).
After you've subjected yourself to enough pain, it will run successfully and
output more errors, but in red text this time. As long as the errors are in red,
that means it worked!
indiscriminately. You may have to remove other things, like everything to do
with `MessageStorage`, `GameInput`, `RenderOutput` and `CallbackOutput`. Keep
running the above bindgen command and fixing errors (fun times!). After you've
subjected yourself to enough pain, it will run successfully and output more
errors, but in red text this time. As long as the errors are in red, that means
it worked!

Now open the resulting file (`src/ffi.rs`) and delete all the `extern "C" pub
fn` declarations at the end. Since the DLL is actually loaded using this
Expand Down
4 changes: 2 additions & 2 deletions examples/bot/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
extern crate nalgebra;
extern crate nalgebra as na;
extern crate rlbot;

use nalgebra::Vector2;
use na::Vector2;
use rlbot::ffi;
use std::error::Error;
use std::f32::consts::PI;
Expand Down
116 changes: 116 additions & 0 deletions examples/gravity.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
//! The ball is a neutron star. The cars are planets.

extern crate flatbuffers;
extern crate nalgebra as na;
ehsanul marked this conversation as resolved.
Show resolved Hide resolved
extern crate rlbot;

use na::{Unit, Vector3};
use rlbot::{ffi::MatchSettings, flat};
use std::error::Error;

fn main() -> Result<(), Box<Error>> {
let rlbot = rlbot::init()?;
let mut settings = MatchSettings {
NumPlayers: 2,
..Default::default()
};

settings.PlayerConfiguration[0].Bot = true;
settings.PlayerConfiguration[0].BotSkill = 1.0;
settings.PlayerConfiguration[0].set_name("Earth");

settings.PlayerConfiguration[1].Bot = true;
settings.PlayerConfiguration[1].BotSkill = 1.0;
settings.PlayerConfiguration[1].set_name("Mars");
settings.PlayerConfiguration[1].Team = 1;

rlbot.start_match(settings)?;

let mut packets = rlbot.packeteer();

let mut i = 0;
loop {
let packet = packets.next_flatbuffer()?;

// 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 :)
// also don't set state on each frame, that can make it laggy
if packet.gameInfo().unwrap().isRoundActive() && i % 8 == 0 {
let desired_state = get_desired_state(&packet);
rlbot.set_game_state(desired_state.finished_data())?;
}
i += 1;
}
}

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");

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 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)),
},
);

let physics = flat::DesiredPhysics::create(
&mut builder,
&flat::DesiredPhysicsArgs {
velocity: Some(new_velocity),
..Default::default()
},
);

let car_state = flat::DesiredCarState::create(
&mut builder,
&flat::DesiredCarStateArgs {
physics: Some(physics),
..Default::default()
},
);
car_offsets.push(car_state);
i += 1;
}
let car_states = builder.create_vector(&car_offsets);

let desired_game_state = flat::DesiredGameState::create(
&mut builder,
&flat::DesiredGameStateArgs {
carStates: Some(car_states),
..Default::default()
},
);

builder.finish(desired_game_state, None);
builder
}

/// 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());
let ball_delta = ball_loc - car_loc;
let distance = ball_delta.norm();
let k = 1000_000.0;
let a = k / (distance / 5.0).powf(2.0);
a * Unit::new_normalize(ball_delta).unwrap()
}
4 changes: 2 additions & 2 deletions examples/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
//! blindly towards the ball no matter what is happening on the field (just
//! like Dory from Finding Nemo).

extern crate nalgebra;
extern crate nalgebra as na;
extern crate rlbot;

use nalgebra::Vector2;
use na::Vector2;
use rlbot::ffi;
use std::error::Error;
use std::f32::consts::PI;
Expand Down
44 changes: 33 additions & 11 deletions examples/simple_flatbuffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@
//! blindly towards the ball no matter what is happening on the field (just
//! like Dory from Finding Nemo).

extern crate nalgebra;
extern crate flatbuffers;
extern crate nalgebra as na;
extern crate rlbot;

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

Expand All @@ -27,13 +25,12 @@ fn main() -> Result<(), Box<Error>> {
// available, so this loop will not roast your CPU :)
if packet.gameInfo().unwrap().isRoundActive() {
let input = get_input(&packet);
let player_index = 0;
rlbot.update_player_input_flatbuffer(player_index, input)?;
rlbot.update_player_input_flatbuffer(input.finished_data())?;
}
}
}

fn get_input(packet: &GameTickPacket) -> ControllerStateArgs {
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");
Expand All @@ -49,11 +46,36 @@ fn get_input(packet: &GameTickPacket) -> ControllerStateArgs {
let flat_car_rot = car_phys.rotation().expect("Missing player rotation");
let steer = desired_yaw - flat_car_rot.yaw();

ControllerStateArgs {
let player_index = 0;
let controller_state_args = flat::ControllerStateArgs {
throttle: 1.0,
steer: normalize_angle(steer).max(-1.0).min(1.0),
..Default::default()
}
};

build_player_input(player_index, controller_state_args)
}

fn build_player_input<'a>(
ehsanul marked this conversation as resolved.
Show resolved Hide resolved
player_index: i32,
controller_state_args: flat::ControllerStateArgs,
) -> flatbuffers::FlatBufferBuilder<'a> {
let mut builder = flatbuffers::FlatBufferBuilder::new_with_capacity(1024);
let controller_state = Some(flat::ControllerState::create(
&mut builder,
&controller_state_args,
));

let player_input = flat::PlayerInput::create(
&mut builder,
&flat::PlayerInputArgs {
playerIndex: player_index,
controllerState: controller_state,
},
);

builder.finish(player_input, None);
builder
}

/// Normalize an angle to between -PI and PI.
Expand Down
20 changes: 20 additions & 0 deletions src/dll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ type UpdateLiveDataPacket = extern "C" fn(*mut LiveDataPacket) -> RLBotCoreStatu
type UpdateLiveDataPacketFlatbuffer = extern "C" fn() -> ByteBuffer;
type StartMatch = extern "C" fn(MatchSettings, CallbackFunction, *mut c_uint) -> RLBotCoreStatus;
type IsInitialized = extern "C" fn() -> bool;
type UpdateFieldInfo = extern "C" fn(*mut FieldInfo) -> RLBotCoreStatus;
type UpdateFieldInfoFlatbuffer = extern "C" fn() -> ByteBuffer;
type SendChat =
extern "C" fn(QuickChatPreset, c_int, bool, CallbackFunction, *mut c_uint) -> RLBotCoreStatus;
type SendQuickChat = extern "C" fn(*mut c_void, c_int) -> RLBotCoreStatus;
type SetGameState = extern "C" fn(*mut c_void, c_int) -> RLBotCoreStatus;
type RenderGroup = extern "C" fn(*mut c_void, c_int) -> RLBotCoreStatus;

/// Tracks whether RLBot_Core_Interface has been loaded into this process.
static INITIALIZED: AtomicBool = AtomicBool::new(false);
Expand All @@ -23,6 +30,13 @@ pub struct RLBotCoreInterface {
pub update_player_input_flatbuffer: UpdatePlayerInputFlatbuffer,
pub update_live_data_packet: UpdateLiveDataPacket,
pub update_live_data_packet_flatbuffer: UpdateLiveDataPacketFlatbuffer,
pub update_field_info: UpdateFieldInfo,
pub update_field_info_flatbuffer: UpdateFieldInfoFlatbuffer,
pub set_game_state: SetGameState,
pub render_group: RenderGroup,
pub send_chat: SendChat,
/// Flatbuffer version of send_chat
pub send_quick_chat: SendQuickChat,
pub start_match: StartMatch,
pub is_initialized: IsInitialized,
}
Expand All @@ -47,6 +61,12 @@ impl RLBotCoreInterface {
update_live_data_packet: *library.get(b"UpdateLiveDataPacket")?,
update_live_data_packet_flatbuffer: *library
.get(b"UpdateLiveDataPacketFlatbuffer")?,
update_field_info: *library.get(b"UpdateFieldInfo")?,
update_field_info_flatbuffer: *library.get(b"UpdateFieldInfoFlatbuffer")?,
set_game_state: *library.get(b"SetGameState")?,
render_group: *library.get(b"RenderGroup")?,
send_chat: *library.get(b"SendChat")?,
send_quick_chat: *library.get(b"SendQuickChat")?,
start_match: *library.get(b"StartMatch")?,
is_initialized: *library.get(b"IsInitialized")?,
})
Expand Down
Loading