mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
Add some approximation of crystal stealing #32
This commit is contained in:
@@ -28,12 +28,11 @@ impl <const EVENT: i16, const PROPERTY: u8, InOut: byteserde::des_slice::ByteDes
|
||||
|
||||
async fn handle(&self, data: Self::In, _peer: &std::sync::Arc<literustlib_server::Connection<crate::PacketData>>, user: &crate::UserData, _sender: &std::sync::Arc<literustlib_server::DataSender<crate::PacketData>>) {
|
||||
if let Some(user_info) = user.user().await {
|
||||
crate::events::log_channel_send_failure(self.msg_router.send(crate::matches::GameMessage::RebroadcastRlnl {
|
||||
skip_user_id: user_info.user_id(),
|
||||
crate::events::log_channel_send_failure(self.msg_router.send(crate::matches::GameMessage::CustomLogicRlnl {
|
||||
user_id: user_info.user_id(),
|
||||
event: self.event,
|
||||
event_in: Self::CODE,
|
||||
property: self.property,
|
||||
data: Some(Box::new(data)),
|
||||
data: Box::new(data),
|
||||
}).await);
|
||||
} else {
|
||||
log::error!("Failed to send gamemode specifc event {:?} for user (no auth!)", self.event);
|
||||
|
||||
@@ -107,6 +107,8 @@ pub(super) struct UserData {
|
||||
pub received_healed: std::sync::atomic::AtomicU32,
|
||||
pub cubes: std::sync::atomic::AtomicU32,
|
||||
pub received_cubes: std::sync::atomic::AtomicU32, // damage taken
|
||||
//pub segments_captured: std::sync::atomic::AtomicU32, // TODO
|
||||
pub crystals: std::sync::atomic::AtomicU32, // crystals destroyed
|
||||
}
|
||||
|
||||
impl UserData {
|
||||
@@ -119,6 +121,8 @@ impl UserData {
|
||||
received_healed: std::sync::atomic::AtomicU32::new(0),
|
||||
cubes: std::sync::atomic::AtomicU32::new(0),
|
||||
received_cubes: std::sync::atomic::AtomicU32::new(0),
|
||||
//segments_captured: std::sync::atomic::AtomicU32::new(0),
|
||||
crystals: std::sync::atomic::AtomicU32::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,6 +131,7 @@ impl UserData {
|
||||
+ self.assists.load(std::sync::atomic::Ordering::Relaxed) * 100
|
||||
+ self.healed.load(std::sync::atomic::Ordering::Relaxed)
|
||||
+ self.cubes.load(std::sync::atomic::Ordering::Relaxed)
|
||||
+ self.crystals.load(std::sync::atomic::Ordering::Relaxed) * 25
|
||||
}
|
||||
|
||||
pub(super) fn get_generic_packet(&self, player_id: u8, stat: rlnl::types::IngameStatId, delta: Option<u32>) -> rlnl::events::ingame::UpdateGameStats {
|
||||
@@ -138,6 +143,7 @@ impl UserData {
|
||||
rlnl::types::IngameStatId::KillAssist => (self.assists.load(std::sync::atomic::Ordering::Relaxed), 100),
|
||||
rlnl::types::IngameStatId::HealCubes => (self.assists.load(std::sync::atomic::Ordering::SeqCst), 1),
|
||||
rlnl::types::IngameStatId::RobotDestroyed => (self.deaths.load(std::sync::atomic::Ordering::Relaxed), 0),
|
||||
rlnl::types::IngameStatId::DestroyedProtoniumCubes => (self.deaths.load(std::sync::atomic::Ordering::Relaxed), 25),
|
||||
s => panic!("Cannot generate game stat {:?}", s)
|
||||
};
|
||||
rlnl::events::ingame::UpdateGameStats {
|
||||
|
||||
@@ -435,6 +435,11 @@ impl BaseInfo {
|
||||
* (max_health as f32)).ceil() as u32
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn max_index(&self) -> usize {
|
||||
self.cube_index.load(std::sync::atomic::Ordering::SeqCst).ceil() as usize
|
||||
}
|
||||
|
||||
fn first_damaged(&self, old_index: usize, max_health: u32) -> Option<usize> {
|
||||
for i in 0..old_index {
|
||||
let health = self.calculate_crystal_health(i, max_health);
|
||||
@@ -444,6 +449,68 @@ impl BaseInfo {
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// returns whether crystal exists
|
||||
fn damage_crystal_at_pos(&self, pos: rlnl::types::Byte3, damage: i32, crystals: &[oj_rc_core::cubes::CubeLocationInfo], max_health: u32) -> bool {
|
||||
if let Some((index, _)) = crystals.iter().enumerate().find(|(_i, crystal)| crystal.x == pos.x && crystal.y == pos.y && crystal.z == pos.z) {
|
||||
let max_index = self.max_index();
|
||||
if index > max_index { return false; }
|
||||
self.damage_crystal(index, damage, max_health);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// returns whether crystal was destroyed
|
||||
fn damage_crystal(&self, i: usize, damage: i32, max_health: u32) -> bool {
|
||||
let damage_byte = (((damage as f32) / (max_health as f32)) * (u8::MAX as f32)).ceil() as u8;
|
||||
let old_health = self.crystals_healths[i].fetch_sub(damage_byte, std::sync::atomic::Ordering::Relaxed);
|
||||
log::debug!("Crystal {} damaged (was {}, now {}, delta {})", i, old_health, old_health.saturating_sub(damage_byte), damage_byte);
|
||||
if old_health < damage_byte {
|
||||
// guarantee underflow behaviour
|
||||
self.crystals_healths[i].store(0, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
old_health != 0 && old_health >= damage_byte
|
||||
}
|
||||
|
||||
/// returns whether crystal exists
|
||||
fn destroy_crystal_at_pos(&self, pos: rlnl::types::Byte3, crystals: &[oj_rc_core::cubes::CubeLocationInfo]) -> bool {
|
||||
if let Some((index, _)) = crystals.iter().enumerate().find(|(_i, crystal)| crystal.x == pos.x && crystal.y == pos.y && crystal.z == pos.z) {
|
||||
let max_index = self.max_index();
|
||||
if index > max_index { return false; }
|
||||
self.destroy_crystal(index);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn destroy_crystal(&self, i: usize) {
|
||||
self.crystals_healths[i].store(0, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
|
||||
fn generate_full_base_heal(&self, base_id: u8, max_health: u32, crystals: &[oj_rc_core::cubes::CubeLocationInfo]) -> rlnl::events::HealedCubes {
|
||||
let max_index = self.max_index();
|
||||
let target_crystals: Vec<rlnl::types::HitCubeInfo> = (0..crystals.len())
|
||||
.take_while(|&i| i <= max_index)
|
||||
.filter(|&i| self.calculate_crystal_health(i, max_health) != 0)
|
||||
.map(|i| {
|
||||
let target_loc = &crystals[i];
|
||||
rlnl::types::HitCubeInfo {
|
||||
pos: rlnl::types::Byte3 { x: target_loc.x, y: target_loc.y, z: target_loc.z, },
|
||||
damage: max_health as i32,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
rlnl::events::HealedCubes {
|
||||
healed_machine: base_id as u16,
|
||||
type_performing_healing: rlnl::types::TargetType::TeamBase,
|
||||
target_type: rlnl::types::TargetType::TeamBase,
|
||||
num_healed_cubes: target_crystals.len() as _,
|
||||
hit_cubes: target_crystals,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum WinMode {
|
||||
@@ -659,7 +726,62 @@ impl BattleArenaLogic {
|
||||
&player.connection,
|
||||
).await);
|
||||
}
|
||||
}
|
||||
|
||||
async fn do_team_base_stealing(&self, cube_damage: &rlnl::events::ingame::DestroyCubeNoEffect, generic: &crate::matches::GenericGamemodeEngine<Self>) {
|
||||
let base_id = cube_damage.hit_machine_id as u8;
|
||||
let mut total_destroyed = 0;
|
||||
let mut valid_cubes = Vec::with_capacity(cube_damage.hit_cubes.len());
|
||||
if let Some(base) = self.base_tracking.bases.get(&base_id) {
|
||||
for hit_cube in cube_damage.hit_cubes.iter() {
|
||||
if let Some(damage) = hit_cube.status.damage {
|
||||
if base.damage_crystal_at_pos(hit_cube.loc, damage, &self.crystals, self.config.protonium_health as u32) {
|
||||
valid_cubes.push(hit_cube.to_owned());
|
||||
}
|
||||
} else if matches!(hit_cube.status.ty, rlnl::types::CubeHistoryEventType::Destroy) {
|
||||
if base.destroy_crystal_at_pos(hit_cube.loc, &self.crystals) {
|
||||
valid_cubes.push(hit_cube.to_owned());
|
||||
total_destroyed += 1;
|
||||
} else {
|
||||
log::warn!("Did not destroy cube with destroy status");
|
||||
}
|
||||
}
|
||||
}
|
||||
generic.broadcast(
|
||||
rlnl::event_code::NetworkEvent::DestroyCubeNoEffect,
|
||||
literustlib::packet::Property::ReliableOrdered,
|
||||
&rlnl::events::ingame::DestroyCubeNoEffect {
|
||||
shooting_machine_id: cube_damage.shooting_machine_id,
|
||||
hit_machine_id: cube_damage.hit_machine_id,
|
||||
target_type: rlnl::types::TargetType::TeamBase,
|
||||
num_hits: valid_cubes.len() as _,
|
||||
hit_cubes: valid_cubes,
|
||||
},
|
||||
true,
|
||||
).await;
|
||||
generic.broadcast(
|
||||
rlnl::event_code::NetworkEvent::SyncTeamBaseCubes,
|
||||
literustlib::packet::Property::ReliableOrdered,
|
||||
&base.generate_full_base_heal(base_id, self.config.protonium_health as u32, &self.crystals),
|
||||
true,
|
||||
).await;
|
||||
}
|
||||
if total_destroyed != 0 {
|
||||
if let Some(team_id) = self.player_tracking.team(cube_damage.shooting_machine_id as u8).await {
|
||||
if let Some(base) = self.base_tracking.bases.get(&team_id) {
|
||||
//log::info!("Player {} stole {} crystals", cube_damage.shooting_machine_id, total_destroyed);
|
||||
base.cube_index.fetch_add(total_destroyed as f32, std::sync::atomic::Ordering::SeqCst);
|
||||
generic.broadcast(
|
||||
rlnl::event_code::NetworkEvent::SyncTeamBaseCubes,
|
||||
literustlib::packet::Property::ReliableOrdered,
|
||||
&base.generate_full_base_heal(team_id, self.config.protonium_health as u32, &self.crystals),
|
||||
true,
|
||||
).await;
|
||||
}
|
||||
} else {
|
||||
log::warn!("Machine {} has no team", cube_damage.shooting_machine_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -874,8 +996,40 @@ impl CustomGameLogic for BattleArenaLogic {
|
||||
true
|
||||
}
|
||||
|
||||
async fn on_broadcast(&self, _generic: &crate::matches::GenericGamemodeEngine<Self>, _user_id: i32, _event_out: rlnl::event_code::NetworkEvent, _event_in: rlnl::event_code::NetworkEvent, _property: literustlib::packet::Property, _data: &Option<Box<dyn crate::Broadcastable>>, _skip_user: bool) -> bool {
|
||||
true
|
||||
async fn on_broadcast(&self, generic: &crate::matches::GenericGamemodeEngine<Self>, _user_id: i32, _event_out: rlnl::event_code::NetworkEvent, event_in: rlnl::event_code::NetworkEvent, _property: literustlib::packet::Property, data: &Option<Box<dyn crate::Broadcastable>>, _skip_user: bool) -> bool {
|
||||
match (event_in, data) {
|
||||
(rlnl::event_code::NetworkEvent::DamageCubeNoEffect, Some(data)) => {
|
||||
let maybe_cube_dmg = <dyn core::any::Any>::downcast_ref::<rlnl::events::ingame::DestroyCubeNoEffect>(data.as_ref());
|
||||
if let Some(cube_damage) = maybe_cube_dmg {
|
||||
if matches!(cube_damage.target_type, rlnl::types::TargetType::TeamBase) {
|
||||
self.do_team_base_stealing(cube_damage, generic).await;
|
||||
false
|
||||
} else {
|
||||
//log::info!("Got non-base DamageCubeNoEffect {:?}", cube_damage.target_type);
|
||||
true
|
||||
}
|
||||
} else {
|
||||
log::warn!("Got DamageCubeNoEffect event with bad serialization type");
|
||||
true
|
||||
}
|
||||
},
|
||||
/*(rlnl::event_code::NetworkEvent::DamageCubeEffectOnly, Some(data)) => {
|
||||
let maybe_cube_dmg = <dyn core::any::Any>::downcast_ref::<rlnl::events::ingame::DestroyCubeEffectOnly>(data.as_ref());
|
||||
if let Some(cube_damage) = maybe_cube_dmg {
|
||||
if matches!(cube_damage.target_type, rlnl::types::TargetType::TeamBase) {
|
||||
log::info!("Got team base DamageCubeEffectOnly for pos ({}, {}, {})", cube_damage.hit_cube.x, cube_damage.hit_cube.y, cube_damage.hit_cube.z);
|
||||
false
|
||||
} else {
|
||||
log::info!("Got non-base DamageCubeEffectOnly {:?}", cube_damage.target_type);
|
||||
true
|
||||
}
|
||||
} else {
|
||||
log::warn!("Got DamageCubeEffectOnly event with bad serialization type");
|
||||
true
|
||||
}
|
||||
},*/
|
||||
_ => true
|
||||
}
|
||||
}
|
||||
|
||||
async fn on_motion(&self, generic: &crate::matches::GenericGamemodeEngine<Self>, motion: &rlnl::machine_motion::MachineMotion, location: (f32, f32, f32)) -> bool {
|
||||
@@ -1029,8 +1183,19 @@ impl CustomGameLogic for BattleArenaLogic {
|
||||
log::warn!("Ignoring SurrenderRequest because it's not implemented (yet)");
|
||||
}
|
||||
(rlnl::event_code::NetworkEvent::AwardTeamBaseProtoniumDestroyedRequest, literustlib::packet::Property::ReliableOrdered) => {
|
||||
// TODO
|
||||
log::warn!("Ignoring AwardTeamBaseProtoniumDestroyedRequest because it's not implemented (yet)");
|
||||
let maybe_crystal_bonus = <dyn core::any::Any>::downcast_ref::<rlnl::events::ingame::AwardProtoniumDestroyedCubes>(data.as_ref());
|
||||
if let Some(crystal_destroyed) = maybe_crystal_bonus {
|
||||
if let Some(generic_player) = generic.users.read().await.get(&crystal_destroyed.player_id) {
|
||||
generic_player.counters.crystals.fetch_add(crystal_destroyed.destroyed_cubes as u32, std::sync::atomic::Ordering::Relaxed);
|
||||
let data = generic_player.counters.get_generic_packet(crystal_destroyed.player_id, rlnl::types::IngameStatId::DestroyedProtoniumCubes, Some(crystal_destroyed.destroyed_cubes as _));
|
||||
generic.broadcast(
|
||||
rlnl::event_code::NetworkEvent::UpdateGameStats,
|
||||
literustlib::packet::Property::ReliableOrdered,
|
||||
&data,
|
||||
true,
|
||||
).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user