@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
# include "common/android/id_cache.h"
@ -10,13 +10,17 @@
# include "network/network.h"
# include "android/log.h"
# include "common/settings.h"
# include "web_service/web_backend.h"
# include "web_service/verify_user_jwt.h"
# include "web_service/web_result.h"
# include <thread>
# include <chrono>
namespace IDCache = Common : : Android ;
AndroidMultiplayer : : AndroidMultiplayer ( Core : : System & system_ ,
AndroidMultiplayer : : AndroidMultiplayer ( Core : : System & system_ ,
std : : shared_ptr < Core : : AnnounceMultiplayerSession > session )
: system { system_ } , announce_multiplayer_session ( session ) { }
@ -27,8 +31,8 @@ void AndroidMultiplayer::AddNetPlayMessage(jint type, jstring msg) {
IDCache : : GetAddNetPlayMessage ( ) , type , msg ) ;
}
void AndroidMultiplayer : : AddNetPlayMessage ( int type , const std : : string & msg ) {
JNIEnv * env = IDCache : : GetEnvForThread ( ) ;
void AndroidMultiplayer : : AddNetPlayMessage ( int type , const std : : string & msg ) {
JNIEnv * env = IDCache : : GetEnvForThread ( ) ;
AddNetPlayMessage ( type , Common : : Android : : ToJString ( env , msg ) ) ;
}
@ -46,7 +50,7 @@ bool AndroidMultiplayer::NetworkInit() {
if ( auto member = Network : : GetRoomMember ( ) . lock ( ) ) {
// register the network structs to use in slots and signals
member - > BindOnStateChanged ( [ this ] ( const Network : : RoomMember : : State & state ) {
member - > BindOnStateChanged ( [ this ] ( const Network : : RoomMember : : State & state ) {
if ( state = = Network : : RoomMember : : State : : Joined | |
state = = Network : : RoomMember : : State : : Moderator ) {
NetPlayStatus status ;
@ -64,7 +68,7 @@ bool AndroidMultiplayer::NetworkInit() {
AddNetPlayMessage ( static_cast < int > ( status ) , msg ) ;
}
} ) ;
member - > BindOnError ( [ this ] ( const Network : : RoomMember : : Error & error ) {
member - > BindOnError ( [ this ] ( const Network : : RoomMember : : Error & error ) {
NetPlayStatus status ;
std : : string msg ;
switch ( error ) {
@ -107,29 +111,30 @@ bool AndroidMultiplayer::NetworkInit() {
}
AddNetPlayMessage ( static_cast < int > ( status ) , msg ) ;
} ) ;
member - > BindOnStatusMessageReceived ( [ this ] ( const Network : : StatusMessageEntry & status_message ) {
NetPlayStatus status = NetPlayStatus : : NO_ERROR ;
std : : string msg ( status_message . nickname ) ;
switch ( status_message . type ) {
case Network : : IdMemberJoin :
status = NetPlayStatus : : MEMBER_JOIN ;
break ;
case Network : : IdMemberLeave :
status = NetPlayStatus : : MEMBER_LEAVE ;
break ;
case Network : : IdMemberKicked :
status = NetPlayStatus : : MEMBER_KICKED ;
break ;
case Network : : IdMemberBanned :
status = NetPlayStatus : : MEMBER_BANNED ;
break ;
case Network : : IdAddressUnbanned :
status = NetPlayStatus : : ADDRESS_UNBANNED ;
break ;
}
AddNetPlayMessage ( static_cast < int > ( status ) , msg ) ;
} ) ;
member - > BindOnChatMessageReceived ( [ this ] ( const Network : : ChatEntry & chat ) {
member - > BindOnStatusMessageReceived (
[ this ] ( const Network : : StatusMessageEntry & status_message ) {
NetPlayStatus status = NetPlayStatus : : NO_ERROR ;
std : : string msg ( status_message . nickname ) ;
switch ( status_message . type ) {
case Network : : IdMemberJoin :
status = NetPlayStatus : : MEMBER_JOIN ;
break ;
case Network : : IdMemberLeave :
status = NetPlayStatus : : MEMBER_LEAVE ;
break ;
case Network : : IdMemberKicked :
status = NetPlayStatus : : MEMBER_KICKED ;
break ;
case Network : : IdMemberBanned :
status = NetPlayStatus : : MEMBER_BANNED ;
break ;
case Network : : IdAddressUnbanned :
status = NetPlayStatus : : ADDRESS_UNBANNED ;
break ;
}
AddNetPlayMessage ( static_cast < int > ( status ) , msg ) ;
} ) ;
member - > BindOnChatMessageReceived ( [ this ] ( const Network : : ChatEntry & chat ) {
NetPlayStatus status = NetPlayStatus : : CHAT_MESSAGE ;
std : : string msg ( chat . nickname ) ;
msg + = " : " ;
@ -140,10 +145,14 @@ bool AndroidMultiplayer::NetworkInit() {
return true ;
}
NetPlayStatus AndroidMultiplayer : : NetPlayCreateRoom ( const std : : string & ipaddress , int port ,
const std : : string & username , const std : : string & preferredGameName ,
const u64 & preferredGameId , const std : : string & password ,
const std : : string & room_name , int max_players ) {
NetPlayStatus AndroidMultiplayer : : NetPlayCreateRoom ( const std : : string & ipaddress , int port ,
const std : : string & username ,
const std : : string & preferredGameName ,
const u64 & preferredGameId ,
const std : : string & password ,
const std : : string & room_name , int max_players ,
bool isPublic ) {
auto member = Network : : GetRoomMember ( ) . lock ( ) ;
if ( ! member ) {
return NetPlayStatus : : NETWORK_ERROR ;
@ -168,17 +177,55 @@ NetPlayStatus AndroidMultiplayer::NetPlayCreateRoom(const std::string& ipaddress
. id = preferredGameId ,
} ;
port = ( port = = 0 ) ? Network : : DefaultRoomPort : static_cast < u16 > ( port ) ;
port = ( port = = 0 ) ? Network : : DefaultRoomPort : static_cast < u16 > ( port ) ;
if ( ! room - > Create ( room_name , " " , ipaddress , static_cast < u16 > ( port ) , password ,
static_cast < u32 > ( std : : min ( max_players , 16 ) ) , username , game , std : : make_unique < Network : : VerifyUser : : NullBackend > ( ) , { } ) ) {
static_cast < u32 > ( std : : min ( max_players , 16 ) ) , username , game ,
CreateVerifyBackend ( isPublic ) , { } ) ) {
return NetPlayStatus : : CREATE_ROOM_ERROR ;
}
// public announce session
if ( isPublic ) {
if ( auto session = announce_multiplayer_session . lock ( ) ) {
WebService : : WebResult result = session - > Register ( ) ;
if ( result . result_code ! = WebService : : WebResult : : Code : : Success ) {
LOG_ERROR ( WebService , " Failed to announce public room lobby " ) ;
room - > Destroy ( ) ;
return NetPlayStatus : : CREATE_ROOM_ERROR ;
}
session - > Start ( ) ;
} else {
LOG_ERROR ( Network , " Failed to start announce session " ) ;
}
}
// Failsafe timer to avoid joining before creation
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( 100 ) ) ;
member - > Join ( username , ipaddress . c_str ( ) , static_cast < u16 > ( port ) , 0 , Network : : NoPreferredIP , password , " " ) ;
std : : string token ;
// TODO(alekpop): properly handle the compile definition, it's not working right
//#ifdef ENABLE_WEB_SERVICE
// LOG_INFO(WebService, "Web Service enabled");
if ( isPublic ) {
WebService : : Client client ( Settings : : values . web_api_url . GetValue ( ) ,
Settings : : values . eden_username . GetValue ( ) ,
Settings : : values . eden_token . GetValue ( ) ) ;
token = client . GetExternalJWT ( room - > GetVerifyUID ( ) ) . returned_data ;
if ( token . empty ( ) ) {
LOG_ERROR ( WebService , " Could not get external JWT, verification may fail " ) ;
} else {
LOG_INFO ( WebService , " Successfully requested external JWT: size={} " , token . size ( ) ) ;
}
}
//#else
// LOG_INFO(WebService, "Web Service disabled");
//#endif
member - > Join ( username , ipaddress . c_str ( ) , static_cast < u16 > ( port ) , 0 , Network : : NoPreferredIP ,
password , token ) ;
// Failsafe timer to avoid joining before creation
for ( int i = 0 ; i < 5 ; i + + ) {
@ -194,8 +241,9 @@ NetPlayStatus AndroidMultiplayer::NetPlayCreateRoom(const std::string& ipaddress
return NetPlayStatus : : CREATE_ROOM_ERROR ;
}
NetPlayStatus AndroidMultiplayer : : NetPlayJoinRoom ( const std : : string & ipaddress , int port ,
const std : : string & username , const std : : string & password ) {
NetPlayStatus AndroidMultiplayer : : NetPlayJoinRoom ( const std : : string & ipaddress , int port ,
const std : : string & username ,
const std : : string & password ) {
auto member = Network : : GetRoomMember ( ) . lock ( ) ;
if ( ! member ) {
return NetPlayStatus : : NETWORK_ERROR ;
@ -209,7 +257,8 @@ NetPlayStatus AndroidMultiplayer::NetPlayJoinRoom(const std::string& ipaddress,
return NetPlayStatus : : ALREADY_IN_ROOM ;
}
member - > Join ( username , ipaddress . c_str ( ) , static_cast < u16 > ( port ) , 0 , Network : : NoPreferredIP , password , " " ) ;
member - > Join ( username , ipaddress . c_str ( ) , static_cast < u16 > ( port ) , 0 , Network : : NoPreferredIP ,
password , " " ) ;
// Wait a bit for the connection and join process to complete
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( 500 ) ) ;
@ -226,7 +275,7 @@ NetPlayStatus AndroidMultiplayer::NetPlayJoinRoom(const std::string& ipaddress,
return NetPlayStatus : : WRONG_PASSWORD ;
}
void AndroidMultiplayer : : NetPlaySendMessage ( const std : : string & msg ) {
void AndroidMultiplayer : : NetPlaySendMessage ( const std : : string & msg ) {
if ( auto room = Network : : GetRoomMember ( ) . lock ( ) ) {
if ( room - > GetState ( ) ! = Network : : RoomMember : : State : : Joined & &
room - > GetState ( ) ! = Network : : RoomMember : : State : : Moderator ) {
@ -237,11 +286,11 @@ void AndroidMultiplayer::NetPlaySendMessage(const std::string& msg) {
}
}
void AndroidMultiplayer : : NetPlayKickUser ( const std : : string & username ) {
void AndroidMultiplayer : : NetPlayKickUser ( const std : : string & username ) {
if ( auto room = Network : : GetRoomMember ( ) . lock ( ) ) {
auto members = room - > GetMemberInformation ( ) ;
auto it = std : : find_if ( members . begin ( ) , members . end ( ) ,
[ & username ] ( const Network : : RoomMember : : MemberInformation & member ) {
[ & username ] ( const Network : : RoomMember : : MemberInformation & member ) {
return member . nickname = = username ;
} ) ;
if ( it ! = members . end ( ) ) {
@ -250,11 +299,11 @@ void AndroidMultiplayer::NetPlayKickUser(const std::string& username) {
}
}
void AndroidMultiplayer : : NetPlayBanUser ( const std : : string & username ) {
void AndroidMultiplayer : : NetPlayBanUser ( const std : : string & username ) {
if ( auto room = Network : : GetRoomMember ( ) . lock ( ) ) {
auto members = room - > GetMemberInformation ( ) ;
auto it = std : : find_if ( members . begin ( ) , members . end ( ) ,
[ & username ] ( const Network : : RoomMember : : MemberInformation & member ) {
[ & username ] ( const Network : : RoomMember : : MemberInformation & member ) {
return member . nickname = = username ;
} ) ;
if ( it ! = members . end ( ) ) {
@ -263,7 +312,7 @@ void AndroidMultiplayer::NetPlayBanUser(const std::string& username) {
}
}
void AndroidMultiplayer : : NetPlayUnbanUser ( const std : : string & username ) {
void AndroidMultiplayer : : NetPlayUnbanUser ( const std : : string & username ) {
if ( auto room = Network : : GetRoomMember ( ) . lock ( ) ) {
room - > SendModerationRequest ( Network : : RoomMessageTypes : : IdModUnban , username ) ;
}
@ -278,7 +327,7 @@ std::vector<std::string> AndroidMultiplayer::NetPlayRoomInfo() {
auto room_info = room - > GetRoomInformation ( ) ;
info_list . push_back ( room_info . name + " | " + std : : to_string ( room_info . member_slots ) ) ;
// all members
for ( const auto & member : members ) {
for ( const auto & member : members ) {
info_list . push_back ( member . nickname ) ;
}
}
@ -368,14 +417,29 @@ std::vector<std::string> AndroidMultiplayer::NetPlayGetBanList() {
auto [ username_bans , ip_bans ] = room - > GetBanList ( ) ;
// Add username bans
for ( const auto & username : username_bans ) {
for ( const auto & username : username_bans ) {
ban_list . push_back ( username ) ;
}
// Add IP bans
for ( const auto & ip : ip_bans ) {
for ( const auto & ip : ip_bans ) {
ban_list . push_back ( ip ) ;
}
}
return ban_list ;
}
std : : unique_ptr < Network : : VerifyUser : : Backend > AndroidMultiplayer : : CreateVerifyBackend ( bool use_validation ) {
std : : unique_ptr < Network : : VerifyUser : : Backend > verify_backend ;
if ( use_validation ) {
//#ifdef ENABLE_WEB_SERVICE
verify_backend =
std : : make_unique < WebService : : VerifyUserJWT > ( Settings : : values . web_api_url . GetValue ( ) ) ;
//#else
// verify_backend = std::make_unique<Network::VerifyUser::NullBackend>();
//#endif
} else {
verify_backend = std : : make_unique < Network : : VerifyUser : : NullBackend > ( ) ;
}
return verify_backend ;
}