1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
// vim: noet
extern crate memmap2;
use std::fs::OpenOptions;
use std::mem::{size_of, transmute};
use std::result::Result;
use memmap2::MmapRaw;
pub mod ipc;
use ipc::IpcSharedMemory;
/**
* Your bot’s interface to the game data.
*
* This class provides a safe interface to the shared memory used for fast communication with the
* gameserver. Use the provided methods to obtain information about your snake’s surroundings or
* the server configuration, and to access persistent memory.
*
* Please note that most methods return direct references to the shared memory structures. These
* are specified and documented in the ipc module.
*/
pub struct Api<'a> {
#[allow(dead_code)]
mmap: MmapRaw,
ipcdata: &'a mut IpcSharedMemory,
}
impl<'i> Api<'i> {
/**
* Construct a new Api instance from the given shared memory file.
*
* This function is used internally by the bot framework. Do not worry about it.
*/
pub fn new(shmfilename: &str) -> Result<Api, String> {
// open and map the shared memory
let file = OpenOptions::new()
.read(true)
.write(true)
.open(shmfilename)
.map_err(|err| format!("Could not open shared memory file {shmfilename}: {err}"))?;
let mmap = MmapRaw::map_raw(&file)
.map_err(|err| format!("Could not create shared memory mmap: {err}"))?;
// check the size of the shared memory. It must be large enough to hold one complete
// IpcSharedMemory structure.
if mmap.len() < size_of::<ipc::IpcSharedMemory>() {
return Err(format!(
"Shared memory contains only {} bytes where {} bytes are required.",
mmap.len(),
size_of::<ipc::IpcSharedMemory>()
));
}
// map the shared memory to a IpcSharedMemory structure.
let ipcdata =
unsafe { &mut *transmute::<*mut u8, *mut ipc::IpcSharedMemory>(mmap.as_mut_ptr()) };
Ok(Api { mmap, ipcdata })
}
/**
* Get a reference to the server config data.
*
* The returned structure contains information about the server
* configuration and static world information.
*/
pub fn get_server_config(&self) -> &ipc::IpcServerConfig {
&self.ipcdata.server_config
}
/**
* Get a pointer to the self information.
*
* The returned structure contains information about your snake and
* parameters of the world.
*/
pub fn get_self_info(&self) -> &ipc::IpcSelfInfo {
&self.ipcdata.self_info
}
/**
* Get a reference to the array of food items around your snake’s head.
*
* The items are sorted by the distance from your snake’s head, so the first entry is the
* closest item.
*
* Only the valid entries in the shared memory are returned.
*/
pub fn get_food(&self) -> &[ipc::IpcFoodInfo] {
&self.ipcdata.food_info[0..self.ipcdata.food_count as usize]
}
/**
* Get a reference to the array of snake segments around your snake’s head.
*
* The items are sorted by the distance from your snake’s head, so the first entry is the
* closest item.
*
* Only the valid entries in the shared memory are returned.
*/
pub fn get_segments(&self) -> &[ipc::IpcSegmentInfo] {
&self.ipcdata.segment_info[0..self.ipcdata.segment_count as usize]
}
/**
* Get a reference to the array of bot information structures.
*
* Only the valid entries in the shared memory are returned.
*/
pub fn get_bot_info(&self) -> &[ipc::IpcBotInfo] {
&self.ipcdata.bot_info[0..self.ipcdata.bot_count as usize]
}
/**
* Remove all color entries from the shared memory.
*
* This must be called in your [`crate::usercode::init()`] function to remove the default color
* in case you want to set your own.
*/
pub fn clear_colors(&mut self) {
self.ipcdata.color_count = 0;
}
/**
* Add a color to your snake’s color sequence.
*
* Call this multiple times from [`crate::usercode::init()`] to set up your snake’s colors. The
* provided sequence will be repeated along your snake if it has more sequence than colors were
* specified. You can set up to [`ipc::IPC_COLOR_MAX_COUNT`] colors.
*/
pub fn add_color(&mut self, r: u8, g: u8, b: u8) {
self.ipcdata.colors[self.ipcdata.color_count as usize] = ipc::IpcColor { r, g, b };
self.ipcdata.color_count += 1;
}
/**
* Get a reference to the persistent memory.
*
* You can use persistent memory to remember things across multiple lives
* of your snake. It is saved after your snake dies (even when your code
* crashes) and restored when it respawns.
*
* Note that the size this memory is very limited (given by the
* [`ipc::IPC_PERSISTENT_MAX_BYTES`] constant). Use it wisely.
*/
pub fn get_persistent_memory(&mut self) -> &mut [u8] {
&mut self.ipcdata.persistent_data
}
/**
* Send a log message.
*
* These messages will appear on the web interface and in the World update
* stream when you provide your viewer key to the server.
*
* Rate limiting is enforced by the gameserver, so messages are dropped
* when you send too many of them.
*/
pub fn log(&mut self, text: &str) -> Result<(), String> {
// determine length of stored data
let startpos = self
.ipcdata
.log_data
.iter()
.position(|&b| b == b'\0')
.ok_or_else(|| "Log memory is not properly initialized or corrupt".to_string())?;
if startpos + text.len() + 2 > self.ipcdata.log_data.len() {
return Err("Log memory too full to append the log message".to_owned());
}
self.ipcdata.log_data[startpos..startpos + text.len()].copy_from_slice(text.as_bytes());
self.ipcdata.log_data[startpos + text.len()] = b'\n';
self.ipcdata.log_data[startpos + text.len() + 1] = b'\0';
Ok(())
}
}