mirror of
https://github.com/redstrate/Kawari.git
synced 2025-06-21 07:27:45 +00:00
Make !spawnnpc debug command networked
Next step is allocating actor IDs properly, instead of using a fixed value.
This commit is contained in:
parent
3757f4e0db
commit
97cdc66ec7
3 changed files with 124 additions and 101 deletions
|
@ -1,10 +1,9 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
common::{CustomizeData, ObjectId, ObjectTypeId, timestamp_secs},
|
common::{ObjectId, ObjectTypeId, timestamp_secs},
|
||||||
inventory::Storage,
|
inventory::Storage,
|
||||||
ipc::zone::{
|
ipc::zone::{
|
||||||
ActorControl, ActorControlCategory, ActorControlSelf, BattleNpcSubKind, ChatMessage,
|
ActorControl, ActorControlCategory, ActorControlSelf, ChatMessage, EventStart, NpcSpawn,
|
||||||
CommonSpawn, EventStart, NpcSpawn, ObjectKind, OnlineStatus, ServerZoneIpcData,
|
OnlineStatus, ServerZoneIpcData, ServerZoneIpcSegment,
|
||||||
ServerZoneIpcSegment,
|
|
||||||
},
|
},
|
||||||
opcodes::ServerZoneIpcType,
|
opcodes::ServerZoneIpcType,
|
||||||
packet::{PacketSegment, SegmentData, SegmentType},
|
packet::{PacketSegment, SegmentData, SegmentType},
|
||||||
|
@ -13,35 +12,6 @@ use crate::{
|
||||||
|
|
||||||
use super::{LuaPlayer, ZoneConnection};
|
use super::{LuaPlayer, ZoneConnection};
|
||||||
|
|
||||||
pub const CUSTOMIZE_DATA: CustomizeData = CustomizeData {
|
|
||||||
race: 4,
|
|
||||||
gender: 1,
|
|
||||||
age: 1,
|
|
||||||
height: 50,
|
|
||||||
subrace: 7,
|
|
||||||
face: 3,
|
|
||||||
hair: 5,
|
|
||||||
enable_highlights: 0,
|
|
||||||
skin_tone: 10,
|
|
||||||
right_eye_color: 75,
|
|
||||||
hair_tone: 50,
|
|
||||||
highlights: 0,
|
|
||||||
facial_features: 1,
|
|
||||||
facial_feature_color: 19,
|
|
||||||
eyebrows: 1,
|
|
||||||
left_eye_color: 75,
|
|
||||||
eyes: 1,
|
|
||||||
nose: 0,
|
|
||||||
jaw: 1,
|
|
||||||
mouth: 1,
|
|
||||||
lips_tone_fur_pattern: 169,
|
|
||||||
race_feature_size: 100,
|
|
||||||
race_feature_type: 1,
|
|
||||||
bust: 100,
|
|
||||||
face_paint: 0,
|
|
||||||
face_paint_color: 167,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct ChatHandler {}
|
pub struct ChatHandler {}
|
||||||
|
|
||||||
impl ChatHandler {
|
impl ChatHandler {
|
||||||
|
@ -55,58 +25,18 @@ impl ChatHandler {
|
||||||
let parts: Vec<&str> = chat_message.message.split(' ').collect();
|
let parts: Vec<&str> = chat_message.message.split(' ').collect();
|
||||||
match parts[0] {
|
match parts[0] {
|
||||||
"!spawnnpc" => {
|
"!spawnnpc" => {
|
||||||
let ipc = ServerZoneIpcSegment {
|
|
||||||
op_code: ServerZoneIpcType::NpcSpawn,
|
|
||||||
timestamp: timestamp_secs(),
|
|
||||||
data: ServerZoneIpcData::NpcSpawn(NpcSpawn {
|
|
||||||
common: CommonSpawn {
|
|
||||||
hp_curr: 100,
|
|
||||||
hp_max: 100,
|
|
||||||
mp_curr: 100,
|
|
||||||
mp_max: 100,
|
|
||||||
look: CUSTOMIZE_DATA,
|
|
||||||
spawn_index: connection.get_free_spawn_index(),
|
|
||||||
bnpc_base: 13498,
|
|
||||||
bnpc_name: 10261,
|
|
||||||
object_kind: ObjectKind::BattleNpc(BattleNpcSubKind::Enemy),
|
|
||||||
target_id: ObjectTypeId {
|
|
||||||
object_id: ObjectId(connection.player_data.actor_id),
|
|
||||||
object_type: 0,
|
|
||||||
}, // target the player
|
|
||||||
level: 1,
|
|
||||||
models: [
|
|
||||||
0, // head
|
|
||||||
89, // body
|
|
||||||
89, // hands
|
|
||||||
89, // legs
|
|
||||||
89, // feet
|
|
||||||
0, // ears
|
|
||||||
0, // neck
|
|
||||||
0, // wrists
|
|
||||||
0, // left finger
|
|
||||||
0, // right finger
|
|
||||||
],
|
|
||||||
pos: connection.player_data.position,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
connection
|
connection
|
||||||
.send_segment(PacketSegment {
|
.handle
|
||||||
source_actor: 0x106ad804,
|
.send(ToServer::DebugNewNpc(
|
||||||
target_actor: connection.player_data.actor_id,
|
connection.id,
|
||||||
segment_type: SegmentType::Ipc,
|
connection.player_data.actor_id,
|
||||||
data: SegmentData::Ipc { data: ipc },
|
))
|
||||||
})
|
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
"!spawnmonster" => {
|
"!spawnmonster" => {
|
||||||
connection
|
connection
|
||||||
.handle
|
.handle
|
||||||
.send(ToServer::DebugNewNpc(
|
.send(ToServer::DebugNewEnemy(
|
||||||
connection.id,
|
connection.id,
|
||||||
connection.player_data.actor_id,
|
connection.player_data.actor_id,
|
||||||
))
|
))
|
||||||
|
|
|
@ -88,7 +88,10 @@ pub enum ToServer {
|
||||||
Disconnected(ClientId),
|
Disconnected(ClientId),
|
||||||
/// A fatal error occured.
|
/// A fatal error occured.
|
||||||
FatalError(std::io::Error),
|
FatalError(std::io::Error),
|
||||||
|
/// Spawn a friendly debug NPC.
|
||||||
DebugNewNpc(ClientId, u32),
|
DebugNewNpc(ClientId, u32),
|
||||||
|
/// Spawn an enemy debug NPC.
|
||||||
|
DebugNewEnemy(ClientId, u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::collections::HashMap;
|
||||||
use tokio::sync::mpsc::Receiver;
|
use tokio::sync::mpsc::Receiver;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
common::ObjectId,
|
common::{CustomizeData, ObjectId, ObjectTypeId},
|
||||||
ipc::zone::{
|
ipc::zone::{
|
||||||
ActorControl, ActorControlCategory, ActorControlSelf, ActorControlTarget, BattleNpcSubKind,
|
ActorControl, ActorControlCategory, ActorControlSelf, ActorControlTarget, BattleNpcSubKind,
|
||||||
ClientTriggerCommand, CommonSpawn, NpcSpawn, ObjectKind,
|
ClientTriggerCommand, CommonSpawn, NpcSpawn, ObjectKind,
|
||||||
|
@ -11,6 +11,36 @@ use crate::{
|
||||||
|
|
||||||
use super::{Actor, ClientHandle, ClientId, FromServer, ToServer};
|
use super::{Actor, ClientHandle, ClientId, FromServer, ToServer};
|
||||||
|
|
||||||
|
/// Used for the debug NPC.
|
||||||
|
pub const CUSTOMIZE_DATA: CustomizeData = CustomizeData {
|
||||||
|
race: 4,
|
||||||
|
gender: 1,
|
||||||
|
age: 1,
|
||||||
|
height: 50,
|
||||||
|
subrace: 7,
|
||||||
|
face: 3,
|
||||||
|
hair: 5,
|
||||||
|
enable_highlights: 0,
|
||||||
|
skin_tone: 10,
|
||||||
|
right_eye_color: 75,
|
||||||
|
hair_tone: 50,
|
||||||
|
highlights: 0,
|
||||||
|
facial_features: 1,
|
||||||
|
facial_feature_color: 19,
|
||||||
|
eyebrows: 1,
|
||||||
|
left_eye_color: 75,
|
||||||
|
eyes: 1,
|
||||||
|
nose: 0,
|
||||||
|
jaw: 1,
|
||||||
|
mouth: 1,
|
||||||
|
lips_tone_fur_pattern: 169,
|
||||||
|
race_feature_size: 100,
|
||||||
|
race_feature_type: 1,
|
||||||
|
bust: 100,
|
||||||
|
face_paint: 0,
|
||||||
|
face_paint_color: 167,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
enum NetworkedActor {
|
enum NetworkedActor {
|
||||||
Player(NpcSpawn),
|
Player(NpcSpawn),
|
||||||
|
@ -40,6 +70,7 @@ struct ClientState {
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
struct WorldServer {
|
struct WorldServer {
|
||||||
|
to_remove: Vec<ClientId>,
|
||||||
clients: HashMap<ClientId, (ClientHandle, ClientState)>,
|
clients: HashMap<ClientId, (ClientHandle, ClientState)>,
|
||||||
/// Indexed by zone id
|
/// Indexed by zone id
|
||||||
instances: HashMap<u16, Instance>,
|
instances: HashMap<u16, Instance>,
|
||||||
|
@ -70,11 +101,24 @@ impl WorldServer {
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tell all the clients that a new NPC spawned.
|
||||||
|
fn send_npc(&mut self, spawn: NpcSpawn) {
|
||||||
|
// TODO: only send in the relevant instance
|
||||||
|
for (id, (handle, _)) in &mut self.clients {
|
||||||
|
let id = *id;
|
||||||
|
|
||||||
|
let msg = FromServer::SpawnNPC(spawn.clone());
|
||||||
|
|
||||||
|
if handle.send(msg).is_err() {
|
||||||
|
self.to_remove.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn server_main_loop(mut recv: Receiver<ToServer>) -> Result<(), std::io::Error> {
|
pub async fn server_main_loop(mut recv: Receiver<ToServer>) -> Result<(), std::io::Error> {
|
||||||
let mut data = WorldServer::default();
|
let mut data = WorldServer::default();
|
||||||
let mut to_remove = Vec::new();
|
|
||||||
|
|
||||||
while let Some(msg) = recv.recv().await {
|
while let Some(msg) = recv.recv().await {
|
||||||
match msg {
|
match msg {
|
||||||
|
@ -162,7 +206,7 @@ pub async fn server_main_loop(mut recv: Receiver<ToServer>) -> Result<(), std::i
|
||||||
);
|
);
|
||||||
|
|
||||||
if handle.send(msg).is_err() {
|
if handle.send(msg).is_err() {
|
||||||
to_remove.push(id);
|
data.to_remove.push(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -188,7 +232,7 @@ pub async fn server_main_loop(mut recv: Receiver<ToServer>) -> Result<(), std::i
|
||||||
let msg = FromServer::ActorDespawn(actor_id);
|
let msg = FromServer::ActorDespawn(actor_id);
|
||||||
|
|
||||||
if handle.send(msg).is_err() {
|
if handle.send(msg).is_err() {
|
||||||
to_remove.push(id);
|
data.to_remove.push(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -203,7 +247,7 @@ pub async fn server_main_loop(mut recv: Receiver<ToServer>) -> Result<(), std::i
|
||||||
let msg = FromServer::Message(msg.clone());
|
let msg = FromServer::Message(msg.clone());
|
||||||
|
|
||||||
if handle.send(msg).is_err() {
|
if handle.send(msg).is_err() {
|
||||||
to_remove.push(id);
|
data.to_remove.push(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -232,7 +276,7 @@ pub async fn server_main_loop(mut recv: Receiver<ToServer>) -> Result<(), std::i
|
||||||
let msg = FromServer::ActorMove(actor_id, position, rotation);
|
let msg = FromServer::ActorMove(actor_id, position, rotation);
|
||||||
|
|
||||||
if handle.send(msg).is_err() {
|
if handle.send(msg).is_err() {
|
||||||
to_remove.push(id);
|
data.to_remove.push(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -255,7 +299,7 @@ pub async fn server_main_loop(mut recv: Receiver<ToServer>) -> Result<(), std::i
|
||||||
});
|
});
|
||||||
|
|
||||||
if handle.send(msg).is_err() {
|
if handle.send(msg).is_err() {
|
||||||
to_remove.push(id);
|
data.to_remove.push(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -275,7 +319,7 @@ pub async fn server_main_loop(mut recv: Receiver<ToServer>) -> Result<(), std::i
|
||||||
);
|
);
|
||||||
|
|
||||||
if handle.send(msg).is_err() {
|
if handle.send(msg).is_err() {
|
||||||
to_remove.push(id);
|
data.to_remove.push(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ClientTriggerCommand::ChangePose { unk1, pose } => {
|
ClientTriggerCommand::ChangePose { unk1, pose } => {
|
||||||
|
@ -290,7 +334,7 @@ pub async fn server_main_loop(mut recv: Receiver<ToServer>) -> Result<(), std::i
|
||||||
);
|
);
|
||||||
|
|
||||||
if handle.send(msg).is_err() {
|
if handle.send(msg).is_err() {
|
||||||
to_remove.push(id);
|
data.to_remove.push(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ClientTriggerCommand::ReapplyPose { unk1, pose } => {
|
ClientTriggerCommand::ReapplyPose { unk1, pose } => {
|
||||||
|
@ -305,7 +349,7 @@ pub async fn server_main_loop(mut recv: Receiver<ToServer>) -> Result<(), std::i
|
||||||
);
|
);
|
||||||
|
|
||||||
if handle.send(msg).is_err() {
|
if handle.send(msg).is_err() {
|
||||||
to_remove.push(id);
|
data.to_remove.push(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => tracing::warn!("Server doesn't know what to do with {:#?}", trigger),
|
_ => tracing::warn!("Server doesn't know what to do with {:#?}", trigger),
|
||||||
|
@ -313,6 +357,60 @@ pub async fn server_main_loop(mut recv: Receiver<ToServer>) -> Result<(), std::i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ToServer::DebugNewNpc(_from_id, from_actor_id) => {
|
ToServer::DebugNewNpc(_from_id, from_actor_id) => {
|
||||||
|
let spawn;
|
||||||
|
{
|
||||||
|
let Some(instance) = data.find_actor_instance_mut(from_actor_id) else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(actor) = instance.find_actor(ObjectId(from_actor_id)) else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
let NetworkedActor::Player(player) = actor else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
spawn = NpcSpawn {
|
||||||
|
aggression_mode: 1,
|
||||||
|
common: CommonSpawn {
|
||||||
|
hp_curr: 100,
|
||||||
|
hp_max: 100,
|
||||||
|
mp_curr: 100,
|
||||||
|
mp_max: 100,
|
||||||
|
look: CUSTOMIZE_DATA,
|
||||||
|
bnpc_base: 13498,
|
||||||
|
bnpc_name: 10261,
|
||||||
|
object_kind: ObjectKind::BattleNpc(BattleNpcSubKind::Enemy),
|
||||||
|
target_id: ObjectTypeId {
|
||||||
|
object_id: ObjectId(from_actor_id),
|
||||||
|
object_type: 0,
|
||||||
|
}, // target the player
|
||||||
|
level: 1,
|
||||||
|
models: [
|
||||||
|
0, // head
|
||||||
|
89, // body
|
||||||
|
89, // hands
|
||||||
|
89, // legs
|
||||||
|
89, // feet
|
||||||
|
0, // ears
|
||||||
|
0, // neck
|
||||||
|
0, // wrists
|
||||||
|
0, // left finger
|
||||||
|
0, // right finger
|
||||||
|
],
|
||||||
|
pos: player.common.pos,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
instance.insert_npc(ObjectId(1), spawn.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
data.send_npc(spawn);
|
||||||
|
}
|
||||||
|
ToServer::DebugNewEnemy(_from_id, from_actor_id) => {
|
||||||
let spawn;
|
let spawn;
|
||||||
{
|
{
|
||||||
let Some(instance) = data.find_actor_instance_mut(from_actor_id) else {
|
let Some(instance) = data.find_actor_instance_mut(from_actor_id) else {
|
||||||
|
@ -350,28 +448,20 @@ pub async fn server_main_loop(mut recv: Receiver<ToServer>) -> Result<(), std::i
|
||||||
instance.insert_npc(ObjectId(1), spawn.clone());
|
instance.insert_npc(ObjectId(1), spawn.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (id, (handle, _)) in &mut data.clients {
|
data.send_npc(spawn);
|
||||||
let id = *id;
|
|
||||||
|
|
||||||
let msg = FromServer::SpawnNPC(spawn.clone());
|
|
||||||
|
|
||||||
if handle.send(msg).is_err() {
|
|
||||||
to_remove.push(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ToServer::Disconnected(from_id) => {
|
ToServer::Disconnected(from_id) => {
|
||||||
to_remove.push(from_id);
|
data.to_remove.push(from_id);
|
||||||
}
|
}
|
||||||
ToServer::FatalError(err) => return Err(err),
|
ToServer::FatalError(err) => return Err(err),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove any clients that errored out
|
// Remove any clients that errored out
|
||||||
for remove_id in &to_remove {
|
for remove_id in data.to_remove.clone() {
|
||||||
// remove any actors they had
|
// remove any actors they had
|
||||||
let mut actor_id = None;
|
let mut actor_id = None;
|
||||||
for (id, (handle, _)) in &mut data.clients {
|
for (id, (handle, _)) in &mut data.clients {
|
||||||
if *id == *remove_id {
|
if *id == remove_id {
|
||||||
actor_id = Some(handle.actor_id);
|
actor_id = Some(handle.actor_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue