matchmake against a bot instead of searching forever
537 only tells the client's ui to start searching, the handshake after it was missing: server sends 24106 StopHomeLogic, client answers 14105, server sends 21903. that last step now builds the same snapshot the npc mission does, using npcs row 0 as the opponent. battle type stays 1 so it runs on the client's offline path. a real pvp battle is type 0 and needs the udp sector channel, which does not exist here. 14107 CancelMatchmake now answers 24125 instead of being ignored.
This commit is contained in:
parent
774e7c60cf
commit
6dc4caede9
9 changed files with 141 additions and 34 deletions
|
|
@ -26,6 +26,7 @@ fn diamonds() -> LogicDataRef {
|
||||||
pub struct TurnResult {
|
pub struct TurnResult {
|
||||||
pub claims: Vec<LogicClaimRewardCommand>,
|
pub claims: Vec<LogicClaimRewardCommand>,
|
||||||
pub purchases: Vec<Purchase>,
|
pub purchases: Vec<Purchase>,
|
||||||
|
pub matchmake: bool,
|
||||||
pub out_of_sync: Option<OutOfSyncMessage>,
|
pub out_of_sync: Option<OutOfSyncMessage>,
|
||||||
pub changed: bool,
|
pub changed: bool,
|
||||||
}
|
}
|
||||||
|
|
@ -214,6 +215,7 @@ impl HomeMode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
CommandOutcome::MatchmakeStarted => result.matchmake = true,
|
||||||
CommandOutcome::Applied => result.changed = true,
|
CommandOutcome::Applied => result.changed = true,
|
||||||
CommandOutcome::Ignored => {}
|
CommandOutcome::Ignored => {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use logic::table;
|
use logic::{table, LogicDataTables};
|
||||||
use logic::{
|
use logic::{
|
||||||
AvailableServerCommandMessage, EndClientTurnMessage, LogicCommandManager, LogicDataRef,
|
AvailableServerCommandMessage, EndClientTurnMessage, LogicCommandManager, LogicDataRef,
|
||||||
LogicShopSeedChangedCommand, OutOfSyncMessage, OwnHomeDataMessage, SectorStateMessage,
|
LogicShopSeedChangedCommand, OutOfSyncMessage, OwnHomeDataMessage, SectorStateMessage,
|
||||||
StartMissionMessage,
|
StartMissionMessage, StopHomeLogicMessage,
|
||||||
};
|
};
|
||||||
use service_rpc::{
|
use service_rpc::{
|
||||||
AccountRef, GameApi, GameRequest, GameResponse, HomeRequestKind, RpcError, RpcResult,
|
AccountRef, GameApi, GameRequest, GameResponse, HomeRequestKind, RpcError, RpcResult,
|
||||||
|
|
@ -77,6 +77,47 @@ impl GameService {
|
||||||
pub fn shop(&self) -> &Arc<ShopCatalog> {
|
pub fn shop(&self) -> &Arc<ShopCatalog> {
|
||||||
&self.shop
|
&self.shop
|
||||||
}
|
}
|
||||||
|
async fn sector_state_for(
|
||||||
|
&self,
|
||||||
|
account: AccountRef,
|
||||||
|
npc: LogicDataRef,
|
||||||
|
) -> RpcResult<Vec<WireMessage>> {
|
||||||
|
let profile = self.profile(account).await?;
|
||||||
|
let avatar = build_avatar(&profile);
|
||||||
|
let location = npc
|
||||||
|
.data()
|
||||||
|
.map(|row| LogicDataRef::by_name(table::LOCATIONS, row.string("Location")))
|
||||||
|
.unwrap_or_default();
|
||||||
|
let battle = self
|
||||||
|
.battles
|
||||||
|
.build(
|
||||||
|
location.clone(),
|
||||||
|
npc.clone(),
|
||||||
|
profile.arena.clone(),
|
||||||
|
vec![avatar.clone(), avatar],
|
||||||
|
self.config.random_seed,
|
||||||
|
)
|
||||||
|
.map_err(|error| RpcError::Rejected(error.to_string()))?;
|
||||||
|
let snapshot = battle
|
||||||
|
.snapshot()
|
||||||
|
.map_err(|error| RpcError::Rejected(error.to_string()))?;
|
||||||
|
tracing::info!(
|
||||||
|
%account,
|
||||||
|
npc = %npc,
|
||||||
|
location = %location,
|
||||||
|
objects = battle.battle.objects.objects.len(),
|
||||||
|
bytes = snapshot.len(),
|
||||||
|
"sending a battle sector state"
|
||||||
|
);
|
||||||
|
Ok(vec![encode(&SectorStateMessage::new(snapshot))?])
|
||||||
|
}
|
||||||
|
fn bot_opponent(&self) -> LogicDataRef {
|
||||||
|
LogicDataTables::instance()
|
||||||
|
.table(table::NPCS)
|
||||||
|
.and_then(|rows| rows.get_at(0).cloned())
|
||||||
|
.map(LogicDataRef::from)
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
async fn roller(&self, account: AccountRef) -> RpcResult<RewardRoller> {
|
async fn roller(&self, account: AccountRef) -> RpcResult<RewardRoller> {
|
||||||
let profile = self.profile(account).await?;
|
let profile = self.profile(account).await?;
|
||||||
let fallback: Vec<LogicDataRef> = profile
|
let fallback: Vec<LogicDataRef> = profile
|
||||||
|
|
@ -178,35 +219,21 @@ impl GameApi for GameService {
|
||||||
) -> RpcResult<Vec<WireMessage>> {
|
) -> RpcResult<Vec<WireMessage>> {
|
||||||
let mission = StartMissionMessage::from_bytes(&payload)
|
let mission = StartMissionMessage::from_bytes(&payload)
|
||||||
.map_err(|error| RpcError::Rejected(error.to_string()))?;
|
.map_err(|error| RpcError::Rejected(error.to_string()))?;
|
||||||
let profile = self.profile(account).await?;
|
self.sector_state_for(account, mission.npc).await
|
||||||
let avatar = build_avatar(&profile);
|
}
|
||||||
let location = mission
|
async fn home_logic_stopped(
|
||||||
.npc
|
&self,
|
||||||
.data()
|
account: AccountRef,
|
||||||
.map(|npc| LogicDataRef::by_name(table::LOCATIONS, npc.string("Location")))
|
_payload: Vec<u8>,
|
||||||
.unwrap_or_default();
|
) -> RpcResult<Vec<WireMessage>> {
|
||||||
let battle = self
|
let npc = self.bot_opponent();
|
||||||
.battles
|
if npc.is_none() {
|
||||||
.build(
|
return Err(RpcError::Rejected(
|
||||||
location.clone(),
|
"npcs.csv has no bot to match against".into(),
|
||||||
mission.npc.clone(),
|
));
|
||||||
profile.arena.clone(),
|
}
|
||||||
vec![avatar.clone(), avatar],
|
tracing::info!(%account, bot = %npc, "matchmaking against a bot");
|
||||||
self.config.random_seed,
|
self.sector_state_for(account, npc).await
|
||||||
)
|
|
||||||
.map_err(|error| RpcError::Rejected(error.to_string()))?;
|
|
||||||
let snapshot = battle
|
|
||||||
.snapshot()
|
|
||||||
.map_err(|error| RpcError::Rejected(error.to_string()))?;
|
|
||||||
tracing::info!(
|
|
||||||
%account,
|
|
||||||
npc = %mission.npc,
|
|
||||||
location = %location,
|
|
||||||
objects = battle.battle.objects.objects.len(),
|
|
||||||
bytes = snapshot.len(),
|
|
||||||
"sending a battle sector state"
|
|
||||||
);
|
|
||||||
Ok(vec![encode(&SectorStateMessage::new(snapshot))?])
|
|
||||||
}
|
}
|
||||||
async fn end_client_turn(
|
async fn end_client_turn(
|
||||||
&self,
|
&self,
|
||||||
|
|
@ -238,6 +265,10 @@ impl GameApi for GameService {
|
||||||
"end client turn"
|
"end client turn"
|
||||||
);
|
);
|
||||||
let mut replies = Vec::new();
|
let mut replies = Vec::new();
|
||||||
|
if result.matchmake {
|
||||||
|
tracing::info!(%account, "client started matchmaking, stopping home logic");
|
||||||
|
replies.push(encode(&StopHomeLogicMessage::default())?);
|
||||||
|
}
|
||||||
if let Some(out_of_sync) = result.out_of_sync {
|
if let Some(out_of_sync) = result.out_of_sync {
|
||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
%account,
|
%account,
|
||||||
|
|
@ -304,6 +335,9 @@ impl RpcService for GameService {
|
||||||
GameRequest::StartMission { account, payload } => Ok(GameResponse::messages(
|
GameRequest::StartMission { account, payload } => Ok(GameResponse::messages(
|
||||||
self.start_mission(account, payload).await?,
|
self.start_mission(account, payload).await?,
|
||||||
)),
|
)),
|
||||||
|
GameRequest::HomeLogicStopped { account, payload } => Ok(GameResponse::messages(
|
||||||
|
self.home_logic_stopped(account, payload).await?,
|
||||||
|
)),
|
||||||
GameRequest::EndClientTurn { account, payload } => Ok(GameResponse::messages(
|
GameRequest::EndClientTurn { account, payload } => Ok(GameResponse::messages(
|
||||||
self.end_client_turn(account, payload).await?,
|
self.end_client_turn(account, payload).await?,
|
||||||
)),
|
)),
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,17 @@ impl GameApi for RemoteGame {
|
||||||
.await?
|
.await?
|
||||||
.into_messages())
|
.into_messages())
|
||||||
}
|
}
|
||||||
|
async fn home_logic_stopped(
|
||||||
|
&self,
|
||||||
|
account: AccountRef,
|
||||||
|
payload: Vec<u8>,
|
||||||
|
) -> RpcResult<Vec<WireMessage>> {
|
||||||
|
Ok(self
|
||||||
|
.client
|
||||||
|
.call(&GameRequest::HomeLogicStopped { account, payload })
|
||||||
|
.await?
|
||||||
|
.into_messages())
|
||||||
|
}
|
||||||
async fn end_client_turn(
|
async fn end_client_turn(
|
||||||
&self,
|
&self,
|
||||||
account: AccountRef,
|
account: AccountRef,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use logic::{
|
use logic::{
|
||||||
message_type, ClientCapabilitiesMessage, GoHomeMessage, KeepAliveServerMessage,
|
message_type, CancelMatchmakeDoneMessage, ClientCapabilitiesMessage, GoHomeMessage,
|
||||||
LoginFailedMessage, LoginMessage, LoginOkMessage, ServerErrorMessage,
|
KeepAliveServerMessage, LoginFailedMessage, LoginMessage, LoginOkMessage, ServerErrorMessage,
|
||||||
};
|
};
|
||||||
use service_rpc::{
|
use service_rpc::{
|
||||||
AccountRef, DeviceInfo, HomeRequestKind, LoginOutcome, Session as AuthSession, WireMessage,
|
AccountRef, DeviceInfo, HomeRequestKind, LoginOutcome, Session as AuthSession, WireMessage,
|
||||||
|
|
@ -88,6 +88,33 @@ impl MessageManager {
|
||||||
let _ = incoming.downcast::<GoHomeMessage>();
|
let _ = incoming.downcast::<GoHomeMessage>();
|
||||||
self.push_home(HomeRequestKind::GoHome).await
|
self.push_home(HomeRequestKind::GoHome).await
|
||||||
}
|
}
|
||||||
|
message_type::CANCEL_MATCHMAKE => {
|
||||||
|
tracing::info!(peer = %self.peer, "client cancelled matchmaking");
|
||||||
|
self.sender
|
||||||
|
.send_message(&CancelMatchmakeDoneMessage::default())
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
message_type::HOME_LOGIC_STOPPED => {
|
||||||
|
let Some(account) = self.account else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
match self
|
||||||
|
.backends
|
||||||
|
.game
|
||||||
|
.home_logic_stopped(account, incoming.payload)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(replies) => self.push_wire_messages(replies).await?,
|
||||||
|
Err(error) => {
|
||||||
|
tracing::warn!(peer = %self.peer, %error, "could not match the player");
|
||||||
|
self.sender
|
||||||
|
.send_message(&CancelMatchmakeDoneMessage::default())
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
message_type::START_MISSION => {
|
message_type::START_MISSION => {
|
||||||
let Some(account) = self.account else {
|
let Some(account) = self.account else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ pub enum CommandOutcome {
|
||||||
data: LogicDataRef,
|
data: LogicDataRef,
|
||||||
count: i32,
|
count: i32,
|
||||||
},
|
},
|
||||||
|
MatchmakeStarted,
|
||||||
Applied,
|
Applied,
|
||||||
Rejected(&'static str),
|
Rejected(&'static str),
|
||||||
Ignored,
|
Ignored,
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,6 @@ impl Execute for LogicStartMatchmakeCommand {
|
||||||
if mode.is_claiming_reward() {
|
if mode.is_claiming_reward() {
|
||||||
return CommandOutcome::Rejected("a reward claim is in progress");
|
return CommandOutcome::Rejected("a reward claim is in progress");
|
||||||
}
|
}
|
||||||
CommandOutcome::Ignored
|
CommandOutcome::MatchmakeStarted
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
18
crates/logic/src/messages/matchmake.rs
Normal file
18
crates/logic/src/messages/matchmake.rs
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
use titan::Message;
|
||||||
|
use crate::commands::LogicCommand;
|
||||||
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Message)]
|
||||||
|
#[message(id = 24106, direction = "server", name = "StopHomeLogicMessage")]
|
||||||
|
pub struct StopHomeLogicMessage {}
|
||||||
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Message)]
|
||||||
|
#[message(id = 24125, direction = "server", name = "CancelMatchmakeDoneMessage")]
|
||||||
|
pub struct CancelMatchmakeDoneMessage {}
|
||||||
|
#[derive(Debug, Default, Message)]
|
||||||
|
#[message(id = 14105, direction = "client", name = "HomeLogicStoppedMessage")]
|
||||||
|
#[codec(partial)]
|
||||||
|
pub struct HomeLogicStoppedMessage {
|
||||||
|
#[codec(vint)]
|
||||||
|
pub tick: i32,
|
||||||
|
#[codec(vint)]
|
||||||
|
pub checksum: i32,
|
||||||
|
pub commands: Vec<Box<dyn LogicCommand>>,
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ mod keep_alive;
|
||||||
mod login;
|
mod login;
|
||||||
mod login_failed;
|
mod login_failed;
|
||||||
mod login_ok;
|
mod login_ok;
|
||||||
|
mod matchmake;
|
||||||
mod out_of_sync;
|
mod out_of_sync;
|
||||||
mod own_home_data;
|
mod own_home_data;
|
||||||
mod sector_state;
|
mod sector_state;
|
||||||
|
|
@ -28,6 +29,7 @@ pub use keep_alive::{KeepAliveMessage, KeepAliveServerMessage};
|
||||||
pub use login::LoginMessage;
|
pub use login::LoginMessage;
|
||||||
pub use login_failed::{LoginFailedMessage, LoginFailureReason};
|
pub use login_failed::{LoginFailedMessage, LoginFailureReason};
|
||||||
pub use login_ok::LoginOkMessage;
|
pub use login_ok::LoginOkMessage;
|
||||||
|
pub use matchmake::{CancelMatchmakeDoneMessage, HomeLogicStoppedMessage, StopHomeLogicMessage};
|
||||||
pub use out_of_sync::OutOfSyncMessage;
|
pub use out_of_sync::OutOfSyncMessage;
|
||||||
pub use own_home_data::OwnHomeDataMessage;
|
pub use own_home_data::OwnHomeDataMessage;
|
||||||
pub use sector_state::SectorStateMessage;
|
pub use sector_state::SectorStateMessage;
|
||||||
|
|
@ -45,6 +47,8 @@ pub mod message_type {
|
||||||
pub const GO_HOME: u16 = 14101;
|
pub const GO_HOME: u16 = 14101;
|
||||||
pub const END_CLIENT_TURN: u16 = 14102;
|
pub const END_CLIENT_TURN: u16 = 14102;
|
||||||
pub const START_MISSION: u16 = 14104;
|
pub const START_MISSION: u16 = 14104;
|
||||||
|
pub const HOME_LOGIC_STOPPED: u16 = 14105;
|
||||||
|
pub const CANCEL_MATCHMAKE: u16 = 14107;
|
||||||
pub const SERVER_HELLO: u16 = 20100;
|
pub const SERVER_HELLO: u16 = 20100;
|
||||||
pub const LOGIN_FAILED: u16 = 20103;
|
pub const LOGIN_FAILED: u16 = 20103;
|
||||||
pub const LOGIN_OK: u16 = 20104;
|
pub const LOGIN_OK: u16 = 20104;
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,11 @@ pub enum GameRequest {
|
||||||
#[serde(with = "crate::base64::serde_bytes")]
|
#[serde(with = "crate::base64::serde_bytes")]
|
||||||
payload: Vec<u8>,
|
payload: Vec<u8>,
|
||||||
},
|
},
|
||||||
|
HomeLogicStopped {
|
||||||
|
account: AccountRef,
|
||||||
|
#[serde(with = "crate::base64::serde_bytes")]
|
||||||
|
payload: Vec<u8>,
|
||||||
|
},
|
||||||
Disconnect {
|
Disconnect {
|
||||||
account: AccountRef,
|
account: AccountRef,
|
||||||
},
|
},
|
||||||
|
|
@ -82,6 +87,11 @@ pub trait GameApi: Send + Sync + 'static {
|
||||||
account: AccountRef,
|
account: AccountRef,
|
||||||
payload: Vec<u8>,
|
payload: Vec<u8>,
|
||||||
) -> RpcResult<Vec<WireMessage>>;
|
) -> RpcResult<Vec<WireMessage>>;
|
||||||
|
async fn home_logic_stopped(
|
||||||
|
&self,
|
||||||
|
account: AccountRef,
|
||||||
|
payload: Vec<u8>,
|
||||||
|
) -> RpcResult<Vec<WireMessage>>;
|
||||||
async fn end_client_turn(
|
async fn end_client_turn(
|
||||||
&self,
|
&self,
|
||||||
account: AccountRef,
|
account: AccountRef,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue