Skip to content

Commit

Permalink
ttserverlog example in Rust
Browse files Browse the repository at this point in the history
  • Loading branch information
bear101 committed Apr 17, 2024
1 parent 86605ce commit fe7a325
Show file tree
Hide file tree
Showing 3 changed files with 280 additions and 7 deletions.
9 changes: 2 additions & 7 deletions Client/ttserverlog/Main.cpp
Expand Up @@ -68,7 +68,6 @@ int main(int argc, TTCHAR* argv[])
bool encrypted = false;
TTMessage msg;
int wait_ms, cmd_id;
struct stat s_file;

ttInst = TT_InitTeamTalkPoll();

Expand All @@ -90,12 +89,8 @@ int main(int argc, TTCHAR* argv[])
cout << "Specify directory where to store audio: " << endl;
audio_dir = get_str(DEFAULT_AUDIO_STORAGE);

int i = stat ( "lame_enc.dll", &s_file );
if ( i != 0 )
cout << "lame_enc.dll not found, so audio will be stored to .wav instead of .mp3" << endl;

//now that we got all the information we needed we can connect and logon
if(!TT_Connect(ttInst, ipaddr.c_str(), tcpport, udpport, 0, 0, false))
if(!TT_Connect(ttInst, ipaddr.c_str(), tcpport, udpport, 0, 0, encrypted))
goto error_connect;

//wait for connect event
Expand Down Expand Up @@ -156,7 +151,7 @@ void processTTMessage(const TTMessage& msg)
{
case CLIENTEVENT_CMD_MYSELF_LOGGEDIN:
cout << "Logged in successfully..." << endl;
cout << "Got user ID #" << msg.user.nUserID << endl;
cout << "Got user ID #" << TT_GetMyUserID(ttInst) << endl;
break;
case CLIENTEVENT_CMD_SERVER_UPDATE:
cout << "Got new server properties:" << endl;
Expand Down
9 changes: 9 additions & 0 deletions Client/ttserverlog_rust/Cargo.toml
@@ -0,0 +1,9 @@
[package]
name = "ttserverlog_rust"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
teamtalk = { path = "../../Library/teamtalk_rust" }
269 changes: 269 additions & 0 deletions Client/ttserverlog_rust/src/main.rs
@@ -0,0 +1,269 @@
use core::panic;
use std::{io::{self, Write}, path::PathBuf, time::Duration, vec};
use teamtalk::{self, wait_for_event, Subscriptions, TTMessage, INT32};
use teamtalk::ttstr as ttstr;
use teamtalk::from_ttstr as from_ttstr;
use teamtalk::TRUE as TRUE;
use teamtalk::FALSE as FALSE;

struct Session {
ipaddr: String,
tcpport: i32,
udpport: i32,
encrypted: bool,
username: String,
password: String,
audiodir: PathBuf
}

const DEF_WAIT: Duration = Duration::new(10, 0);

fn print_ne(pstr: &str) {
print!("{pstr}");
io::stdout().flush().expect("Failed to flush");
}

fn get_value(info: &str, default: &str) -> io::Result<String> {
print_ne(format!("{} ({}): ", info, default).as_str());
let mut text = String::new();
match io::stdin().read_line(&mut text) {
Ok(_) => {
let text_s = text.trim();
Ok( if text_s.is_empty() { default.to_string() } else { text_s.to_string() } )
},
Err(e) => Err(e),
}
}

fn get_session() -> Session {

println!("TeamTalk 5 server login information.");

let ipaddr = get_value("Specify IP-address of server to connect to", "192.168.0.110").expect("Invalid IP-address");

let tcpport_s = get_value("Specify TCP port", "10333").expect("Invalid TCP port");
let udpport_s = get_value("Specify UDP port", "10333").expect("Invalid UDP port");

let mut encrypted_s = get_value("Is server encrypted?", "N").expect("Invalid encryption input");
encrypted_s = encrypted_s.to_lowercase();

let username = get_value("Specify username", "admin").expect("Invalid username");
let password = get_value("Specify password", "admin").expect("Invalid password");

let path_s = get_value("Specify directory where to store audio", "./").expect("Invalid path");
let path = PathBuf::from(path_s);
if !path.exists() {
panic!("Directory {} does not exist", path.to_str().unwrap());
}

Session {
ipaddr,
tcpport: tcpport_s.trim().parse().expect("Invalid TCP port"),
udpport: udpport_s.trim().parse().expect("Invalid UDP port"),
encrypted: encrypted_s == "y" || encrypted_s == "yes",
username, password,
audiodir: path
}
}

unsafe fn init_sound(ttinst: *mut teamtalk::TTInstance) -> bool {
let mut inputid: INT32 = 0;
let mut outputid: INT32 = 0;
if teamtalk::TT_GetDefaultSoundDevices(&mut inputid, &mut outputid) == FALSE {
return false;
}

let mut snddevs = vec![teamtalk::SoundDevice::default(); 25];
let mut count = snddevs.len() as INT32;
if teamtalk::TT_GetSoundDevices(snddevs.as_mut_ptr(), &mut count) != TRUE {
panic!("Failed to get sound devices");
}

let mut indev = snddevs.iter().filter(|d| d.nDeviceID == inputid);
match indev.next() {
Some(d) => {
println!("Using sound input device {}", from_ttstr(d.szDeviceName.as_ptr()));
}
None => {
println!("Cannot find sound input device");
}
}

let mut outdev = snddevs.iter().filter(|d| d.nDeviceID == outputid);
match outdev.next() {
Some(d) => {
println!("Using sound output device {}", from_ttstr(d.szDeviceName.as_ptr()));
}
None => {
println!("Cannot find sound output device");
}
}

teamtalk::TT_InitSoundDuplexDevices(ttinst, inputid, outputid) == TRUE
}

unsafe fn connect(ttinst: *mut teamtalk::TTInstance, session: &Session) -> bool {
if teamtalk::TT_Connect(ttinst, ttstr(&session.ipaddr).as_ptr(),
session.tcpport, session.udpport, 0, 0,
if session.encrypted { TRUE } else { FALSE }) != TRUE {
return false;
}

if wait_for_event(ttinst, teamtalk::ClientEvent::CLIENTEVENT_CON_SUCCESS, DEF_WAIT).is_err() {
return false;
}

assert_eq!(teamtalk::TT_GetFlags(ttinst) & teamtalk::ClientFlag::CLIENT_CONNECTED as u32, teamtalk::ClientFlag::CLIENT_CONNECTED as u32);

true
}

unsafe fn login(ttinst: *mut teamtalk::TTInstance, session: &Session) -> bool {
let cmdid = teamtalk::TT_DoLogin(ttinst, ttstr("").as_ptr(), ttstr(&session.username).as_ptr(), ttstr(&session.password).as_ptr());
if cmdid < 0 {
return false;
}

assert!(wait_for_event(ttinst, teamtalk::ClientEvent::CLIENTEVENT_CMD_PROCESSING, DEF_WAIT).is_ok());

match wait_for_event(ttinst, teamtalk::ClientEvent::CLIENTEVENT_CMD_MYSELF_LOGGEDIN, DEF_WAIT) {
Ok(_) => {
}
Err(_) => {
return false;
}
}
assert_eq!(teamtalk::TT_GetFlags(ttinst) & teamtalk::ClientFlag::CLIENT_AUTHORIZED as u32, teamtalk::ClientFlag::CLIENT_AUTHORIZED as u32);

true
}

fn main() {
println!("TeamTalk 5 server logger");

let session = get_session();
unsafe {
let ttinst = teamtalk::TT_InitTeamTalkPoll();

if ! init_sound(ttinst) {
panic!("Failed to initialize sound devices");
}

if ! connect(ttinst, &session) {
panic!("Failed to connect to {}", session.ipaddr);
}
println!("Connected to {}:{}", session.ipaddr, session.tcpport);

if ! login(ttinst, &session) {
panic!("Login failed");
}
println!("Logged in... Got user ID #{}", teamtalk::TT_GetMyUserID(ttinst));

let mut msg = teamtalk::TTMessage::default();
while teamtalk::TT_GetMessage(ttinst, &mut msg, std::ptr::null()) == TRUE {
process_ttmessage(ttinst, &session, msg);
}
}
}

unsafe fn process_ttmessage(ttinst: *mut teamtalk::TTInstance, session: &Session, msg: TTMessage) {
match msg.nClientEvent {
teamtalk::ClientEvent::CLIENTEVENT_CMD_SERVER_UPDATE => {
println!("Got new server properties");
println!("MOTD: {}", from_ttstr(msg.__bindgen_anon_1.serverproperties.szMOTD.as_ptr()));
}
teamtalk::ClientEvent::CLIENTEVENT_CMD_CHANNEL_NEW => {
let chanpath = get_channel_path(ttinst, msg.__bindgen_anon_1.channel.nChannelID);
println!("Added channel {chanpath}");
}
teamtalk::ClientEvent::CLIENTEVENT_CMD_CHANNEL_UPDATE => {
let chanpath = get_channel_path(ttinst, msg.__bindgen_anon_1.channel.nChannelID);
println!("Updated channel {chanpath}");
}
teamtalk::ClientEvent::CLIENTEVENT_CMD_CHANNEL_REMOVE => {
println!("Removed channel #{}", msg.__bindgen_anon_1.channel.nChannelID);
}
teamtalk::ClientEvent::CLIENTEVENT_CMD_USER_LOGGEDIN => {
println!("User #{} {} logged in", msg.__bindgen_anon_1.user.nUserID,
from_ttstr(msg.__bindgen_anon_1.user.szNickname.as_ptr()));
let cmdid = teamtalk::TT_DoSubscribe(ttinst, msg.__bindgen_anon_1.user.nUserID,
teamtalk::Subscription::SUBSCRIBE_INTERCEPT_USER_MSG as Subscriptions |
teamtalk::Subscription::SUBSCRIBE_INTERCEPT_CHANNEL_MSG as Subscriptions |
teamtalk::Subscription::SUBSCRIBE_INTERCEPT_VOICE as Subscriptions);
if cmdid > 0 {
println!("Subscribing to text and audio events from #{}", msg.__bindgen_anon_1.user.nUserID);
} else {
println!("Failed to issue command to subscribe");
}
}
teamtalk::ClientEvent::CLIENTEVENT_CMD_USER_LOGGEDOUT => {
println!("User #{} {} logged out", msg.__bindgen_anon_1.user.nUserID,
from_ttstr(msg.__bindgen_anon_1.user.szNickname.as_ptr()));
}
teamtalk::ClientEvent::CLIENTEVENT_CMD_USER_JOINED => {
let chanpath = get_channel_path(ttinst, msg.__bindgen_anon_1.user.nChannelID);
println!("User #{} {} joined {}", msg.__bindgen_anon_1.user.nUserID,
from_ttstr(msg.__bindgen_anon_1.user.szNickname.as_ptr()),
chanpath);
setup_audio_storage(ttinst, msg.__bindgen_anon_1.user.nUserID, session);
}
teamtalk::ClientEvent::CLIENTEVENT_CMD_USER_LEFT => {
let chanpath = get_channel_path(ttinst, msg.__bindgen_anon_1.user.nChannelID);
println!("User #{} {} left {}", msg.__bindgen_anon_1.user.nUserID,
from_ttstr(msg.__bindgen_anon_1.user.szNickname.as_ptr()),
chanpath);
}
teamtalk::ClientEvent::CLIENTEVENT_CMD_USER_TEXTMSG => {
printtextmsg(ttinst, msg.__bindgen_anon_1.textmessage);
}
teamtalk::ClientEvent::CLIENTEVENT_USER_RECORD_MEDIAFILE => {
if msg.__bindgen_anon_1.mediafileinfo.nStatus == teamtalk::MediaFileStatus::MFS_FINISHED {
println!("Finished recoding audio from #{} to file {}", msg.nSource, from_ttstr(msg.__bindgen_anon_1.mediafileinfo.szFileName.as_ptr()));
}
}
_ => {
println!("Skipped event {:?}", msg.nClientEvent)
}
}
}

unsafe fn get_channel_path(ttinst: *mut teamtalk::TTInstance, chanid: INT32) -> String {
let mut chanpath: [teamtalk::TTCHAR; teamtalk::TT_STRLEN as usize] = [0; teamtalk::TT_STRLEN as usize];
if teamtalk::TT_GetChannelPath(ttinst, chanid, chanpath.as_mut_ptr()) == TRUE {
from_ttstr(chanpath.as_ptr())
} else {
"".to_string()
}
}

unsafe fn printtextmsg(ttinst: *mut teamtalk::TTInstance, textmsg: teamtalk::TextMessage) {
let msgtext = from_ttstr(textmsg.szMessage.as_ptr());
match textmsg.nMsgType {
teamtalk::TextMsgType::MSGTYPE_USER => {
println!("Text message from user #{} to user #{} content: {}",
textmsg.nFromUserID, textmsg.nToUserID, msgtext);
}
teamtalk::TextMsgType::MSGTYPE_CHANNEL => {
println!("Text message from user #{} to channel {} content: {}",
textmsg.nFromUserID, get_channel_path(ttinst, textmsg.nChannelID), msgtext);
}
teamtalk::TextMsgType::MSGTYPE_BROADCAST => {
println!("Text message from user #{} to entire server content: {}",
textmsg.nFromUserID, msgtext);

}
teamtalk::TextMsgType::MSGTYPE_CUSTOM => {
}
teamtalk::TextMsgType::MSGTYPE_NONE => {
}
}
}

unsafe fn setup_audio_storage(ttinst: *mut teamtalk::TTInstance, userid: INT32, session: &Session) {
let audiodir = session.audiodir.to_str().unwrap_or("");
teamtalk::TT_SetUserMediaStorageDir(ttinst, userid,
ttstr(&audiodir).as_ptr(),
std::ptr::null(),
teamtalk::AudioFileFormat::AFF_WAVE_FORMAT);

}

0 comments on commit fe7a325

Please sign in to comment.