AtomicXCore provides two core modules: CoHostStore for cross-room connections and BattleStore for PK battles. This guide explains how to integrate these modules to enable connection and PK features in a voice chat room application.
Core Concept | Type | Core Responsibilities & Description |
CoHostStore | abstract class | Manages the lifecycle of host-to-host connections. Provides connection APIs such as requestHostConnection and acceptHostConnection. |
BattleStore | abstract class | Handles PK battle signaling and state management across rooms, including PK status tracking. |
LiveListStore | abstract class | Fetches the list of active live rooms ( fetchLiveList). Use this to discover rooms available for connection. |
LiveSeatStore | abstract class | Manages seat states within a room, including cross-room seats during connection. Tracks user join/leave events and microphone status. |
CoHostLayoutTemplate | enum | Defines seat layout templates for cross-room connections, e.g., HOST_STATIC_VOICE_6V6. |
BattleConfig | data | Configures PK battle details such as duration ( duration) and whether a secondary response is required (needResponse). |
LiveListStore.shared().fetchLiveList.import io.trtc.tuikit.atomicxcore.api.live.LiveListStoreimport io.trtc.tuikit.atomicxcore.api.CompletionHandlerfun fetchLiveListForCoHost() {val cursor = "" // Initial fetchval pageCount = 20LiveListStore.shared().fetchLiveList(cursor, pageCount, object : CompletionHandler {override fun onSuccess() {println("Live room list fetched successfully")// Update your invitation panel with LiveListStore.liveState.liveList// inviteAdapter.submitList(LiveListStore.liveState.liveList)}override fun onFailure(code: Int, desc: String) {println("Failed to fetch live room list: $code")}})}
CoHostStore.requestHostConnection to send a connection invitation.onSuccess callback.onCoHostRequestReceived in CoHostListener. Use this to pass business logic such as "start PK immediately".import io.trtc.tuikit.atomicxcore.api.live.CoHostLayoutTemplateimport io.trtc.tuikit.atomicxcore.api.CompletionHandlerval targetHostLiveID = "target_host_room_id"val layoutTemplate = CoHostLayoutTemplate.HOST_STATIC_VOICE_6V6val timeout = 10 // secondsval extraInfo = "" // e.g., direct PK flagcoHostStore.requestHostConnection(targetHostLiveID,layoutTemplate,timeout,extraInfo,object : CompletionHandler {override fun onSuccess() {println("Connection request sent. Awaiting response...")}override fun onFailure(code: Int, desc: String) {println("Failed to send invitation: $desc")}})
onCoHostRequestReceived in CoHostListener. Display a UI prompt such as "Host invites you to join the connection".private val coHostListener = object : CoHostListener() {override fun onCoHostRequestReceived(inviter: SeatUserInfo, extensionInfo: String){// Show invitation dialog}}
acceptHostConnection or rejectHostConnection as appropriate.private val coHostStore = CoHostStore.create(liveID)private val coHostListener = object : CoHostListener() {override fun onCoHostRequestReceived(inviter: SeatUserInfo, extensionInfo: String){// Accept connectioncoHostStore.acceptHostConnection(fromHostLiveID, null)// Or reject// coHostStore.rejectHostConnection(fromHostLiveID, null)}}
coHostStatus in CoHostStore.coHostState and seatList in LiveSeatStore.liveSeatState to update the seat layout dynamically.import io.trtc.tuikit.atomicxcore.api.live.CoHostStatusimport io.trtc.tuikit.atomicxcore.api.live.LiveSeatStoreimport io.trtc.tuikit.atomicxcore.api.live.SeatInfoimport kotlinx.coroutines.flow.collectprivate val liveSeatStore = LiveSeatStore.create(liveID)coHostStore.coHostState.coHostStatus.collect { status ->if (status == CoHostStatus.CONNECTED) {// Render connection layout (e.g., 6v6)}}liveSeatStore.liveSeatState.seatList.collect { seatList: List<SeatInfo> ->// Update avatars, mute status, etc.// Use seatInfo.userInfo.liveID to distinguish local/remote users.// Use seatInfo.userInfo.microphoneStatus for mute icon.}
requestHostConnection again.import io.trtc.tuikit.atomicxcore.api.live.CoHostLayoutTemplateimport io.trtc.tuikit.atomicxcore.api.CompletionHandlerval targetHostLiveID = "target_host_room_id"val layoutTemplate = CoHostLayoutTemplate.HOST_STATIC_VOICE_6V6val timeout = 10val extraInfo = ""coHostStore.requestHostConnection(targetHostLiveID,layoutTemplate,timeout,extraInfo,object : CompletionHandler {override fun onSuccess() {println("Connection request sent. Awaiting response...")}override fun onFailure(code: Int, desc: String) {println("Failed to send invitation: $desc")}})
CoHostStore.exitHostConnection. Remaining hosts should update their seat layout accordingly.public void onExitButtonClicked() {val coHostStore = CoHostStore.create(liveInfo.liveID)coHostStore.exitHostConnection(object : CompletionHandler{override fun onSuccess(){// Handle successful exit}override fun onFailure(code: Int, desc: String){// Handle error}})}
PK Mode | Room State (Before PK) | Room State (After PK) |
Scenario 1: PK after connection | Both sides connected | Returns to connection state |
Scenario 2: Connection with PK mode | Both sides not connected | Returns to connection state |


BattleStore.requestBattle. Configure PK duration, whether a response is required, and any extension info.BattleConfig.needResponse to false.needResponse is false, set timeout to 0.onBattleRequestTimeout. PK can be retried.val targetUserID = "target_host_user_id"val config = BattleConfig(duration = 30, // secondsneedResponse = true,extensionInfo = "")battleStore.requestBattle(config,listOf(targetUserID),timeout = 10, // secondscompletion = null)
onBattleRequestReceived in BattleListener. Use needResponse in battleInfo.config to determine whether to prompt the user.needResponse is false, enter PK state directly.private val mBattleListener: BattleListener = object : BattleListener() {override fun onBattleRequestReceived(battleId: String,inviter: SeatUserInfo,invitee: SeatUserInfo,) {super.onBattleRequestReceived(battleInfo, inviter, invitee)val userId = inviter.userIdval userName = inviter.userNameval needResponse = battleInfo.config.needResponse// Show PK prompt if needed}}
onBattleStarted is triggered in BattleListener, render the PK progress bar in your UI.needResponse is false, PK starts automatically.needResponse is true, PK starts after all invited hosts call acceptBattle.onUserJoinBattle.onBattleStarted.
onBattleEnded.exitBattle. Remaining hosts stay in PK and receive onUserExitBattle. If all hosts exit, PK ends early.val battleStore: BattleStore = BattleStore.create(liveID)battleID = battleStore.battleState.currentBattleInfo.value?.battleIDbattleStore.exitBattle(battleID, null)
onBattleEnded to display results and determine winner/loser.exitHostConnection.PK, opens a panel with active streamers (see Step 1 above).

needResponse in BattleConfig set to false.extensionInfo field in requestHostConnection to indicate PK mode for UI differentiation.extraInfo is {"withPK:true"}, show "Host invites you to PK".extraInfo is {"withPK:false"}, show "Host invites you to join a cross-room connection".import io.trtc.tuikit.atomicxcore.api.live.CoHostLayoutTemplateimport io.trtc.tuikit.atomicxcore.api.CompletionHandlerval targetHostLiveID = "target_host_room_id"val layoutTemplate = CoHostLayoutTemplate.HOST_STATIC_VOICE_6V6val timeout = 10val extraInfo = "withPK:true"coHostStore.requestHostConnection(targetHostLiveID,layoutTemplate,timeout,extraInfo,object : CompletionHandler {override fun onSuccess() {println("Connection request sent. Awaiting response...")}override fun onFailure(code: Int, desc: String) {println("Failed to send invitation: $desc")}})
onCoHostRequestReceived and can accept or reject.private val coHostListener = object : CoHostListener() {override fun onCoHostRequestReceived(inviter: SeatUserInfo, extensionInfo: String){// Show invitation dialog}}
private val coHostStore = CoHostStore.create(liveID)private val coHostListener = object : CoHostListener() {override fun onCoHostRequestReceived(inviter: SeatUserInfo, extensionInfo: String){coHostStore.acceptHostConnection(fromHostLiveID, null)// Or reject// coHostStore.rejectHostConnection(fromHostLiveID, null)}}
BattleStore.requestBattle with needResponse: false to start PK.override fun onCoHostRequestAccepted(invitee: SeatUserInfo) {coHostStore.acceptHostConnection(invitee.liveID, null)val directPKConfig = BattleConfig(duration = 30,needResponse = false,extensionInfo = "")battleStore.requestBattle(directPKConfig,listOf(invitee.userID),timeout = 0,completion = null)}
Gift Type | Score Calculation Rule | Example |
Basic Gift | Gift value × 5 | 10 RMB gift → 50 points |
Intermediate Gift | Gift value × 8 | 50 RMB gift → 400 points |
Advanced Gift | Gift value × 12 | 100 RMB gift → 1200 points |
Special Effect Gift | Fixed high score | 520 RMB gift → 1314 points |

API | Function Description | Request Example |
Active API - Query PK Status | Check whether the current room is in PK | |
Active API - Update PK Score | Update the calculated PK score | |
Callback Configuration - PK Start Callback | Receive real-time notification when PK starts | |
Callback Configuration - PK End Callback | Receive real-time notification when PK ends |

battleScore in BattleState. Update your PK progress bar whenever battleScore changes.private fun observeBattleScore() {val job = lifecycleScope.launch {battleStore?.battleState?.battleScore?.collect { battleScore ->// Update PK progress bar here}}collectJobs.add(job)}
Store/Component | Function Description | API Documentation |
CoHostStore | Host Connection Lifecycle Management: Responsible for managing and coordinating the lifecycle of anchor connections, including initiating connection invitations, handling acceptance/rejection, switching connection status, managing video streams during connections, and disconnecting connections. | |
BattleStore | Streamer PK Battle Lifecycle Management: Responsible for managing the entire lifecycle of timed PK battles between streamers, including initiating PK invitations, starting the battle, updating PK scores in real time, settling the winner at the end of the PK, and penalty time. | |
LiveListStore | Manages live room lifecycle: query room list, modify live info, etc. | |
LiveSeatStore | Manages seat states within a room (including cross-room seats), tracks user seat changes, microphone status, and more. |
onBattleEnded callback, determine the winner using battleScore from BattleState. The connection remains active after PK. Implement a custom 30-second punishment period, then disconnect after the countdown.onCoHostUserLeft or onUserExitBattle. Use these events to update the UI (e.g., "The other party has disconnected") and end the interaction.Feedback