Make most configs optional; Add docs; Optimize
This commit is contained in:
parent
677b472d66
commit
67e72dcf20
80
README.md
80
README.md
@ -6,6 +6,38 @@ The bot is containerized and can be run without compiling or building anything.
|
||||
can clone this repository and build the image yourself or build the binary directly with Cargo if
|
||||
you are familiar with Rust.
|
||||
|
||||
## Warnings and Notices
|
||||
|
||||
- This project is **not officially supported** by the Veloren team. It will connect to the official
|
||||
Veloren server by default, but the moderators have the final say in whether it is allowed or
|
||||
not. **If you are asked not to use it, do not use it**.
|
||||
- This is **not a cheat bot**. It does not give the player any advantage in the game. It is
|
||||
intended to be a fun addition to the game that can help players trade items with each other.
|
||||
- This project may have bugs. You are encouraged to report them to the author but the author takes
|
||||
no responsibility for any lost items. **No such incidents have been reported so far.**
|
||||
- This program **handles your password securely** and does nothing with it expect connecting to the
|
||||
veloren server during launch. However, third-party video game software is often infected with
|
||||
malware. You should **review the source code** or ask someone you trust to do so for you.
|
||||
- You are welcome to make changes to the code or fork the project. The author is open to
|
||||
contributions and suggestions. But you **must indicate that the software has changed and
|
||||
distribute it under the same license**, which also requires it being open-source. You may not
|
||||
distribute the modified software as if it were the original.
|
||||
|
||||
## In-Game Commands
|
||||
|
||||
The bot is able to respond to the following commands, which must be sent via "/tell".
|
||||
|
||||
- `price [search term]`: Returns the buy/sell offers of any item whose name or ID contains the
|
||||
search term.
|
||||
- `admin_access`: Admin-only, prompts the bot to send a trade invite to the sender, after which it
|
||||
will give away and accept any items until the trade ends.
|
||||
- `announce`: Admin-only, sends the announcement message to "/world". This will reset the
|
||||
announcement timer to 45 minutes.
|
||||
- `sort [count (optional)]`: Admin-only, sorts the inventory once or the given number of times.
|
||||
- `pos [x] [y] [z]`: Admin-only, sets the bot's desired position where it will try to stand (must
|
||||
be close to the bot's current position)
|
||||
- `ori [0-360]`: Admin-only, sets the bot's desired orientation (or facing direction)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You must have either [Docker](docker.com) or [Podman](podman.io) installed.
|
||||
@ -24,7 +56,10 @@ Create a "secrets.toml" file:
|
||||
username = "bot_username"
|
||||
password = "bot_password"
|
||||
character = "bot_character"
|
||||
admins = ["my_username"] # You may add usernames or UUIDs to this list. The only advantage of using UUIDs is that it will persist accross changes to your username or player alias.
|
||||
|
||||
# You may add usernames or UUIDs to this list. The only advantage of using UUIDs is that it will
|
||||
# persist accross changes to your username or player alias.
|
||||
admins = ["my_username"]
|
||||
```
|
||||
|
||||
Then create a secret to pass the file securely to the container.
|
||||
@ -38,9 +73,33 @@ to the container:
|
||||
|
||||
```toml
|
||||
# config/config.toml
|
||||
position = [0, 0, 0] # Change these to the desired X, Y, Z coordinates. The bot will try to stand here, but the coordinates must be close to the bot's spawn point.
|
||||
orientation = 0 # 0 = North, 90 = West, 180 = South, 270 = East
|
||||
announcement = "I love cheese! I am at {location}." # Optional. Announcements are sent every 45 minutes. Use {location} to insert the bot's current location.
|
||||
|
||||
# Optional. The bot will connect to the official server if this is not set.
|
||||
game_server = "server.veloren.net"
|
||||
|
||||
# Optional. The bot will connect to the official auth server if this is not set. Be careful
|
||||
# if you set this, your username and password will be sent to this server. Most servers use the
|
||||
# official auth server so you can probably leave this out, even if you are using an alternate
|
||||
# game server.
|
||||
auth_server = "https://auth.veloren.net"
|
||||
|
||||
# Optional. Change these to the desired X, Y, Z coordinates. The bot will try to stand here, but
|
||||
# the coordinates must be close to the bot's spawn point. If not set, the bot will stand at its
|
||||
# spawn point. Its position can be changed in-game with the "pos" command.
|
||||
position = [0, 0, 0]
|
||||
|
||||
# Optional. (0 = North, 90 = West, 180 = South, 270 = East) If not set, the bot will face North.
|
||||
# Its orientation can be changed in-game with the "ori" command.
|
||||
orientation = 0
|
||||
|
||||
# Optional. Announcements are sent every 45 minutes. Use {location} to insert the bot's current
|
||||
# location. If not set, the bot will not send /world announcements but will still send /region
|
||||
# announcement with usage instructions.
|
||||
announcement = "I love cheese! I am at {location}."
|
||||
|
||||
# The buy_prices and sell_prices tables are required. The keys are item definition IDs and the
|
||||
# values are the price in coins. You may type in-game "/give_item common.items." and press Tab to
|
||||
# explore the item definition IDs. Then just leave off the "common.items." part in this file.
|
||||
|
||||
[buy_prices]
|
||||
"food.cheese" = 50
|
||||
@ -77,16 +136,3 @@ podman build . -t trade_bot
|
||||
|
||||
Then follow the [above](#running) steps with the tag "trade_bot" instead of
|
||||
"git.jeffa.io/jeff/trade_bot".
|
||||
|
||||
### In-Game Commands
|
||||
|
||||
The bot is able to respond to the following commands, which must be sent via "/tell".
|
||||
|
||||
- `price [search term]`: Returns the buy/sell offers of any item whose item definition ID contains
|
||||
the search term.
|
||||
- `admin_access`: Admin-only, prompts the bot to send a trade invite to the sender, after which it
|
||||
will give away and accept any items until the trade ends.
|
||||
- `sort [count (optional)]`: Admin-only, sorts the inventory once or the given number of times.
|
||||
- `pos [x] [y] [z]`: Admin-only, sets the bot's desired position where it will try to stand (must
|
||||
be close to the bot's current position)
|
||||
- `ori [0-360]`: Admin-only, sets the bot's desired orientation (or facing direction)
|
||||
|
54
src/bot.rs
54
src/bot.rs
@ -15,7 +15,7 @@ use std::{
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use tokio::runtime::Runtime;
|
||||
use vek::Quaternion;
|
||||
use vek::{Quaternion, Vec3};
|
||||
use veloren_client::{addr::ConnectionArgs, Client, Event as VelorenEvent, SiteInfoRich, WorldExt};
|
||||
use veloren_client_i18n::LocalizationHandle;
|
||||
use veloren_common::{
|
||||
@ -44,8 +44,8 @@ const COINS: &str = "common.items.utility.coins";
|
||||
/// See the [module-level documentation](index.html) for more information.
|
||||
pub struct Bot {
|
||||
username: String,
|
||||
position: [f32; 3],
|
||||
orientation: f32,
|
||||
position: Pos,
|
||||
orientation: Ori,
|
||||
admins: Vec<String>,
|
||||
announcement: Option<String>,
|
||||
|
||||
@ -78,8 +78,8 @@ impl Bot {
|
||||
admins: Vec<String>,
|
||||
buy_prices: HashMap<String, u32>,
|
||||
sell_prices: HashMap<String, u32>,
|
||||
position: [f32; 3],
|
||||
orientation: f32,
|
||||
position: Option<[f32; 3]>,
|
||||
orientation: Option<f32>,
|
||||
announcement: Option<String>,
|
||||
) -> Result<Self, String> {
|
||||
log::info!("Connecting to veloren");
|
||||
@ -125,6 +125,19 @@ impl Bot {
|
||||
clock.tick();
|
||||
}
|
||||
|
||||
let position = if let Some(coords) = position {
|
||||
Pos([coords[0], coords[1], coords[2]].into())
|
||||
} else {
|
||||
client
|
||||
.position()
|
||||
.map(|coords| Pos(coords))
|
||||
.ok_or("Failed to get position")?
|
||||
};
|
||||
let orientation = if let Some(orientation) = orientation {
|
||||
Ori::new(Quaternion::rotation_z(orientation.to_radians()))
|
||||
} else {
|
||||
client.current::<Ori>().ok_or("Failed to get orientation")?
|
||||
};
|
||||
let now = Instant::now();
|
||||
|
||||
Ok(Bot {
|
||||
@ -267,11 +280,14 @@ impl Bot {
|
||||
}
|
||||
"ori" => {
|
||||
if self.is_user_admin(&sender)? {
|
||||
if let Some(orientation) = split_content.next() {
|
||||
self.orientation = orientation
|
||||
if let Some(new_rotation) = split_content.next() {
|
||||
let new_rotation = new_rotation
|
||||
.parse::<f32>()
|
||||
.map_err(|error| error.to_string())?;
|
||||
|
||||
self.orientation =
|
||||
Ori::new(Quaternion::rotation_z(new_rotation.to_radians()));
|
||||
|
||||
None
|
||||
} else {
|
||||
Some("Use the format 'ori [0-360]'")
|
||||
@ -294,13 +310,11 @@ impl Bot {
|
||||
split_content.next(),
|
||||
split_content.next(),
|
||||
) {
|
||||
let position = [
|
||||
self.position = Pos(Vec3::new(
|
||||
x.parse::<f32>().map_err(|error| error.to_string())?,
|
||||
y.parse::<f32>().map_err(|error| error.to_string())?,
|
||||
z.parse::<f32>().map_err(|error| error.to_string())?,
|
||||
];
|
||||
|
||||
self.position = position;
|
||||
));
|
||||
|
||||
None
|
||||
} else {
|
||||
@ -438,8 +452,8 @@ impl Bot {
|
||||
.sites()
|
||||
.into_iter()
|
||||
.find_map(|(_, SiteInfoRich { site, .. })| {
|
||||
let x_difference = self.position[0] - site.wpos[0] as f32;
|
||||
let y_difference = self.position[1] - site.wpos[1] as f32;
|
||||
let x_difference = self.position.0[0] - site.wpos[0] as f32;
|
||||
let y_difference = self.position.0[1] - site.wpos[1] as f32;
|
||||
|
||||
if x_difference.abs() < 100.0 && y_difference.abs() < 100.0 {
|
||||
site.name.clone()
|
||||
@ -824,31 +838,25 @@ impl Bot {
|
||||
/// Moves the character to the configured position and orientation.
|
||||
fn handle_position_and_orientation(&mut self) -> Result<(), String> {
|
||||
if let Some(current_position) = self.client.current::<Pos>() {
|
||||
let target_position = Pos(self.position.into());
|
||||
|
||||
if current_position != target_position {
|
||||
if current_position != self.position {
|
||||
let entity = self.client.entity();
|
||||
let ecs = self.client.state_mut().ecs();
|
||||
let mut position_state = ecs.write_storage::<Pos>();
|
||||
|
||||
position_state
|
||||
.insert(entity, target_position)
|
||||
.insert(entity, self.position)
|
||||
.map_err(|error| error.to_string())?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(current_orientation) = self.client.current::<Ori>() {
|
||||
let target_orientation = Ori::default()
|
||||
.uprighted()
|
||||
.rotated(Quaternion::rotation_z(self.orientation.to_radians()));
|
||||
|
||||
if current_orientation != target_orientation {
|
||||
if current_orientation != self.orientation {
|
||||
let entity = self.client.entity();
|
||||
let ecs = self.client.state_mut().ecs();
|
||||
let mut orientation_state = ecs.write_storage::<Ori>();
|
||||
|
||||
orientation_state
|
||||
.insert(entity, target_orientation)
|
||||
.insert(entity, self.orientation)
|
||||
.map_err(|error| error.to_string())?;
|
||||
}
|
||||
}
|
||||
|
@ -10,8 +10,10 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct Config {
|
||||
pub position: [f32; 3],
|
||||
pub orientation: f32,
|
||||
pub game_server: Option<String>,
|
||||
pub auth_server: Option<String>,
|
||||
pub position: Option<[f32; 3]>,
|
||||
pub orientation: Option<f32>,
|
||||
pub announcement: Option<String>,
|
||||
pub buy_prices: HashMap<String, u32>,
|
||||
pub sell_prices: HashMap<String, u32>,
|
||||
|
Loading…
Reference in New Issue
Block a user