Verified Commit 62beedbd authored by Jørgen Syvertsen's avatar Jørgen Syvertsen
Browse files

Implement exchange of HeroData

parent 9fe4a169
Pipeline #175781 failed with stages
in 29 minutes and 58 seconds
......@@ -4,6 +4,7 @@ import com.badlogic.gdx.scenes.scene2d.Stage
import com.badlogic.gdx.utils.Logger
import se.battlegoo.battlegoose.Game
import se.battlegoo.battlegoose.datamodels.BattleData
import se.battlegoo.battlegoose.models.heroes.Hero
import se.battlegoo.battlegoose.network.CreateLobbyStatus
import se.battlegoo.battlegoose.network.MultiplayerService
import se.battlegoo.battlegoose.utils.Modal
......@@ -11,6 +12,7 @@ import se.battlegoo.battlegoose.utils.ModalType
import se.battlegoo.battlegoose.views.CreateLobbyView
class CreateLobbyController(
val hero: Hero,
val createLobbyView: CreateLobbyView,
val onClickStartBattle: (BattleData) -> Unit,
val onClickMainMenu: () -> Unit,
......@@ -66,7 +68,7 @@ class CreateLobbyController(
ModalType.Error(),
stage
).show()
MultiplayerService.startBattle(lobbyIDCpy) {
MultiplayerService.startBattle(lobbyIDCpy, hero) {
battleData = it
}
}
......
......@@ -4,6 +4,7 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.scenes.scene2d.Stage
import com.badlogic.gdx.utils.Logger
import se.battlegoo.battlegoose.Game
import se.battlegoo.battlegoose.models.heroes.Hero
import se.battlegoo.battlegoose.network.JoinLobbyStatus
import se.battlegoo.battlegoose.network.MultiplayerService
import se.battlegoo.battlegoose.utils.Modal
......@@ -11,6 +12,7 @@ import se.battlegoo.battlegoose.utils.ModalType
import se.battlegoo.battlegoose.views.JoinLobbyView
class JoinLobbyController(
hero: Hero,
val joinLobbyView: JoinLobbyView,
val onReadyStartBattle: (JoinLobbyStatus.StartBattle) -> Unit,
val onClickMainMenu: () -> Unit,
......@@ -25,7 +27,7 @@ class JoinLobbyController(
init {
joinLobbyView.onClickJoinLobby = { lobbyID ->
MultiplayerService.joinLobby(lobbyID) { status, cancelListener ->
MultiplayerService.joinLobby(lobbyID, hero) { status, cancelListener ->
if (listenForStartingBattle == false) {
listenForStartingBattle = null
cancelListener()
......
......@@ -2,6 +2,8 @@ package se.battlegoo.battlegoose.controllers
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.scenes.scene2d.Stage
import se.battlegoo.battlegoose.datamodels.BattleData
import se.battlegoo.battlegoose.models.heroes.Hero
import se.battlegoo.battlegoose.network.MultiplayerService
import se.battlegoo.battlegoose.network.RandomPairingStatus
import se.battlegoo.battlegoose.utils.Modal
......@@ -9,8 +11,9 @@ import se.battlegoo.battlegoose.utils.ModalType
import se.battlegoo.battlegoose.views.QuickJoinView
class QuickJoinController(
hero: Hero,
private val quickJoinView: QuickJoinView,
val onReadyStartBattle: (userID: String, battleID: String, isHost: Boolean) -> Unit,
val onReadyStartBattle: (battle: BattleData, isHost: Boolean) -> Unit,
val onClickMainMenu: () -> Unit,
val stage: Stage
) : ControllerBase(quickJoinView) {
......@@ -25,12 +28,12 @@ class QuickJoinController(
private var successfullyLeftQueue = false
private var leaveTimeoutCounter = 0f
private var battleData: BattleStateData? = null
private var battleData: RandomPairingStatus.StartBattle? = null
private var showingErrorModal = false
init {
MultiplayerService.requestOpponent({ status, leaveQueue ->
MultiplayerService.requestOpponent(hero, { status, leaveQueue ->
setBattleData(status)
quickJoinView.setStatus(status)
shouldStartBattle = status is RandomPairingStatus.StartBattle
......@@ -55,11 +58,7 @@ class QuickJoinController(
private fun setBattleData(status: RandomPairingStatus) {
if (status is RandomPairingStatus.StartBattle) {
battleData = BattleStateData(
userID = status.playerID,
battleID = status.battleID,
isHost = status.isHost
)
battleData = status
}
}
......@@ -89,8 +88,7 @@ class QuickJoinController(
when {
wantToLeaveQueue && canLeaveQueue && successfullyLeftQueue -> onClickMainMenu()
shouldStartBattle && dataCpy != null -> onReadyStartBattle(
dataCpy.userID,
dataCpy.battleID,
dataCpy.battle,
dataCpy.isHost
)
shouldStartBattle && dataCpy == null -> showModal(
......@@ -113,5 +111,3 @@ class QuickJoinController(
quickJoinView.dispose()
}
}
data class BattleStateData(val userID: String, val battleID: String, val isHost: Boolean)
......@@ -4,6 +4,7 @@ data class BattleData(
val battleID: String,
val hostID: String,
val otherPlayerID: String,
val actions: List<ActionData>
// Also store starting positions and starting map here
val actions: List<ActionData>,
val hostHero: HeroData<*>,
val otherHero: HeroData<*>?
) : DataModel
package se.battlegoo.battlegoose.datamodels
import se.battlegoo.battlegoose.models.heroes.AdmiralAlbatross
import se.battlegoo.battlegoose.models.heroes.Hero
import se.battlegoo.battlegoose.models.heroes.MajorMallard
import se.battlegoo.battlegoose.models.heroes.SergeantSwan
class HeroData<T : Hero>(hero: T) : DataModel {
val heroType: String = hero::class.java.name
fun toHero(): Hero {
return when (heroType) {
SergeantSwan::class.java.name -> SergeantSwan()
MajorMallard::class.java.name -> MajorMallard()
AdmiralAlbatross::class.java.name -> AdmiralAlbatross()
else ->
throw NotImplementedError("$heroType not implemented in HeroData")
}
}
}
......@@ -6,16 +6,22 @@ import se.battlegoo.battlegoose.datamodels.GridVector
import se.battlegoo.battlegoose.models.Battle
import se.battlegoo.battlegoose.models.BattleMap
import se.battlegoo.battlegoose.models.BattleMapBackground
import se.battlegoo.battlegoose.models.heroes.SergeantSwan
import se.battlegoo.battlegoose.models.heroes.Hero
import se.battlegoo.battlegoose.views.FacingDirection
import se.battlegoo.battlegoose.views.UnitSprite
import se.battlegoo.battlegoose.views.UnitView
class BattleState(playerID: String, battleID: String, isHost: Boolean) : GameState() {
class BattleState(
playerID: String,
battleID: String,
hostHero: Hero,
otherHero: Hero,
isHost: Boolean
) : GameState() {
private val battleController: BattleController = BattleController(
Battle(
SergeantSwan(),
SergeantSwan(),
if (isHost) hostHero else otherHero,
if (isHost) otherHero else hostHero,
BattleMap(
BattleMapBackground.values().random(BattleController.getRandom(battleID)),
GridVector(10, 6)
......
......@@ -8,6 +8,7 @@ import se.battlegoo.battlegoose.views.CreateLobbyView
class CreateLobbyState(selectedHero: Hero) : LobbyState(selectedHero) {
private val createLobbyController: CreateLobbyController = CreateLobbyController(
selectedHero,
createLobbyView = CreateLobbyView(
this::goBack,
stage
......@@ -17,6 +18,8 @@ class CreateLobbyState(selectedHero: Hero) : LobbyState(selectedHero) {
BattleState(
it.hostID,
it.battleID,
it.hostHero.toHero(),
it.otherHero!!.toHero(),
true
)
)
......
......@@ -11,8 +11,8 @@ import se.battlegoo.battlegoose.views.HeroSelectionView
import se.battlegoo.battlegoose.views.HeroSelectionViewModel
class HeroSelectionState(
private val createLobbyState: (Hero<*>) -> LobbyState
): GameState() {
private val createLobbyState: (Hero) -> LobbyState
) : GameState() {
private val heroes: List<Hero> = listOf(SergeantSwan(), MajorMallard(), AdmiralAlbatross())
private val heroSelection = HeroSelection(heroes)
......
......@@ -8,12 +8,15 @@ import se.battlegoo.battlegoose.views.JoinLobbyView
class JoinLobbyState(selectedHero: Hero) : LobbyState(selectedHero) {
private var joinLobbyController: JoinLobbyController = JoinLobbyController(
selectedHero,
joinLobbyView = JoinLobbyView(stage),
onReadyStartBattle = {
GameStateManager.replace(
BattleState(
it.lobby.otherPlayerID,
it.lobby.battleID,
it.battle.otherPlayerID,
it.battle.battleID,
it.battle.hostHero.toHero(),
it.battle.otherHero!!.toHero(),
false
)
)
......
......@@ -2,4 +2,4 @@ package se.battlegoo.battlegoose.gamestates
import se.battlegoo.battlegoose.models.heroes.Hero
abstract class LobbyState(val selectedHero: Hero<*>) : GameState()
abstract class LobbyState(val selectedHero: Hero) : GameState()
......@@ -8,12 +8,15 @@ import se.battlegoo.battlegoose.views.QuickJoinView
class QuickJoinState(selectedHero: Hero) : LobbyState(selectedHero) {
private val quickJoinController = QuickJoinController(
selectedHero,
quickJoinView = QuickJoinView(this::goBack, stage),
onReadyStartBattle = { userID, battleID, isHost ->
onReadyStartBattle = { battle, isHost ->
GameStateManager.replace(
BattleState(
userID,
battleID,
if (isHost) battle.hostID else battle.otherPlayerID,
battle.battleID,
battle.hostHero.toHero(),
battle.otherHero!!.toHero(),
isHost
)
)
......
......@@ -10,9 +10,13 @@ import se.battlegoo.battlegoose.datamodels.ActionData
import se.battlegoo.battlegoose.datamodels.BattleData
import se.battlegoo.battlegoose.datamodels.DataModel
import se.battlegoo.battlegoose.datamodels.GridVector
import se.battlegoo.battlegoose.datamodels.HeroData
import se.battlegoo.battlegoose.datamodels.LobbyData
import se.battlegoo.battlegoose.datamodels.RandomOpponentData
import se.battlegoo.battlegoose.datamodels.SpellData
import se.battlegoo.battlegoose.models.heroes.AdmiralAlbatross
import se.battlegoo.battlegoose.models.heroes.MajorMallard
import se.battlegoo.battlegoose.models.heroes.SergeantSwan
import java.util.function.BiConsumer
import java.util.function.Consumer
......@@ -220,6 +224,7 @@ class DatabaseHandler {
SpellData::class -> ::convertToSpellData
BattleData::class -> ::convertToBattle
RandomOpponentData::class -> ::convertToRandomOpponentData
HeroData::class -> ::convertToHeroData
else -> throw NotImplementedError("No deserializer for ${T::class}")
} as ConversionFunc<T>
}
......@@ -232,6 +237,16 @@ class DatabaseHandler {
return LobbyData(lobbyID, hostID, otherPlayerID, battleID)
}
fun convertToHeroData(data: Map<String, Any>): HeroData<*> {
val heroType = data[HeroData<*>::heroType.name] as String
return when (Class.forName(heroType).kotlin) {
SergeantSwan::class -> HeroData(SergeantSwan())
MajorMallard::class -> HeroData(MajorMallard())
AdmiralAlbatross::class -> HeroData(AdmiralAlbatross())
else -> throw NotImplementedError("The hero class $heroType has no deserializer")
}
}
@Suppress("UNCHECKED_CAST")
fun convertToBattle(battleData: Map<String, Any>): BattleData {
val battleID = battleData[BattleData::battleID.name] as String
......@@ -242,7 +257,10 @@ class DatabaseHandler {
val actions: List<ActionData> = actionsRaw
?.map(::convertToActionData)
?: emptyList()
return BattleData(battleID, hostID, otherPlayerID, actions)
val hostHero = convertToHeroData(battleData[BattleData::hostHero.name] as Map<String, Any>)
val otherHero = (battleData[BattleData::otherHero.name] as Map<String, Any>?)
?.let(::convertToHeroData)
return BattleData(battleID, hostID, otherPlayerID, actions, hostHero, otherHero)
}
@Suppress("UNCHECKED_CAST")
......
package se.battlegoo.battlegoose.network
import se.battlegoo.battlegoose.datamodels.BattleData
import se.battlegoo.battlegoose.datamodels.LobbyData
sealed class JoinLobbyStatus() {
data class Ready(val lobby: LobbyData) : JoinLobbyStatus()
data class StartBattle(val lobby: LobbyData) : JoinLobbyStatus()
data class StartBattle(val battle: BattleData) : JoinLobbyStatus()
object NotAccessible : JoinLobbyStatus()
object Full : JoinLobbyStatus()
object DoesNotExist : JoinLobbyStatus()
......
......@@ -4,8 +4,10 @@ import com.badlogic.gdx.utils.Logger
import se.battlegoo.battlegoose.Game
import se.battlegoo.battlegoose.datamodels.ActionData
import se.battlegoo.battlegoose.datamodels.BattleData
import se.battlegoo.battlegoose.datamodels.HeroData
import se.battlegoo.battlegoose.datamodels.LobbyData
import se.battlegoo.battlegoose.datamodels.RandomOpponentData
import se.battlegoo.battlegoose.models.heroes.Hero
import java.util.Date
import java.util.function.BiConsumer
import java.util.function.Consumer
......@@ -136,6 +138,7 @@ object MultiplayerService {
}
fun requestOpponent(
hero: Hero,
listener: RandomPairingListener,
onFail: FailFunc = { _, throwable -> throw throwable },
) {
......@@ -190,6 +193,7 @@ object MultiplayerService {
setAvailableLobby("") {
joinLobby(
availableLobbyID,
hero,
onFail = { reason, throwable ->
queueListenerCanceler()
onFail(reason, throwable)
......@@ -209,8 +213,7 @@ object MultiplayerService {
cancelJoinLobbyListener()
listener(
RandomPairingStatus.StartBattle(
joinLobbyStatus.lobby.otherPlayerID,
joinLobbyStatus.lobby.battleID,
joinLobbyStatus.battle,
false
),
cannotLeaveQueue
......@@ -253,11 +256,10 @@ object MultiplayerService {
}
CreateLobbyStatus.OTHER_PLAYER_JOINED -> {
cancelOtherPlayerIDListener()
startBattle(lobby.lobbyID, onFail) {
startBattle(lobby.lobbyID, hero, onFail) {
listener(
RandomPairingStatus.StartBattle(
it.hostID,
it.battleID,
it,
true
),
cannotLeaveQueue
......@@ -278,6 +280,7 @@ object MultiplayerService {
fun joinLobby(
lobbyID: String,
hero: Hero,
onFail: FailFunc = { _, throwable -> throw throwable },
battleListener: BiConsumer<JoinLobbyStatus, ListenerCanceler>,
) {
......@@ -310,7 +313,9 @@ object MultiplayerService {
)
)
) {}
listenForBattleStart(lobbyID, onFail) { battleID, cancelLobbyListener ->
listenForBattleStart(
lobbyID, hero, onFail
) { battleID, battleData, cancelLobbyListener ->
when (battleID) {
null ->
battleListener.accept(
......@@ -329,18 +334,14 @@ object MultiplayerService {
),
cancelLobbyListener
)
else ->
else -> {
battleListener.accept(
JoinLobbyStatus.StartBattle(
LobbyData(
lobbyID,
lobby.hostID,
userID,
battleID
)
battleData!!
),
cancelLobbyListener
)
}
}
}
}
......@@ -370,6 +371,7 @@ object MultiplayerService {
fun startBattle(
lobbyID: String,
hostHero: Hero,
onFail: FailFunc = { _, throwable -> throw throwable },
onCreated: (initialBattleData: BattleData) -> Unit = {}
) {
......@@ -380,7 +382,9 @@ object MultiplayerService {
battleID,
userID,
"",
listOf()
listOf(),
HeroData(hostHero),
null
)
// The otherPlayerId is set by that other player
// to confirm that the other player still wants to enter battle
......@@ -399,11 +403,18 @@ object MultiplayerService {
if (otherPlayerID == null || otherPlayerID == "") {
return@listen
}
this.battleID = battleID
databaseHandler.deleteValue(DbPath.Lobbies[lobbyID]) {}
otherPlayerIDListenerCanceler()
listenForActions(battleID, onFail)
onCreated(initialBattleData)
databaseHandler.read(
DbPath.Battles[battleID],
onFail
) {
it?.let {
this.battleID = battleID
databaseHandler.deleteValue(DbPath.Lobbies[lobbyID]) {}
otherPlayerIDListenerCanceler()
listenForActions(battleID, onFail)
onCreated(it)
}
}
}
}
}
......@@ -412,29 +423,35 @@ object MultiplayerService {
private fun listenForBattleStart(
lobbyID: String,
hero: Hero,
onFail: FailFunc = { _, throwable -> throw throwable },
consumer: BiConsumer<String?, ListenerCanceler>,
consumer: (String?, BattleData?, ListenerCanceler) -> Unit,
) {
// Have to listen on the battleID to also run the function when otherPlayerID changes.
databaseHandler.listen(DbPath.Lobbies[lobbyID], onFail = onFail) { lobbyData,
cancelLobbyListener ->
databaseHandler.listen(
DbPath.Lobbies[lobbyID], onFail = onFail
) { lobbyData, cancelLobbyListener ->
if (lobbyData?.battleID == null || lobbyData.battleID == "") {
consumer.accept(lobbyData?.battleID, cancelLobbyListener)
consumer(lobbyData?.battleID, null, cancelLobbyListener)
return@listen
}
databaseHandler.getUserID { userID ->
databaseHandler.setValue(
DbPath.Battles[lobbyData.battleID][BattleData::otherPlayerID],
userID,
onFail
) {
// The lobby will now be deleted
cancelLobbyListener()
// Keep track of the battleID of the joined battle
this.battleID = lobbyData.battleID
// Passing empty canceler to not call it twice
consumer.accept(lobbyData.battleID) {}
listenForActions(lobbyData.battleID, onFail)
databaseHandler.read(DbPath.Battles[lobbyData.battleID]) {
val data = it?.copy(otherPlayerID = userID, otherHero = HeroData(hero))
?: return@read
databaseHandler.setValue(
DbPath.Battles[lobbyData.battleID],
data,
onFail
) {
// The lobby will now be deleted
cancelLobbyListener()
// Keep track of the battleID of the joined battle
this.battleID = lobbyData.battleID
// Passing empty canceler to not call it twice
consumer(lobbyData.battleID, data) {}
listenForActions(lobbyData.battleID, onFail)
}
}
}
}
......
package se.battlegoo.battlegoose.network
import se.battlegoo.battlegoose.datamodels.BattleData
sealed class RandomPairingStatus {
object WaitingForOtherPlayer : RandomPairingStatus()
object CreatedLobby : RandomPairingStatus()
......@@ -9,8 +11,7 @@ sealed class RandomPairingStatus {
object FirstInQueue : RandomPairingStatus()
object Failed : RandomPairingStatus()
data class StartBattle(
val playerID: String,
val battleID: String,
val battle: BattleData,
val isHost: Boolean
) :
RandomPairingStatus()
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment