Platoons social functionality (#91)

### Description

This implements and completes #87 (to the best of my knowledge)

### Game

Robocraft

### Please confirm

- [x] I am the legal owner or represent the owner of all work submitted
- [x] I consent to my submission being added to this FOSS project
- [ ] This PR used LLMs to generate some or all of the code changes

Reviewed-on: https://git.ngram.ca/OpenJam/rc-servers/pulls/91
Co-authored-by: NG (Graham) <ngniusness@gmail.com>
Co-committed-by: NG (Graham) <ngniusness@gmail.com>
This commit is contained in:
NG (Graham) authored and NGnius committed 2026-02-19 01:57:03 +00:00
1 parent a82488ab3a
commit 50b396f34a
49 files changed
+1152 -47

No files matched your search

+17 -19
View File
@@ -1,6 +1,7 @@
#[derive(Debug, Clone)]
pub enum CustomType {
FriendInfo(super::friend::FriendInfo), // TODO actually serialise
FriendInfo(super::friend::FriendInfo),
PlatoonMember(super::platoon::PlatoonMemberInfo),
Unknown,
}
@@ -8,7 +9,8 @@ impl CustomType {
fn custom_ty(&self) -> u8 {
match self {
Self::FriendInfo(_) => 0,
Self::Unknown => 1,
Self::PlatoonMember(_) => 1,
Self::Unknown => u8::MAX,
}
}
}
@@ -16,31 +18,27 @@ impl CustomType {
pub struct CustomTypeSerdes;
impl polariton::serdes::CustomSerdes<CustomType> for CustomTypeSerdes {
fn dump(c: &CustomType, w: &mut dyn std::io::Write) -> std::io::Result<usize> {
w.write_all(&[c.custom_ty()])?;
let mut buf = Vec::new();
fn custom_ty(c: &CustomType) -> u8 {
c.custom_ty()
}
fn dump_inner(c: &CustomType, w: &mut dyn std::io::Write) -> std::io::Result<usize> {
let total_written_len = match c {
CustomType::FriendInfo(friend) => {
friend.dump(&mut std::io::Cursor::new(&mut buf))?
friend.dump(w)?
},
CustomType::PlatoonMember(member) => {
member.dump(w)?
},
CustomType::Unknown => 0,
};
w.write_all(&(total_written_len as i16).to_be_bytes())?;
w.write_all(&buf)?;
Ok(3 + total_written_len)
/*let payload = vec![ // FIXME don't manually serialize
0u8, // byte custom type
0u8, 5u8, // short custom object size
3u8, 0u8, 0u8, 0u8, 0u8, // content
];*/
Ok(total_written_len)
}
fn parse(r: &mut dyn std::io::Read) -> std::io::Result<CustomType> {
let mut buf = [0u8; 3];
r.read_exact(&mut buf)?;
// TODO only read up up to size
match buf[0] {
fn parse_inner(ty: u8, r: &mut dyn std::io::Read) -> std::io::Result<CustomType> {
match ty {
0 => super::friend::FriendInfo::parse(r).map(CustomType::FriendInfo),
1 => super::platoon::PlatoonMemberInfo::parse(r).map(CustomType::PlatoonMember),
_ => Ok(CustomType::Unknown),
}
}
+1
View File
@@ -2,3 +2,4 @@ pub mod friend;
pub mod clan_invite;
pub mod clan;
pub mod custom;
pub mod platoon;
+72
View File
@@ -0,0 +1,72 @@
#[derive(Debug, Clone)]
pub struct PlatoonMemberInfo {
pub public_id: String,
pub display_name: String,
pub status: MemberStatus,
pub added: i64, // timestamp
pub avatar_id: i32,
pub use_custom_avatar: bool,
}
impl PlatoonMemberInfo {
pub(super) fn dump(&self, w: &mut dyn std::io::Write) -> std::io::Result<usize> {
let mut total = 1 + 8 + 4 + 1;
total += oj_rc_core::data::write_str_for_binreader(&self.public_id, w)?;
total += oj_rc_core::data::write_str_for_binreader(&self.display_name, w)?;
w.write_all(&[self.status.as_u8()])?;
w.write_all(&self.added.to_be_bytes())?;
w.write_all(&self.avatar_id.to_be_bytes())?;
w.write_all(&[self.use_custom_avatar as u8])?;
Ok(total)
}
pub(super) fn parse(r: &mut dyn std::io::Read) -> std::io::Result<Self> {
let public_id = oj_rc_core::data::read_str_for_binwriter(r)?;
let display_name = oj_rc_core::data::read_str_for_binwriter(r)?;
let mut buf = [0u8; 1 + 8 + 4 + 1];
r.read_exact(&mut buf)?;
let status = MemberStatus::from_u8(buf[0]).ok_or_else(|| std::io::Error::other(format!("Invalid platoon member status {}", buf[0])))?;
let added = i64::from_be_bytes([
buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8],
]);
let avatar_id = i32::from_be_bytes([
buf[9], buf[10], buf[11], buf[12],
]);
let use_custom_avatar = buf[13] != 0;
Ok(Self {
public_id,
display_name,
status,
added,
avatar_id,
use_custom_avatar,
})
}
}
#[repr(u8)]
#[derive(Debug, Clone, Copy)]
pub enum MemberStatus {
Invited = 0,
Ready = 1,
InQueue = 2,
InBattle = 3,
}
impl MemberStatus {
#[inline]
pub fn from_u8(num: u8) -> Option<Self> {
match num {
0 => Some(Self::Invited),
1 => Some(Self::Ready),
2 => Some(Self::InQueue),
3 => Some(Self::InBattle),
_ => None,
}
}
#[inline]
pub fn as_u8(&self) -> u8 {
*self as u8
}
}