Make most configs optional; Add docs; Optimize

This commit is contained in:
Jeff 2024-07-16 10:08:04 -04:00
parent 677b472d66
commit 67e72dcf20
3 changed files with 98 additions and 42 deletions

View File

@ -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)

View File

@ -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())?;
}
}

View File

@ -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>,