feat:完成游戏SDK加载与游戏状态关联

feat:公屏数据与游戏状态关联
feat:增加轮次ID协议
This commit is contained in:
max
2024-05-31 19:47:51 +08:00
parent a6f5b24a6d
commit cdbb895a68
12 changed files with 171 additions and 86 deletions

View File

@@ -42,10 +42,18 @@ abstract class BaseGameActivity<T : ViewBinding> : BaseViewBindingActivity<T>(),
GameEngineAbility::class.java.simpleName
)
@Deprecated("Dialog管理后续需要优化")
private var gameInfoDialogManager: DialogManager? = null
@Deprecated("Dialog管理后续需要优化")
protected var loadingDialogManager: DialogManager? = null
@Deprecated("Dialog管理后续需要优化")
protected var matchFailedDialogManager: DialogManager? = null
@Deprecated("Dialog管理后续需要优化")
protected var balanceNotEnoughDialogManager: DialogManager? = null
private var resultDialog: GameResultDialog? = null
override fun init() {
initView()
@@ -78,6 +86,7 @@ abstract class BaseGameActivity<T : ViewBinding> : BaseViewBindingActivity<T>(),
protected open fun onGameContext(gameContext: GameContext) {
gameContext.onViewAttach(this)
initGameEngine(gameContext)
stateAbility?.let {
observeGameInfoUiState(it)
@@ -93,7 +102,6 @@ abstract class BaseGameActivity<T : ViewBinding> : BaseViewBindingActivity<T>(),
}
}
}
initGameEngine(gameContext)
}
private fun observeGameInfoUiState(stateAbility: GameStateAbility) {
@@ -109,8 +117,8 @@ abstract class BaseGameActivity<T : ViewBinding> : BaseViewBindingActivity<T>(),
is GameInfoUiState.Success -> {
matchFailedDialogManager?.dismissDialog()
dialogManager?.dismissDialog()
resultDialog?.dismissAllowingStateLoss()
balanceNotEnoughDialogManager?.dismissDialog()
dismissResultDialog()
gameInfoDialogManager?.dismissDialog()
}
@@ -138,24 +146,6 @@ abstract class BaseGameActivity<T : ViewBinding> : BaseViewBindingActivity<T>(),
protected open fun initGameEngine(gameContext: GameContext) {
val gameEngineAbility =
gameContext.findAbility<GameEngineAbility>(GameEngineAbility::class.java.simpleName)
lifecycleScope.launch {
val stateAbility = stateAbility ?: return@launch
stateAbility.gameIdFlow.zip(stateAbility.roomIdFlow) { gameId, roomId ->
if (gameId?.toLongOrNull() != null && roomId != null) {
Pair(gameId.toLong(), roomId)
} else {
null
}
}.collectLatest {
if (it != null) {
gameEngineAbility?.loadGame(
this@BaseGameActivity,
it.second.toString(),
it.first
)
}
}
}
lifecycleScope.launch {
gameEngineAbility?.gameViewFlow?.collectLatest {
onGameViewChanged(it)
@@ -167,6 +157,7 @@ abstract class BaseGameActivity<T : ViewBinding> : BaseViewBindingActivity<T>(),
if (list == null) {
return
}
dismissResultDialog()
resultDialog = GameResultDialog(list, onClose = {
closeRoom()
}, onRestart = {
@@ -196,7 +187,10 @@ abstract class BaseGameActivity<T : ViewBinding> : BaseViewBindingActivity<T>(),
}
protected open fun onBalanceNotEnough() {
dialogManager?.showOkCancelDialog(
if (balanceNotEnoughDialogManager == null) {
balanceNotEnoughDialogManager = DialogManager(this)
}
balanceNotEnoughDialogManager?.showOkCancelDialog(
ResUtil.getString(R.string.star_send_gift_balance),
ResUtil.getString(R.string.treasure_to_charge)
) {
@@ -287,13 +281,22 @@ abstract class BaseGameActivity<T : ViewBinding> : BaseViewBindingActivity<T>(),
override fun onDestroy() {
super.onDestroy()
balanceNotEnoughDialogManager?.dismissDialog()
balanceNotEnoughDialogManager = null
gameInfoDialogManager?.dismissDialog()
gameInfoDialogManager = null
loadingDialogManager?.dismissDialog()
loadingDialogManager = null
resultDialog?.dismissAllowingStateLoss()
dismissResultDialog()
resultDialog = null
matchFailedDialogManager?.dismissDialog()
matchFailedDialogManager = null
}
private fun dismissResultDialog() {
try {
resultDialog?.dismissAllowingStateLoss()
} catch (e: Exception) {
}
}
}

View File

@@ -23,8 +23,6 @@ class GameIMEngineAbility(private val listenerOwner: ListenerOwner<GameIMEngineA
private var chatRoomClient: ChatRoomClient? = null
val receiveMessageFlow = MutableSharedFlow<List<ChatRoomMessage>>()
private val stateAbility: GameStateAbility?
get() = roomContext?.findAbility<GameStateAbility>(
GameStateAbility::class.java.simpleName
@@ -54,7 +52,7 @@ class GameIMEngineAbility(private val listenerOwner: ListenerOwner<GameIMEngineA
chatRoomClient?.let {
safeLaunch {
it.stateFlow.collect {
this@GameIMEngineAbility.stateFlow.emit(it)
this@GameIMEngineAbility.stateFlow.value = it
}
}
addDisposable(it.messageObservable.subscribe { list ->

View File

@@ -11,6 +11,7 @@ import com.netease.nimlib.sdk.chatroom.ChatRoomMessageBuilder
import com.netease.nimlib.sdk.chatroom.model.ChatRoomMessage
import com.netease.nimlib.sdk.msg.constant.MsgTypeEnum
import io.reactivex.Single
import kotlinx.coroutines.flow.collectLatest
import java.lang.IllegalStateException
class GameMessageAbility(private val listenerOwner: ListenerOwner<GameMessageAbility.Listener> = SafeListenerOwner()) :
@@ -23,6 +24,43 @@ class GameMessageAbility(private val listenerOwner: ListenerOwner<GameMessageAbi
get() =
roomContext?.findAbility<GameIMEngineAbility>(GameIMEngineAbility::class.java.simpleName)
override fun onStart(context: RoomContext) {
super.onStart(context)
val imEngineAbility =
context.findAbility<GameIMEngineAbility>(GameIMEngineAbility::class.java.simpleName)
imEngineAbility?.addListener(this)
val stateAbility =
context.findAbility<GameStateAbility>(GameStateAbility::class.java.simpleName)
stateAbility?.let {
safeLaunch {
it.imIdFlow.collectLatest {
resetMessageList()
}
}
safeLaunch {
it.gameStateFlow.collectLatest {
if (it == GameStateAbility.STATE_GAME_END) {
resetMessageList()
}
}
}
}
}
private fun resetMessageList() {
val stateAbility =
roomContext?.findAbility<GameStateAbility>(GameStateAbility::class.java.simpleName)
dataList.clear()
val sessionId = stateAbility?.imIdFlow?.value
if (sessionId != null) {
dataList.add(getSystemNotifyMessage(sessionId.toString()))
}
listenerOwner.postEvent {
it.onReplaceMessageList(dataList)
}
}
fun getMessageList(): List<Any> {
return dataList
}
@@ -48,22 +86,14 @@ class GameMessageAbility(private val listenerOwner: ListenerOwner<GameMessageAbi
return Single.error(IllegalStateException("engine is NULL"))
}
override fun onStart(context: RoomContext) {
super.onStart(context)
val imEngineAbility =
context.findAbility<GameIMEngineAbility>(GameIMEngineAbility::class.java.simpleName)
imEngineAbility?.addListener(this)
addSystemNotify()
}
private fun addSystemNotify() {
private fun getSystemNotifyMessage(sessionId: String): ChatRoomMessage {
val message = ChatRoomMessageBuilder.createTipMessage(
roomContext?.roomId?.toString() ?: ""
sessionId
)
message.content = ResUtil.getString(
R.string.yizhuan_xchat_android_constants_xchatconstants_01
)
addMessage(message)
return message
}
override fun onStop(context: RoomContext) {

View File

@@ -33,19 +33,20 @@ class GameStateAbility : RoomAbility(), GameIMEngineAbility.Listener {
/**
* 游戏状态: 本地定义状态+匹配状态
* 本地定义状态NULL:不合法状态)
* 匹配状态(服务端定义)0:匹配中、1:匹配成功、2:游戏结束、3:匹配失败)
*/
val gameStateFlow: MutableStateFlow<Int?> = MutableStateFlow(STATE_MATCHING)
val gameIconFlow = MutableStateFlow<String?>(null)
val gameIdFlow = MutableStateFlow<String?>(null)
val gameIdFlow = MutableStateFlow<Long?>(null)
val roomIdFlow = MutableStateFlow<Long?>(null)
val imIdFlow = MutableStateFlow<Long?>(null)
val matchRoundIdFlow = MutableStateFlow<Long?>(null)
val scoresFlow = MutableStateFlow<List<Double>?>(null)
val gameConfigFlow = MutableStateFlow<String?>(null)
@@ -90,7 +91,7 @@ class GameStateAbility : RoomAbility(), GameIMEngineAbility.Listener {
safeLaunch(onError = {
gameInfoUiStateFlow.value = GameInfoUiState.Failed(it, gameInfoFlow.value)
}) {
val roomId = GameModel2.startGame(intent.gameId.toString(), intent.gameMode)
GameModel2.startGame(intent.gameId.toString(), intent.gameMode)
requestRoomInfo()
}
}
@@ -100,10 +101,11 @@ class GameStateAbility : RoomAbility(), GameIMEngineAbility.Listener {
gameInfoFlow.value = info
roomIdFlow.value = info.roomId
imIdFlow.value = info.roomId
gameIdFlow.value = info.data?.mgId
gameIdFlow.value = info.data?.mgId?.toLongOrNull()
gameIconFlow.value = info.data?.gameRoomIcon
scoresFlow.value = info.data?.scores
gameConfigFlow.value = info.data?.configJson
matchRoundIdFlow.value = info.data?.matchRoundId
syncGameState(info.data?.matchStatus)
syncQueue(info)
}
@@ -114,6 +116,7 @@ class GameStateAbility : RoomAbility(), GameIMEngineAbility.Listener {
private suspend fun syncQueue(info: GameRoomInfo) {
queueAbility?.updateQueue(info.roomMics)
matchRoundIdFlow.value = info.data?.matchRoundId
syncGameState(info.data?.matchStatus)
}

View File

@@ -4,17 +4,20 @@ import com.chwl.app.game.core.GameQueueAbility
import com.chwl.app.game.core.GameStateAbility
import com.chwl.core.bean.game.GameResultBean
import com.chwl.core.bean.game.SudGameConfigBean
import com.chwl.core.sud.model.GameViewInfoModel
import com.chwl.core.sud.state.SudMGPMGState
import com.chwl.core.support.room.RoomContext
import com.chwl.library.utils.json.JsonUtils
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import org.json.JSONObject
import tech.sud.mgp.core.ISudFSMStateHandle
class GameEngineAbility : SudEngineAbility() {
private val stateAbility get() = roomContext?.findAbility<GameStateAbility>(GameStateAbility::class.java.simpleName)
private var gameConfig: SudGameConfigBean? = null
val gameViewRectFlow = MutableStateFlow<GameViewInfoModel.GameViewRectModel?>(null)
override fun onStart(context: RoomContext) {
super.onStart(context)
@@ -24,30 +27,40 @@ class GameEngineAbility : SudEngineAbility() {
}
}
safeLaunch {
stateAbility?.gameStateFlow?.collectLatest {
logD("GameEngineAbility gameStateFlow state:${it}")
when (it) {
GameStateAbility.STATE_MATCH_SUCCESS -> {
autoPlayGame()
stateAbility?.let {
combine(
*arrayOf(
gameViewRectFlow,
it.gameIdFlow,
it.roomIdFlow,
it.gameStateFlow
), transform = {
true
})
.collectLatest {
checkRunning()
}
GameStateAbility.STATE_MATCHING -> {
tryStopGame()
}
GameStateAbility.STATE_MATCH_FAILED -> {
tryStopGame()
}
GameStateAbility.STATE_GAME_END -> {
tryStopGame()
}
}
}
}
}
private fun tryStopGame() {
private fun checkRunning() {
logD("checkRunning()")
if (gameViewRectFlow.value == null) {
logD("checkRunning() gameViewRect:null")
return
}
val gameState = stateAbility?.gameStateFlow?.value ?: return
logD("checkRunning() gameState:$gameState")
if (gameState == GameStateAbility.STATE_MATCH_SUCCESS) {
val activity = roomView?.getActivity() ?: return
val roomId = stateAbility?.roomIdFlow?.value ?: return
val gameId = stateAbility?.gameIdFlow?.value ?: return
loadGame(activity, roomId.toString(), gameId)
} else {
logD("checkRunning() destroyGame")
destroyGame()
}
}
private fun parseGameConfig(json: String?) {
@@ -59,6 +72,9 @@ class GameEngineAbility : SudEngineAbility() {
}
private fun autoPlayGame() {
if (!isRunning) {
return
}
val stateAbility = stateAbility ?: return
logD("autoPlayGame state:${stateAbility.gameStateFlow.value}")
if (stateAbility.gameStateFlow.value != GameStateAbility.STATE_MATCH_SUCCESS) {
@@ -73,18 +89,34 @@ class GameEngineAbility : SudEngineAbility() {
}
logD("autoPlayGame queueSize:$queueSize size:${sudFSMMG.sudFSMMGCache.playerReadySet.size}")
if (sudFSMMG.sudFSMMGCache.playerReadySet.size >= queueSize) {
sudFSTAPP.notifyAPPCommonSelfPlaying(true, null, null)
val roundId = stateAbility.matchRoundIdFlow.value
val jsonObject = JSONObject()
jsonObject.put("value0", roundId)
val extras = jsonObject.toString()
sudFSTAPP.notifyAPPCommonSelfPlaying(true, extras, "value0")
}
}
private fun autoJoinGame() {
if (!isRunning) {
return
}
sudFSTAPP.notifyAPPCommonSelfIn(true, -1, true, 1)
sudFSTAPP.notifyAPPCommonSelfReady(true)
}
fun setGameViewRect(rect: GameViewInfoModel.GameViewRectModel) {
this.gameViewRectFlow.value = rect
}
override fun getGameViewRect(): GameViewInfoModel.GameViewRectModel {
return this.gameViewRectFlow.value ?: super.getGameViewRect()
}
override fun onGameStarted() {
super.onGameStarted()
autoJoinGame()
autoPlayGame()
updateGameSettingSelectConfig()
}

View File

@@ -33,21 +33,28 @@ import tech.sud.mgp.core.SudMGP
open class SudEngineAbility : RoomAbility(), SudFSMMGListener, ILog {
val gameViewFlow = MutableStateFlow<View?>(null)
protected var isRunning = true
protected var isRunning = false
protected val sudFSMMG = SudFSMMGDecorator().apply {
setSudFSMMGListener(this@SudEngineAbility)
}
protected val sudFSTAPP = SudFSTAPPDecorator()
private var gameViewRect: GameViewInfoModel.GameViewRectModel =
GameViewInfoModel.GameViewRectModel()
private var roomId: String = ""
private var gameId: Long = 0
fun loadGame(activity: Activity, roomId: String, gameId: Long) {
logD("loadGame() roomId:$roomId gameId:$gameId")
if (!this.isRunning) {
if (isRunning && roomId == getRoomId() && gameId == getGameId()) {
logD("loadGame() 重复")
return
}
if (activity.isFinishing || activity.isDestroyed) {
logD("loadGame() activity 状态异常")
return
}
destroyGame()
this.isRunning = true
this.roomId = roomId
this.gameId = gameId
getGameCode({
initSDK(activity, gameId, it)
}, {
@@ -55,6 +62,18 @@ open class SudEngineAbility : RoomAbility(), SudFSMMGListener, ILog {
})
}
fun destroyGame() {
if (!isRunning) {
return
}
logD("destroyGame()")
isRunning = false
sudFSTAPP.destroyMG()
gameViewFlow.value = null
roomId = ""
gameId = 0
}
private fun initSDK(activity: Activity, gameId: Long, code: String) {
SudMGP.initSDK(
activity,
@@ -86,10 +105,6 @@ open class SudEngineAbility : RoomAbility(), SudFSMMGListener, ILog {
gameViewFlow.value = iSudFSTAPP.gameView
}
fun setGameViewRect(rect: GameViewInfoModel.GameViewRectModel) {
this.gameViewRect = rect
}
private fun getGameCode(onSuccess: (String) -> Unit, onFailed: (Throwable) -> Unit) {
addDisposable(
GameModel.getGameCode()
@@ -112,7 +127,7 @@ open class SudEngineAbility : RoomAbility(), SudFSMMGListener, ILog {
response.ret_code = 0
response.view_size.width = width
response.view_size.height = height
response.view_game_rect = gameViewRect
response.view_game_rect = getGameViewRect()
handle.success(JsonUtils.toJson(response))
}
@@ -157,11 +172,6 @@ open class SudEngineAbility : RoomAbility(), SudFSMMGListener, ILog {
}
private fun destroyGame() {
isRunning = false
sudFSTAPP.destroyMG()
}
override fun onExpireCode(handle: ISudFSMStateHandle, p1: String?) {
getGameCode({
handle.success(MGStateResponse.success().toJson())
@@ -203,7 +213,9 @@ open class SudEngineAbility : RoomAbility(), SudFSMMGListener, ILog {
sudFSTAPP.notifyAPPCommonSelfIn(true, -1, true, 1)
}
protected open fun getRoomId() = roomId
private fun getRoomId() = roomId
private fun getGameId() = gameId
protected open fun getAppId() = AppConfig.APP_ID
@@ -211,6 +223,10 @@ open class SudEngineAbility : RoomAbility(), SudFSMMGListener, ILog {
protected open fun isTestEnv() = AppConfig.isTestEnv
protected open fun getGameViewRect(): GameViewInfoModel.GameViewRectModel {
return GameViewInfoModel.GameViewRectModel()
}
protected open fun getGameLanguage(): String {
return when (LanguageHelper.getCurrentLanguageType()) {
LanguageHelper.ZH -> {

View File

@@ -28,7 +28,7 @@ object GameModel2 : BaseModel() {
api.getChatRoomInfo(BaseRoomInfo.ROOM_TYPE_GAME)
}
suspend fun startGame(gameId: String, gameMode: Int): Long? =
suspend fun startGame(gameId: String, gameMode: Int): String? =
launchRequest {
api.startGame(gameId, gameMode)
}
@@ -59,7 +59,7 @@ object GameModel2 : BaseModel() {
suspend fun startGame(
@Field("mgId") mgId: String,
@Field("gameMode") gameMode: Int
): ServiceResult<Long>
): ServiceResult<String>
/**
* 关闭游戏

View File

@@ -63,7 +63,7 @@ class GameBuyDialog(private val gameConfig: GameConfigBean, private val gameMode
if (it.isSuccess) {
dismissAllowingStateLoss()
val intent =
GameIntent(it.data ?: 0, gameConfig.mgId ?: 0L, gameMode.gameMode ?: -1)
GameIntent(gameConfig.mgId ?: 0L, gameMode.gameMode ?: -1)
GameActivity.start(requireContext(), intent)
} else {
if (it.code == BalanceNotEnoughExeption.code) {

View File

@@ -4,5 +4,5 @@ import androidx.annotation.Keep
import java.io.Serializable
@Keep
data class GameIntent(val roomId: Long, val gameId: Long, val gameMode: Int) : Serializable {
data class GameIntent(val gameId: Long, val gameMode: Int) : Serializable {
}

View File

@@ -20,7 +20,7 @@ class GameViewModel : BaseViewModel() {
fun init(intent: GameIntent) {
this.gameIntent = intent
val gameContext = GameContext(intent.roomId)
val gameContext = GameContext(0)
gameContext.performStart()
gameContextLiveData.value = gameContext
}

View File

@@ -11,7 +11,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow
class GameHomeViewModel : BaseViewModel() {
val gameConfigLiveData = MutableLiveData<BeanResult<GameConfigBean>?>()
val startGameFlow = MutableSharedFlow<BeanResult<Long>>()
val startGameFlow = MutableSharedFlow<BeanResult<String?>>()
fun getGameList() {
safeLaunch(onError = {
@@ -26,9 +26,9 @@ class GameHomeViewModel : BaseViewModel() {
safeLaunch(onError = {
startGameFlow.emit(BeanResult.Companion.failed(it))
}) {
GameModel2.startGame(gameId, gameMode)
startGameFlow.emit(BeanResult.success(null))
PayModel.get().refreshWalletInfo(true)
val value = GameModel2.startGame(gameId, gameMode)
startGameFlow.emit(BeanResult.success(value))
}
}
}