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
// vim: noet
/*!
* Welcome to the Rust framework documentation for Schlangenprogrammiernacht players!
*
* This documentation will guide you while you program your Schlangenprogrammiernacht (SPN) bot in Rust.
*
* The code you write in the webinterface will replace the [`usercode`] module, which must provide
* the [`usercode::init()`] and [`usercode::step()`] functions. These will be called by the
* framework when requested to do so by the gameserver.
*
* We recommend that you start by reading the documentation of the [`api::Api`] struct,
* which provides safe access to the data provided by the gameserver.
*/
extern crate uds;
#[macro_use]
extern crate num_derive;
extern crate num;
use num::FromPrimitive;
use uds::UnixSeqpacketConn;
use std::mem::{size_of, transmute};
pub mod api;
use api::ipc::{IpcRequestType, IpcResponse, IpcResponseType};
pub mod usercode;
use usercode::{init, step};
static SPN_SHM_FILE: &str = "/spnshm/shm";
static SPN_SOCKET_FILE: &str = "/spnshm/socket";
fn mainloop(mut api: api::Api, socket: UnixSeqpacketConn) -> Result<(), String> {
let mut running = true;
let mut rxbuf = [0u8; size_of::<api::ipc::IpcRequest>()];
while running {
// receive messages from the Gameserver
let (len, _truncated) = socket
.recv(&mut rxbuf)
.map_err(|err| format!("Failed to receive data from unix socket: {err}"))?;
// length check
if len == 0 {
println!("Socket connection terminated by server.");
break;
} else if len < rxbuf.len() {
println!("Error: packet is too short! Expected {} bytes, but received only {}. Packet ignored.",
rxbuf.len(), len);
continue;
}
// check whether the data contains a valid enum value
let request_value = unsafe { *transmute::<*const u8, *const u32>(rxbuf.as_ptr()) };
let request_type = match api::ipc::IpcRequestType::from_u32(request_value) {
Some(x) => x,
None => {
println!("Request type cannot be decoded!");
continue;
}
};
/*
// reinterpret the received data as struct IpcRequest
let request: &api::ipc::IpcRequest = unsafe {
& *transmute::<*const u8, *const api::ipc::IpcRequest>(rxbuf.as_ptr())
};
*/
let angle: f32;
let boost: bool;
// execute the user functions corresponding to the request type
match request_type {
IpcRequestType::Init => {
running = init(&mut api);
angle = 0.0;
boost = false;
}
IpcRequestType::Step => {
// unfortunately, destructuring is not stable yet.
let (tmp_running, tmp_angle, tmp_boost) = step(&mut api);
running = tmp_running;
angle = tmp_angle;
boost = tmp_boost;
}
}
// build the response structure
let response = IpcResponse {
response_type: match running {
true => IpcResponseType::Ok,
false => IpcResponseType::Error,
},
data: api::ipc::ResponseData {
step: api::ipc::IpcStepResponse {
delta_angle: angle,
boost,
},
},
};
// reinterpret the response structure as byte array
let txdata: &[u8] = unsafe {
std::slice::from_raw_parts(
(&response as *const IpcResponse) as *const u8,
std::mem::size_of::<IpcResponse>(),
)
};
// send the result packet
socket
.send(txdata)
.map_err(|err| format!("Failed to send data to unix socket: {err}"))?;
}
Ok(())
}
fn main() -> Result<(), String> {
/*
// For cross-checking structure layout between different programming languages.
println!("sizeof(IpcSelfInfo) = {:8}", size_of::<api::ipc::IpcSelfInfo>());
println!("sizeof(IpcServerConfig) = {:8}", size_of::<api::ipc::IpcServerConfig>());
println!("sizeof(IpcFoodInfo) = {:8}", size_of::<api::ipc::IpcFoodInfo>());
println!("sizeof(IpcBotInfo) = {:8}", size_of::<api::ipc::IpcBotInfo>());
println!("sizeof(IpcSegmentInfo) = {:8}", size_of::<api::ipc::IpcSegmentInfo>());
println!("sizeof(IpcColor) = {:8}", size_of::<api::ipc::IpcColor>());
println!("sizeof(IpcSharedMemory) = {:8}", size_of::<api::ipc::IpcSharedMemory>());
println!("sizeof(IpcRequest) = {:8}", size_of::<api::ipc::IpcRequest>());
println!("sizeof(IpcStepResponse) = {:8}", size_of::<api::ipc::IpcStepResponse>());
println!("sizeof(IpcResponse) = {:8}", size_of::<api::ipc::IpcResponse>());
println!("sizeof(bool) = {:8}", size_of::<bool>());
return;
*/
let a = api::Api::new(SPN_SHM_FILE)?;
let conn = UnixSeqpacketConn::connect(SPN_SOCKET_FILE)
.map_err(|err| format!("Failed to connect to unix socket: {err}"))?;
mainloop(a, conn)?;
Ok(())
}