Support for Nested combat messages and JSPatch Notify from files #2

Merged
xeon merged 10 commits from nested-msgs into master 2024-09-14 09:05:18 +00:00
31 changed files with 159 additions and 60 deletions

View file

@ -1,4 +1,4 @@
FROM alpine:3.20 as release
FROM alpine:3.20
ARG MICROSERVICE
WORKDIR /app

View file

@ -4,7 +4,7 @@ pub trait TomlConfig: DeserializeOwned {
const DEFAULT_TOML: &str;
}
pub fn load_or_create<'a, C>(path: &str) -> C
pub fn load_or_create<C>(path: &str) -> C
where
C: DeserializeOwned + TomlConfig,
{

View file

@ -4,7 +4,7 @@ pub fn unix_timestamp() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs() as u64
.as_secs()
}
pub fn unix_timestamp_ms() -> u64 {

View file

@ -26,10 +26,7 @@ macro_rules! impl_from_data {
impl Component for Attribute {
fn set_pb_data(&self, pb: &mut shorekeeper_protocol::EntityPb) {
pb.living_status = self
.is_alive()
.then_some(LivingStatus::Alive)
.unwrap_or(LivingStatus::Dead)
pb.living_status = (if self.is_alive() { LivingStatus::Alive } else { LivingStatus::Dead })
.into();
pb.component_pbs.push(EntityComponentPb {

View file

@ -5,6 +5,7 @@ mod owner_player;
mod player_entity_marker;
mod position;
mod visibility;
mod weapon;
pub use attribute::Attribute;
pub use entity_config::EntityConfig;
@ -13,3 +14,4 @@ pub use owner_player::OwnerPlayer;
pub use player_entity_marker::PlayerEntityMarker;
pub use position::Position;
pub use visibility::Visibility;
pub use weapon::Weapon;

View file

@ -0,0 +1,19 @@
use shorekeeper_protocol::{EntityComponentPb, EquipComponentPb};
use shorekeeper_protocol::entity_component_pb::ComponentPb;
use crate::logic::ecs::component::Component;
pub struct Weapon {
pub weapon_id: i32,
pub weapon_breach_level: i32,
}
impl Component for Weapon {
fn set_pb_data(&self, pb: &mut shorekeeper_protocol::EntityPb) {
pb.component_pbs.push(EntityComponentPb {
component_pb: Some(ComponentPb::EquipComponent(EquipComponentPb {
weapon_id: self.weapon_id,
weapon_breach_level: self.weapon_breach_level,
}))
})
}
}

View file

@ -30,6 +30,7 @@ impl_component_container! {
Attribute;
PlayerEntityMarker;
Movement;
Weapon;
}
pub trait Component {

View file

@ -59,6 +59,6 @@ macro_rules! query_components {
}),
)*)
})
.unwrap_or_else(|| ($( crate::ident_as_none!($comp), )*))
.unwrap_or_else(|| ($( $crate::ident_as_none!($comp), )*))
};
}

View file

@ -24,7 +24,7 @@ impl World {
pub fn create_entity(&mut self) -> EntityBuilder {
let entity = self.entity_manager.create();
EntityBuilder::builder(entity, self.components.entry(entity).or_insert(Vec::new()))
EntityBuilder::builder(entity, self.components.entry(entity).or_default())
}
pub fn is_in_world(&self, entity_id: i64) -> bool {

View file

@ -4,23 +4,23 @@ pub use scene::*;
use shorekeeper_protocol::message::Message;
macro_rules! handle_request {
($($name:ident;)*) => {
($($name:ident $(, $inner_package:ident)?;)*) => {
fn handle_request(player: &mut super::player::Player, mut msg: Message) {
use ::shorekeeper_protocol::{MessageID, Protobuf};
::paste::paste! {
match msg.get_message_id() {
$(
::shorekeeper_protocol::[<$name Request>]::MESSAGE_ID => {
let Ok(request) = ::shorekeeper_protocol::[<$name Request>]::decode(&*msg.remove_payload()) else {
tracing::debug!("failed to decode {}, player_id: {}", stringify!([<$name Request>]), player.basic_info.id);
::shorekeeper_protocol::$($inner_package::)?[<$name Request>]::MESSAGE_ID => {
let Ok(request) = ::shorekeeper_protocol::$($inner_package::)?[<$name Request>]::decode(&*msg.remove_payload()) else {
tracing::debug!("failed to decode {}, player_id: {}", stringify!($($inner_package::)?[<$name Request>]), player.basic_info.id);
return;
};
tracing::debug!("logic: processing request {}", stringify!([<$name Request>]));
tracing::debug!("logic: processing request {}", stringify!($($inner_package::)?[<$name Request>]));
let mut response = ::shorekeeper_protocol::[<$name Response>]::default();
[<on_ $name:snake _request>](player, request, &mut response);
let mut response = ::shorekeeper_protocol::$($inner_package::)?[<$name Response>]::default();
[<on_ $($inner_package:snake _)? $name:snake _request>](player, request, &mut response);
player.respond(response, msg.get_rpc_id());
},
@ -33,20 +33,22 @@ macro_rules! handle_request {
}
macro_rules! handle_push {
($($name:ident;)*) => {
($($name:ident $(, $inner_package:ident)?;)*) => {
fn handle_push(player: &mut super::player::Player, mut msg: Message) {
use ::shorekeeper_protocol::{MessageID, Protobuf};
::paste::paste! {
match msg.get_message_id() {
$(
::shorekeeper_protocol::[<$name Push>]::MESSAGE_ID => {
let Ok(push) = ::shorekeeper_protocol::[<$name Push>]::decode(&*msg.remove_payload()) else {
tracing::debug!("failed to decode {}, player_id: {}", stringify!([<$name Push>]), player.basic_info.id);
::shorekeeper_protocol::$($inner_package::)?[<$name Push>]::MESSAGE_ID => {
let Ok(push) = ::shorekeeper_protocol::$($inner_package::)?[<$name Push>]::decode(&*msg.remove_payload()) else {
tracing::debug!("failed to decode {}, player_id: {}", stringify!($($inner_package::)?[<$name Push>]), player.basic_info.id);
return;
};
[<on_ $name:snake _push>](player, push);
tracing::debug!("logic: processing push {}", stringify!($($inner_package::)?[<$name Push>]));
[<on_ $($inner_package:snake _)? $name:snake _push>](player, push);
},
)*
unhandled => ::tracing::warn!("can't find handler for push with message_id={unhandled}")
@ -60,6 +62,7 @@ handle_request! {
UpdateSceneDate;
EntityActive;
EntityOnLanded;
CombatSendPack, combat_message;
}
handle_push! {

View file

@ -2,6 +2,7 @@ use shorekeeper_protocol::{
EntityActiveRequest, EntityActiveResponse, EntityOnLandedRequest, EntityOnLandedResponse,
ErrorCode, MovePackagePush, UpdateSceneDateRequest, UpdateSceneDateResponse,
};
use shorekeeper_protocol::combat_message::{CombatSendPackRequest, CombatSendPackResponse};
use crate::{logic::ecs::component::ComponentContainer, logic::player::Player, query_components};
@ -13,6 +14,14 @@ pub fn on_update_scene_date_request(
response.error_code = ErrorCode::Success.into();
}
pub fn on_combat_message_combat_send_pack_request(
_player: &Player,
_request: CombatSendPackRequest,
response: &mut CombatSendPackResponse,
) {
response.error_code = ErrorCode::Success.into();
}
pub fn on_entity_active_request(
player: &Player,
request: EntityActiveRequest,

View file

@ -43,7 +43,7 @@ impl Player {
// we need shorekeeper
// TODO: remove this part after implementing team switch
if !self.role_list.iter().any(|r| r.role_id == 1505) {
self.role_list.push(Role::new(1505));
self.role_list.push(Role::new(1505, Some(21050036)));
}
self.formation_list.clear();
@ -126,8 +126,7 @@ impl Player {
.unwrap()
.role_id_set
.iter()
.map(|id| self.role_list.iter().find(|r| r.role_id == *id))
.flatten()
.flat_map(|id| self.role_list.iter().find(|r| r.role_id == *id))
.collect()
}
@ -217,8 +216,8 @@ impl Player {
fn create_main_character_role(name: String, sex: i32) -> Role {
let mut role = match sex {
0 => Role::new(Role::MAIN_CHARACTER_FEMALE_ID),
1 => Role::new(Role::MAIN_CHARACTER_MALE_ID),
0 => Role::new(Role::MAIN_CHARACTER_FEMALE_ID, None),
1 => Role::new(Role::MAIN_CHARACTER_MALE_ID, None),
_ => unreachable!(),
};

View file

@ -1,12 +1,11 @@
use std::collections::HashMap;
use common::time_util;
pub use formation::RoleFormation;
use shorekeeper_data::role_info_data;
use shorekeeper_protocol::{ArrayIntInt, RoleData, RoleInfo};
mod formation;
pub use formation::RoleFormation;
pub struct Role {
pub role_id: i32,
pub name: String,
@ -17,14 +16,19 @@ pub struct Role {
pub star: i32,
pub favor: i32,
pub create_time: u32,
pub equip_weapon: i32,
}
impl Role {
pub const MAIN_CHARACTER_MALE_ID: i32 = 1501;
pub const MAIN_CHARACTER_FEMALE_ID: i32 = 1502;
pub fn new(role_id: i32) -> Self {
pub fn new(role_id: i32, weapon_id: Option<i32>) -> Self {
let data = role_info_data::iter().find(|d| d.id == role_id).unwrap();
let equip_weapon = match weapon_id {
None => data.init_weapon_item_id,
Some(x) => x,
};
Self {
role_id,
@ -36,6 +40,7 @@ impl Role {
star: 0,
favor: 0,
create_time: time_util::unix_timestamp() as u32,
equip_weapon,
}
}
@ -69,6 +74,7 @@ impl Role {
star: data.star,
favor: data.favor,
create_time: data.create_time,
equip_weapon: data.equip_weapon,
}
}
@ -83,6 +89,7 @@ impl Role {
star: self.star,
favor: self.favor,
create_time: self.create_time,
equip_weapon: self.equip_weapon,
..Default::default()
}
}

View file

@ -11,8 +11,9 @@ use std::{
};
use common::time_util;
use shorekeeper_protocol::{message::Message, JoinSceneNotify, TransitionOptionPb};
use shorekeeper_protocol::{AfterJoinSceneNotify, EnterGameResponse, PlayerSaveData};
use shorekeeper_protocol::{message::Message, JoinSceneNotify, TransitionOptionPb,
AfterJoinSceneNotify, EnterGameResponse, JsPatchNotify};
use shorekeeper_protocol::{PlayerSaveData};
use crate::{
player_save_task::{self, PlayerSaveReason},
@ -21,6 +22,9 @@ use crate::{
use super::{ecs::world::World, player::Player, utils::world_util};
const WATER_MASK: &str = include_str!("../../watermask.js");
const UID_FIX: &str = include_str!("../../uidfix.js");
pub enum LogicInput {
AddPlayer {
player_id: i32,
@ -105,8 +109,7 @@ fn logic_thread_func(receiver: mpsc::Receiver<LogicInput>, load: Arc<AtomicUsize
let mut world = world.borrow_mut();
let mut players = world
.player_ids()
.map(|id| state.players.get(id).map(|pl| pl.borrow_mut()))
.flatten()
.flat_map(|id| state.players.get(id).map(|pl| pl.borrow_mut()))
.collect::<Box<_>>();
super::systems::tick_systems(&mut world, &mut players);
@ -164,6 +167,14 @@ fn handle_logic_input(state: &mut LogicState, input: LogicInput) {
scene_info: Some(scene_info),
transition_option: Some(TransitionOptionPb::default()),
});
player.notify(JsPatchNotify {
content: WATER_MASK.to_string(),
});
player.notify(JsPatchNotify {
content: UID_FIX
.replace("{PLAYER_USERNAME}", &player.basic_info.name)
.replace("{SELECTED_COLOR}", "50FC71"),
});
player.respond(EnterGameResponse::default(), enter_rpc_id);
player.notify(AfterJoinSceneNotify::default());

View file

@ -11,8 +11,7 @@ pub fn build_scene_add_on_init_data(world: &World) -> PlayerSceneAoiData {
let mut aoi_data = PlayerSceneAoiData::default();
for entity in entities {
let mut pb = EntityPb::default();
pb.id = entity.into();
let mut pb = EntityPb { id: entity.into(), ..Default::default() };
world
.get_entity_components(entity)

View file

@ -8,14 +8,13 @@ use crate::{
logic::{
components::{
Attribute, EntityConfig, Movement, OwnerPlayer, PlayerEntityMarker, Position,
Visibility,
Visibility, Weapon
},
ecs::{component::ComponentContainer, world::World},
player::Player,
},
query_with,
};
use super::entity_serializer;
pub fn add_player_entities(world: &mut World, player: &Player) {
@ -44,6 +43,10 @@ pub fn add_player_entities(world: &mut World, player: &Player) {
.unwrap(),
)))
.with(ComponentContainer::Movement(Movement::default()))
.with(ComponentContainer::Weapon(Weapon {
weapon_id: role.equip_weapon,
weapon_breach_level: 0, // TODO: store this too
}))
.build();
tracing::debug!(

9
game-server/uidfix.js Normal file
View file

@ -0,0 +1,9 @@
setTimeout(() => {
const UiManager_1 = require("../Ui/UiManager");
const UE = require("ue");
const ControllerManagerBase_1 = require("../../Core/Framework/ControllerManagerBase");
const UiText = UiManager_1.UiManager.GetViewByName("UidView").GetText(0);
UiText.SetText("{PLAYER_USERNAME} - Reversed Rooms");
UiText.SetColor(UE. Color.FromHex("{SELECTED_COLOR}"));
}, 10000);

38
game-server/watermask.js Normal file
View file

@ -0,0 +1,38 @@
const UE = require("ue"),
Info_1 = require("../../../Core/Common/Info"),
MathUtils_1 = require("../../../Core/Utils/MathUtils"),
EventDefine_1 = require("../../Common/Event/EventDefine"),
EventSystem_1 = require("../../Common/Event/EventSystem"),
UiControllerBase_1 = require("../../Ui/Base/UiControllerBase"),
UiLayerType_1 = require("../../Ui/Define/UiLayerType"),
UiLayer_1 = require("../../Ui/UiLayer");
var _a = require('../Module/WaterMask/WaterMaskController').WaterMaskView;
_a.vOo = function () {
void 0 !== _a.SOo && _a.EOo();
var e = UiLayer_1.UiLayer.GetLayerRootUiItem(UiLayerType_1.ELayerType.WaterMask),
t = (_a.SOo = UE.KuroActorManager.SpawnActor(Info_1.Info.World, UE.UIContainerActor.StaticClass(),
MathUtils_1.MathUtils.DefaultTransform, void 0), _a.SOo.RootComponent),
e = (t.SetDisplayName("WaterMaskContainer"), UE.KuroStaticLibrary.SetActorPermanent(_a.SOo, !0, !0), _a.SOo
.K2_AttachRootComponentTo(e), t.GetRootCanvas().GetOwner().RootComponent),
i = e.widget.width % _a.yOo / 2,
r = e.widget.height % _a.IOo / 2,
n = e.widget.width / 2,
_ = e.widget.height / 2,
s = Math.ceil(e.widget.width / _a.yOo),
o = Math.ceil(e.widget.height / _a.IOo),
v = "discord.gg/reversedrooms";
for (let a = 0; a < s; a++)
for (let e = 0; e < o; e++) {
var E = UE.KuroActorManager.SpawnActor(Info_1.Info.World, UE.UITextActor.StaticClass(), MathUtils_1
.MathUtils.DefaultTransform, void 0),
U = E.RootComponent,
U = (E.K2_AttachRootComponentTo(t), U.SetDisplayName("WaterMaskText"), E.GetComponentByClass(UE
.UIText.StaticClass()));
U.SetFontSize(_a.vFt), U.SetOverflowType(0), U.SetAlpha(_a.LOo), U.SetFont(UE.LGUIFontData
.GetDefaultFont()), U.SetText(v), U.SetUIRelativeLocation(new UE.Vector(a * _a.yOo - n + i, e *
_a.IOo - _ + r, 0)), U.SetUIRelativeRotation(new UE.Rotator(0, _a.TOo, 0)), UE.KuroStaticLibrary
.SetActorPermanent(E, !0, !0)
}
};
_a.vOo();

View file

@ -867,7 +867,7 @@ impl<Output> Kcp<Output> {
}
let mut ts_flush = self.ts_flush;
let mut tm_packet = u32::max_value();
let mut tm_packet = u32::MAX;
if timediff(current, ts_flush) >= 10000 || timediff(current, ts_flush) < -10000 {
ts_flush = current;
@ -1142,7 +1142,7 @@ impl<Output: Write> Kcp<Output> {
let resent = if self.fastresend > 0 {
self.fastresend
} else {
u32::max_value()
u32::MAX
};
let rtomin = if !self.nodelay { self.rx_rto >> 3 } else { 0 };
@ -1381,7 +1381,7 @@ impl<Output: AsyncWrite + Unpin + Send> Kcp<Output> {
let resent = if self.fastresend > 0 {
self.fastresend
} else {
u32::max_value()
u32::MAX
};
let rtomin = if !self.nodelay { self.rx_rto >> 3 } else { 0 };

View file

@ -140,7 +140,7 @@ async fn on_login_request(
session.player_id = Some(player_id);
response.code = ErrorCode::Success.into();
response.timestamp = time_util::unix_timestamp() as i64;
response.timestamp = time_util::unix_timestamp_ms() as i64;
tracing::info!(
"login success, user_id: {}, player_id: {}",

View file

@ -155,11 +155,11 @@ impl Session {
fn next_message(&mut self) -> Option<Message> {
self.decoder.pop_with(|buf| {
Message::decode(&buf)
Message::decode(buf)
.inspect_err(|err| {
tracing::error!(
"failed to decode a message, err: {err}, buf: {}",
hex::encode(&buf)
hex::encode(buf)
)
})
.ok()

View file

@ -15,8 +15,7 @@ impl LengthFieldBasedDecoder {
pub fn input(&mut self, data: &[u8]) {
self.ensure_capacity(data.len());
(&mut self.buffer[self.cur_index..self.cur_index + data.len()]).copy_from_slice(data);
self.buffer[self.cur_index..self.cur_index + data.len()].copy_from_slice(data);
self.cur_index += data.len();
}

View file

@ -64,7 +64,7 @@ impl UdpServer {
conv_id,
addr,
self.socket.clone(),
&self.protokey_helper,
self.protokey_helper,
self.db.clone(),
);
self.session_mgr.add(conv_id, session);

View file

@ -11,13 +11,10 @@ pub async fn handle_login_api_call(
tracing::debug!("login requested");
let user_data = parameters.user_data;
let result = match login(&state, parameters).await {
Ok(result) => result,
Err(err) => {
tracing::warn!("login: internal error occurred {err:?}");
schema::LoginResult::error(-1, String::from("Internal server error"))
}
};
let result = login(&state, parameters).await.unwrap_or_else(|err| {
tracing::warn!("login: internal error occurred {err:?}");
schema::LoginResult::error(-1, String::from("Internal server error"))
});
Json(result.with_user_data(user_data))
}
@ -32,7 +29,7 @@ async fn login(state: &ServiceState, params: schema::LoginParameters) -> Result<
Some(account) => {
if let Some(ban_time_stamp) = account.ban_time_stamp {
if time_util::unix_timestamp() < ban_time_stamp as u64 {
return Ok(schema::LoginResult::banned(String::from("You're banned MF"), ban_time_stamp as i64));
return Ok(schema::LoginResult::banned(String::from("You're banned MF"), ban_time_stamp));
}
}

View file

@ -24,6 +24,12 @@ pub struct Application<S> {
impl Application<()> {
pub fn new() -> Self {
Default::default()
}
}
impl Default for Application<()> {
fn default() -> Self {
Self {
router: Router::new(),
state: (),

View file

@ -15,7 +15,7 @@ impl ServiceMessage {
w.write_u16::<LE>(self.rpc_id)?;
w.write_u16::<LE>(self.message_id)?;
w.write_u32::<LE>(self.data.len() as u32)?;
w.write(&self.data)?;
w.write_all(&self.data)?;
Ok(())
}

View file

@ -28,8 +28,7 @@ impl ServiceListener {
for message in data
.into_vec()
.into_iter()
.map(|b| ServiceMessage::decode(b.as_ref()))
.flatten()
.flat_map(|b| ServiceMessage::decode(b.as_ref()))
{
let _ = sender.send(message).await;
}

View file

@ -15,7 +15,7 @@ pub fn main() {
let config_path = Path::new("proto/config.csv");
if config_path.exists() {
println!("cargo:rerun-if-changed={config_file}");
impl_proto_config(config_path, &Path::new("generated/proto_config.rs")).unwrap();
impl_proto_config(config_path, Path::new("generated/proto_config.rs")).unwrap();
}
let proto_file = "proto/shorekeeper.proto";

View file

@ -43,6 +43,7 @@ message RoleData {
repeated int32 models = 12;
repeated RoleSkillNodeData skill_node_state = 13;
int32 resonant_chain_group_index = 14;
int32 equip_weapon = 15;
}
message RoleFormationData {

View file

@ -92,7 +92,7 @@ impl Message {
let recv_crc = r.read_u32::<LE>()?;
let mut payload = vec![0u8; src.len() - r.position() as usize].into_boxed_slice();
r.read(&mut payload)?;
let _ = r.read(&mut payload)?;
let calc_crc = crc32fast::hash(&payload);

View file

@ -187,6 +187,6 @@ fn encrypt_aes256_ecb_pkcs7(
) -> Result<Box<[u8]>, InvalidLength> {
let cipher = Aes256::new_from_slice(session_key)?;
Ok(cipher
.encrypt_padded_vec::<Pkcs7>(&data[..])
.encrypt_padded_vec::<Pkcs7>(data)
.into_boxed_slice())
}