mirror of
https://github.com/redstrate/Kawari.git
synced 2025-05-13 07:07:45 +00:00
Implement basic zone isolation for multiplayer
Now you don't see every player from every zone, like some kind of madman. The code still sucks, but it works.
This commit is contained in:
parent
9787126a1b
commit
cbeaa83307
3 changed files with 178 additions and 54 deletions
|
@ -76,6 +76,7 @@ pub fn spawn_client(connection: ZoneConnection) {
|
||||||
|
|
||||||
let id = &connection.id.clone();
|
let id = &connection.id.clone();
|
||||||
let ip = &connection.ip.clone();
|
let ip = &connection.ip.clone();
|
||||||
|
let actor_id = connection.player_data.actor_id;
|
||||||
|
|
||||||
let data = ClientData {
|
let data = ClientData {
|
||||||
//id: connection.id,
|
//id: connection.id,
|
||||||
|
@ -93,6 +94,7 @@ pub fn spawn_client(connection: ZoneConnection) {
|
||||||
id: *id,
|
id: *id,
|
||||||
ip: *ip,
|
ip: *ip,
|
||||||
channel: send,
|
channel: send,
|
||||||
|
actor_id,
|
||||||
//kill,
|
//kill,
|
||||||
};
|
};
|
||||||
let _ = my_send.send(handle);
|
let _ = my_send.send(handle);
|
||||||
|
@ -163,8 +165,11 @@ async fn client_loop(
|
||||||
connection.exit_position = Some(connection.player_data.position);
|
connection.exit_position = Some(connection.player_data.position);
|
||||||
connection.exit_rotation = Some(connection.player_data.rotation);
|
connection.exit_rotation = Some(connection.player_data.rotation);
|
||||||
|
|
||||||
|
let mut client_handle = client_handle.clone();
|
||||||
|
client_handle.actor_id = actor_id;
|
||||||
|
|
||||||
// tell the server we exist, now that we confirmed we are a legitimate connection
|
// tell the server we exist, now that we confirmed we are a legitimate connection
|
||||||
connection.handle.send(ToServer::NewClient(client_handle.clone())).await;
|
connection.handle.send(ToServer::NewClient(client_handle)).await;
|
||||||
} else if connection_type == ConnectionType::Chat {
|
} else if connection_type == ConnectionType::Chat {
|
||||||
// We have send THEM a keep alive
|
// We have send THEM a keep alive
|
||||||
connection.send_chat_segment(PacketSegment {
|
connection.send_chat_segment(PacketSegment {
|
||||||
|
@ -314,7 +319,7 @@ async fn client_loop(
|
||||||
}
|
}
|
||||||
ClientZoneIpcData::FinishLoading { .. } => {
|
ClientZoneIpcData::FinishLoading { .. } => {
|
||||||
// tell the server we loaded into the zone, so it can start sending us acors
|
// tell the server we loaded into the zone, so it can start sending us acors
|
||||||
connection.handle.send(ToServer::ZoneLoaded(connection.id)).await;
|
connection.handle.send(ToServer::ZoneLoaded(connection.id, connection.zone.as_ref().unwrap().id)).await;
|
||||||
|
|
||||||
let common = connection.get_player_common_spawn(connection.exit_position, connection.exit_rotation);
|
let common = connection.get_player_common_spawn(connection.exit_position, connection.exit_rotation);
|
||||||
|
|
||||||
|
@ -372,7 +377,7 @@ async fn client_loop(
|
||||||
connection.exit_rotation = None;
|
connection.exit_rotation = None;
|
||||||
|
|
||||||
// tell the other players we're here
|
// tell the other players we're here
|
||||||
connection.handle.send(ToServer::ActorSpawned(connection.id, Actor { id: ObjectId(connection.player_data.actor_id), hp: 100, spawn_index: 0 }, common)).await;
|
connection.handle.send(ToServer::ActorSpawned(connection.id, connection.zone.as_ref().unwrap().id, Actor { id: ObjectId(connection.player_data.actor_id), hp: 100, spawn_index: 0 }, common)).await;
|
||||||
}
|
}
|
||||||
ClientZoneIpcData::ClientTrigger(trigger) => {
|
ClientZoneIpcData::ClientTrigger(trigger) => {
|
||||||
// inform the server of our trigger, it will handle sending it to other clients
|
// inform the server of our trigger, it will handle sending it to other clients
|
||||||
|
|
|
@ -79,6 +79,7 @@ pub struct ClientHandle {
|
||||||
pub id: ClientId,
|
pub id: ClientId,
|
||||||
pub ip: SocketAddr,
|
pub ip: SocketAddr,
|
||||||
pub channel: Sender<FromServer>,
|
pub channel: Sender<FromServer>,
|
||||||
|
pub actor_id: u32,
|
||||||
// TODO: restore, i guess
|
// TODO: restore, i guess
|
||||||
//pub kill: JoinHandle<()>,
|
//pub kill: JoinHandle<()>,
|
||||||
}
|
}
|
||||||
|
@ -108,11 +109,14 @@ impl ClientHandle {
|
||||||
pub enum ToServer {
|
pub enum ToServer {
|
||||||
NewClient(ClientHandle),
|
NewClient(ClientHandle),
|
||||||
Message(ClientId, String),
|
Message(ClientId, String),
|
||||||
ActorSpawned(ClientId, Actor, CommonSpawn),
|
// TODO: ditto, zone id should not be here
|
||||||
|
ActorSpawned(ClientId, u16, Actor, CommonSpawn),
|
||||||
ActorMoved(ClientId, u32, Position, f32),
|
ActorMoved(ClientId, u32, Position, f32),
|
||||||
ActorDespawned(ClientId, u32),
|
ActorDespawned(ClientId, u32),
|
||||||
ClientTrigger(ClientId, u32, ClientTrigger),
|
ClientTrigger(ClientId, u32, ClientTrigger),
|
||||||
ZoneLoaded(ClientId),
|
// TODO: the connection should not be in charge and telling the global server what zone they just loaded in! but this will work for now
|
||||||
|
ZoneLoaded(ClientId, u16),
|
||||||
|
LeftZone(ClientId, u32, u16),
|
||||||
Disconnected(ClientId),
|
Disconnected(ClientId),
|
||||||
FatalError(std::io::Error),
|
FatalError(std::io::Error),
|
||||||
}
|
}
|
||||||
|
@ -365,12 +369,16 @@ impl ZoneConnection {
|
||||||
|
|
||||||
pub async fn change_zone(&mut self, new_zone_id: u16) {
|
pub async fn change_zone(&mut self, new_zone_id: u16) {
|
||||||
// tell everyone we're gone
|
// tell everyone we're gone
|
||||||
// TODO: check if we ever sent an initial ActorSpawn packet first, before sending this.
|
|
||||||
// the connection already checks to see if the actor already exists, so it's seems harmless if we do
|
// the connection already checks to see if the actor already exists, so it's seems harmless if we do
|
||||||
self.handle
|
if self.zone.is_some() {
|
||||||
.send(ToServer::ActorDespawned(self.id, self.player_data.actor_id))
|
self.handle
|
||||||
.await;
|
.send(ToServer::LeftZone(
|
||||||
|
self.id,
|
||||||
|
self.player_data.actor_id,
|
||||||
|
self.zone.as_ref().unwrap().id,
|
||||||
|
))
|
||||||
|
.await;
|
||||||
|
}
|
||||||
{
|
{
|
||||||
let mut game_data = self.gamedata.lock().unwrap();
|
let mut game_data = self.gamedata.lock().unwrap();
|
||||||
self.zone = Some(Zone::load(&mut game_data.game_data, new_zone_id));
|
self.zone = Some(Zone::load(&mut game_data.game_data, new_zone_id));
|
||||||
|
|
|
@ -10,13 +10,62 @@ use crate::{
|
||||||
|
|
||||||
use super::{Actor, ClientHandle, ClientId, FromServer, ToServer};
|
use super::{Actor, ClientHandle, ClientId, FromServer, ToServer};
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Clone)]
|
||||||
struct WorldServer {
|
struct Instance {
|
||||||
clients: HashMap<ClientId, ClientHandle>,
|
zone_id: u16,
|
||||||
// structure temporary, of course
|
// structure temporary, of course
|
||||||
actors: HashMap<ObjectId, CommonSpawn>,
|
actors: HashMap<ObjectId, CommonSpawn>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone)]
|
||||||
|
struct ClientState {
|
||||||
|
zone_id: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
struct WorldServer {
|
||||||
|
clients: HashMap<ClientId, (ClientHandle, ClientState)>,
|
||||||
|
/// Indexed by zone id
|
||||||
|
instances: HashMap<u16, Instance>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WorldServer {
|
||||||
|
/// Finds the instance associated with a zone, or None if it doesn't exist yet.
|
||||||
|
fn find_instance(&self, zone_id: u16) -> Option<&Instance> {
|
||||||
|
self.instances.get(&zone_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds the instance associated with a zone, or creates it if it doesn't exist yet
|
||||||
|
fn find_instance_mut(&mut self, zone_id: u16) -> &mut Instance {
|
||||||
|
if self.instances.contains_key(&zone_id) {
|
||||||
|
self.instances.get_mut(&zone_id).unwrap()
|
||||||
|
} else {
|
||||||
|
self.instances.insert(zone_id, Instance::default());
|
||||||
|
self.instances.get_mut(&zone_id).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds the instance associated with an actor, or returns None if they are not found.
|
||||||
|
fn find_actor_instance(&self, actor_id: u32) -> Option<&Instance> {
|
||||||
|
for (_, instance) in &self.instances {
|
||||||
|
if instance.actors.contains_key(&ObjectId(actor_id)) {
|
||||||
|
return Some(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds the instance associated with an actor, or returns None if they are not found.
|
||||||
|
fn find_actor_instance_mut(&mut self, actor_id: u32) -> Option<&mut Instance> {
|
||||||
|
for (_, instance) in &mut self.instances {
|
||||||
|
if instance.actors.contains_key(&ObjectId(actor_id)) {
|
||||||
|
return Some(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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();
|
let mut to_remove = Vec::new();
|
||||||
|
@ -24,33 +73,70 @@ pub async fn server_main_loop(mut recv: Receiver<ToServer>) -> Result<(), std::i
|
||||||
while let Some(msg) = recv.recv().await {
|
while let Some(msg) = recv.recv().await {
|
||||||
match msg {
|
match msg {
|
||||||
ToServer::NewClient(handle) => {
|
ToServer::NewClient(handle) => {
|
||||||
data.clients.insert(handle.id, handle);
|
data.clients
|
||||||
|
.insert(handle.id, (handle, ClientState::default()));
|
||||||
}
|
}
|
||||||
ToServer::ZoneLoaded(from_id) => {
|
ToServer::ZoneLoaded(from_id, zone_id) => {
|
||||||
for (id, handle) in &mut data.clients {
|
// create a new instance if nessecary
|
||||||
|
if !data.instances.contains_key(&zone_id) {
|
||||||
|
data.instances.insert(zone_id, Instance::default());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send existing player data, if any
|
||||||
|
if let Some(instance) = data.find_instance(zone_id).cloned() {
|
||||||
|
for (id, (handle, state)) in &mut data.clients {
|
||||||
|
let id = *id;
|
||||||
|
|
||||||
|
if id == from_id {
|
||||||
|
state.zone_id = zone_id;
|
||||||
|
|
||||||
|
// send existing player data
|
||||||
|
for (id, common) in &instance.actors {
|
||||||
|
let msg = FromServer::ActorSpawn(
|
||||||
|
Actor {
|
||||||
|
id: *id,
|
||||||
|
hp: 100,
|
||||||
|
spawn_index: 0,
|
||||||
|
},
|
||||||
|
common.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
handle.send(msg).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ToServer::LeftZone(from_id, actor_id, zone_id) => {
|
||||||
|
// when the actor leaves the zone, remove them from the instance
|
||||||
|
let current_instance = data.find_actor_instance_mut(actor_id).unwrap();
|
||||||
|
current_instance.actors.remove(&ObjectId(actor_id));
|
||||||
|
|
||||||
|
// Then tell any clients in the zone that we left
|
||||||
|
for (id, (handle, state)) in &mut data.clients {
|
||||||
let id = *id;
|
let id = *id;
|
||||||
|
|
||||||
|
// don't bother telling the client who told us
|
||||||
if id == from_id {
|
if id == from_id {
|
||||||
// send existing player data
|
continue;
|
||||||
for (id, common) in &data.actors {
|
}
|
||||||
let msg = FromServer::ActorSpawn(
|
|
||||||
Actor {
|
|
||||||
id: *id,
|
|
||||||
hp: 100,
|
|
||||||
spawn_index: 0,
|
|
||||||
},
|
|
||||||
common.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
handle.send(msg).unwrap();
|
// skip any clients not in our zone
|
||||||
}
|
if state.zone_id != zone_id {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
let msg = FromServer::ActorDespawn(actor_id);
|
||||||
|
|
||||||
|
if handle.send(msg).is_err() {
|
||||||
|
to_remove.push(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ToServer::Message(from_id, msg) => {
|
ToServer::Message(from_id, msg) => {
|
||||||
for (id, handle) in &mut data.clients {
|
for (id, (handle, _)) in &mut data.clients {
|
||||||
let id = *id;
|
let id = *id;
|
||||||
|
|
||||||
if id == from_id {
|
if id == from_id {
|
||||||
|
@ -64,16 +150,24 @@ pub async fn server_main_loop(mut recv: Receiver<ToServer>) -> Result<(), std::i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ToServer::ActorSpawned(from_id, actor, common) => {
|
ToServer::ActorSpawned(from_id, zone_id, actor, common) => {
|
||||||
data.actors.insert(actor.id, common.clone());
|
let instance = data.find_instance_mut(zone_id);
|
||||||
|
instance.actors.insert(actor.id, common.clone());
|
||||||
|
|
||||||
for (id, handle) in &mut data.clients {
|
// Then tell any clients in the zone that we spawned
|
||||||
|
for (id, (handle, state)) in &mut data.clients {
|
||||||
let id = *id;
|
let id = *id;
|
||||||
|
|
||||||
|
// don't bother telling the client who told us
|
||||||
if id == from_id {
|
if id == from_id {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// skip any clients not in our zone
|
||||||
|
if state.zone_id != zone_id {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let msg = FromServer::ActorSpawn(actor, common.clone());
|
let msg = FromServer::ActorSpawn(actor, common.clone());
|
||||||
|
|
||||||
if handle.send(msg).is_err() {
|
if handle.send(msg).is_err() {
|
||||||
|
@ -81,12 +175,27 @@ pub async fn server_main_loop(mut recv: Receiver<ToServer>) -> Result<(), std::i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ToServer::ActorDespawned(_from_id, actor_id) => {
|
ToServer::ActorDespawned(from_id, actor_id) => {
|
||||||
data.actors.remove(&ObjectId(actor_id));
|
// NOTE: the order of operations here is very intentional
|
||||||
|
// the client will send actordespawn before we get a ZoneLoaded from the server
|
||||||
|
let current_instance = data.find_actor_instance_mut(actor_id).unwrap();
|
||||||
|
let instance = current_instance.clone();
|
||||||
|
current_instance.actors.remove(&ObjectId(actor_id));
|
||||||
|
|
||||||
for (id, handle) in &mut data.clients {
|
// Then tell any clients in the zone that we left
|
||||||
|
for (id, (handle, state)) in &mut data.clients {
|
||||||
let id = *id;
|
let id = *id;
|
||||||
|
|
||||||
|
// don't bother telling the client who told us
|
||||||
|
if id == from_id {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip any clients not in our zone
|
||||||
|
if state.zone_id != instance.zone_id {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let msg = FromServer::ActorDespawn(actor_id);
|
let msg = FromServer::ActorDespawn(actor_id);
|
||||||
|
|
||||||
if handle.send(msg).is_err() {
|
if handle.send(msg).is_err() {
|
||||||
|
@ -95,31 +204,33 @@ pub async fn server_main_loop(mut recv: Receiver<ToServer>) -> Result<(), std::i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ToServer::ActorMoved(from_id, actor_id, position, rotation) => {
|
ToServer::ActorMoved(from_id, actor_id, position, rotation) => {
|
||||||
if let Some((_, common)) = data
|
if let Some(instance) = data.find_actor_instance_mut(actor_id) {
|
||||||
.actors
|
if let Some((_, common)) = instance
|
||||||
.iter_mut()
|
.actors
|
||||||
.find(|actor| *actor.0 == ObjectId(actor_id))
|
.iter_mut()
|
||||||
{
|
.find(|actor| *actor.0 == ObjectId(actor_id))
|
||||||
common.pos = position;
|
{
|
||||||
common.rotation = rotation;
|
common.pos = position;
|
||||||
}
|
common.rotation = rotation;
|
||||||
|
|
||||||
for (id, handle) in &mut data.clients {
|
|
||||||
let id = *id;
|
|
||||||
|
|
||||||
if id == from_id {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let msg = FromServer::ActorMove(actor_id, position, rotation);
|
for (id, (handle, _)) in &mut data.clients {
|
||||||
|
let id = *id;
|
||||||
|
|
||||||
if handle.send(msg).is_err() {
|
if id == from_id {
|
||||||
to_remove.push(id);
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let msg = FromServer::ActorMove(actor_id, position, rotation);
|
||||||
|
|
||||||
|
if handle.send(msg).is_err() {
|
||||||
|
to_remove.push(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ToServer::ClientTrigger(from_id, from_actor_id, trigger) => {
|
ToServer::ClientTrigger(from_id, from_actor_id, trigger) => {
|
||||||
for (id, handle) in &mut data.clients {
|
for (id, (handle, _)) in &mut data.clients {
|
||||||
let id = *id;
|
let id = *id;
|
||||||
|
|
||||||
// there's no reason to tell the actor what it just did
|
// there's no reason to tell the actor what it just did
|
||||||
|
|
Loading…
Add table
Reference in a new issue