* Impl. combined forces attack

* Minor refactoring
* Fixed component updates on updating entities from snapshot
* Fixed mapping for incoming player moves
This commit is contained in:
Aldin Duraki 2020-05-20 00:11:49 +02:00
parent de5929d070
commit 1571a1e786
6 changed files with 83 additions and 92 deletions

View File

@ -48,10 +48,10 @@ class TimerComponent: GKComponent {
if !MultiplayerNetwork.sharedInstance.isSending { if !MultiplayerNetwork.sharedInstance.isSending {
MultiplayerNetwork.sharedInstance.sendPlayerMoves(playerMoves: DataService.sharedInstance.localPlayerMoves) MultiplayerNetwork.sharedInstance.sendPlayerMoves(playerMoves: DataService.sharedInstance.localPlayerMoves)
} }
if !RoundCalculatorServie.sharedInstance.isCalculating if !RoundCalculatorService.sharedInstance.isCalculating
&& DataService.sharedInstance.didReceiveAllData() && DataService.sharedInstance.didReceiveAllData()
&& MatchmakingHelper.sharedInstance.isServer { && MatchmakingHelper.sharedInstance.isServer {
RoundCalculatorServie.sharedInstance.calculateRound() RoundCalculatorService.sharedInstance.calculateRound()
} }
} }
} }

View File

@ -109,17 +109,17 @@ class EntityManager {
let base = (entity as! Base) let base = (entity as! Base)
if base.changeOwnership { if base.changeOwnership {
if let component = entity.component(ofType: TeamComponent.self) { //FIX-ME: Find a way to update the Component without deleting it upfront
component.player = entities[0].component(ofType: TeamComponent.self)!.player //TODO: outsource component handling to a generic function
component.team = entities[0].component(ofType: TeamComponent.self)!.team base.removeComponent(ofType: TeamComponent.self)
} else {
base.addComponent(TeamComponent( base.addComponent(TeamComponent(
team: (entities[0] as! Base).component(ofType: TeamComponent.self)!.team, team: (entities[0] as! Base).component(ofType: TeamComponent.self)!.team,
player: (entities[0] as! Base).component(ofType: TeamComponent.self)!.player, player: (entities[0] as! Base).component(ofType: TeamComponent.self)!.player,
position: (base.component(ofType: DefaultBaseComponent.self)?.spriteNode.position)! position: (base.component(ofType: DefaultBaseComponent.self)?.spriteNode.position)!
) )
) )
scene.addChild(base.component(ofType: TeamComponent.self)!.fire) if let fire = entity.component(ofType: TeamComponent.self)?.fire{
scene.addChild(fire)
} }
base.changeOwnership = false base.changeOwnership = false
} }
@ -131,8 +131,6 @@ class EntityManager {
for entity in bases{ for entity in bases{
let base = entity as! Base let base = entity as! Base
let snapBase = self.getSnapshotBaseById(baseId: base.baseID, snapshotModel: snapshotModel) let snapBase = self.getSnapshotBaseById(baseId: base.baseID, snapshotModel: snapshotModel)
print("in updateSnap -> Entity \(base)")
print("in updateSnap -> snapBase \(snapBase)")
var getOwnerBySnapBase: GKPlayer? = nil var getOwnerBySnapBase: GKPlayer? = nil
base.unitCount = snapBase.unitCount base.unitCount = snapBase.unitCount
@ -142,10 +140,9 @@ class EntityManager {
if getOwnerBySnapBase != nil { if getOwnerBySnapBase != nil {
base.changeOwnership = true base.changeOwnership = true
base.ownershipPlayer = getOwnerBySnapBase base.ownershipPlayer = getOwnerBySnapBase
if let component = entity.component(ofType: TeamComponent.self) { //FIX-ME: Find a way to update the Component without deleting it upfront
component.player = getOwnerBySnapBase! //TODO: outsource component handling to a generic function
component.team = getTeamByPlayer(playerName: snapBase.ownership!) entity.removeComponent(ofType: TeamComponent.self)
} else {
entity.addComponent(TeamComponent( entity.addComponent(TeamComponent(
team: getTeamByPlayer(playerName: snapBase.ownership!), team: getTeamByPlayer(playerName: snapBase.ownership!),
player: getOwnerBySnapBase!, player: getOwnerBySnapBase!,
@ -156,10 +153,6 @@ class EntityManager {
scene.addChild(fire) scene.addChild(fire)
} }
} }
}
print("nach updateSnap -> Entity \(base)")
} }
} }

View File

@ -118,7 +118,6 @@ class MatchmakingHelper: NSObject, GKMatchmakerViewControllerDelegate, GKMatchDe
} }
if let snapshotModel = try? jsonDecoder.decode(SnapshotModel.self, from: data) { if let snapshotModel = try? jsonDecoder.decode(SnapshotModel.self, from: data) {
print("received data package -> \(snapshotModel)")
DataService.sharedInstance.snapshotModel = snapshotModel DataService.sharedInstance.snapshotModel = snapshotModel
EntityManager.sharedInstance.updateSnapshotModel(snapshotModel: snapshotModel) EntityManager.sharedInstance.updateSnapshotModel(snapshotModel: snapshotModel)
} }

View File

@ -12,6 +12,7 @@ import GameKit
class MultiplayerNetwork{ class MultiplayerNetwork{
static let sharedInstance = MultiplayerNetwork() static let sharedInstance = MultiplayerNetwork()
var isSending = false var isSending = false
func sendData(data: Data) { func sendData(data: Data) {
@ -20,25 +21,20 @@ class MultiplayerNetwork{
do { do {
try multiplayerMatch.sendData(toAllPlayers: data, with: .reliable) try multiplayerMatch.sendData(toAllPlayers: data, with: .reliable)
} catch { } catch {
print("Tim war am Werk") //TODO: Add logging
} }
} }
} }
func sendDataToHost(data: Data) { func sendDataToHost(data: Data) {
let mmHelper = MatchmakingHelper.sharedInstance let mmHelper = MatchmakingHelper.sharedInstance
for player in mmHelper.mpMatch!.players {
print(player.displayName)
}
print(DataService.sharedInstance.gameHost!.playerName)
let hostGKPlayer = MatchmakingHelper.sharedInstance.mpMatch?.players.filter{ $0.displayName == DataService.sharedInstance.gameHost!.playerName}[0] let hostGKPlayer = MatchmakingHelper.sharedInstance.mpMatch?.players.filter{ $0.displayName == DataService.sharedInstance.gameHost!.playerName}[0]
if let multiplayerMatch = mmHelper.mpMatch{ if let multiplayerMatch = mmHelper.mpMatch{
do { do {
try multiplayerMatch.send(data, to: [hostGKPlayer!], dataMode: .reliable) try multiplayerMatch.send(data, to: [hostGKPlayer!], dataMode: .reliable)
} catch { } catch {
print("Tim war mal wieder am Werk der Krasse") //TODO: Add logging
} }
} }
} }
@ -59,10 +55,8 @@ class MultiplayerNetwork{
} }
func sendSnapshotModelToPlayers() { func sendSnapshotModelToPlayers() {
print("sending snapshot -> \(DataService.sharedInstance.snapshotModel)")
let encoder = JSONEncoder() let encoder = JSONEncoder()
let encoded = (try? encoder.encode(DataService.sharedInstance.snapshotModel))! let encoded = (try? encoder.encode(DataService.sharedInstance.snapshotModel))!
print("sending package -> \(encoded)")
sendData(data: encoded) sendData(data: encoded)
} }

View File

@ -6,47 +6,43 @@
// Copyright © 2020 SP2. All rights reserved. // Copyright © 2020 SP2. All rights reserved.
// //
import Foundation
import GameKit import GameKit
import os import os
class RoundCalculatorServie { class RoundCalculatorService {
static let sharedInstance = RoundCalculatorServie() static let sharedInstance = RoundCalculatorService()
var isCalculating = false static let LOG = OSLog.init(subsystem: "Round Calculator", category: "RoundCalculatorService")
var allPlayerMoves: [String: [PlayerMove]] = [:] var allPlayerMoves: [String: [PlayerMove]] = [:]
var baseSpecificMoves: [Int: [String: PlayerMove]] = [:] var baseSpecificMoves: [Int: [String: [PlayerMove]]] = [:]
var isCalculating = false
func calculateRound() { func calculateRound() {
os_log("Started calculating Round", log: OSLog.default, type: .info) os_log("Started calculating Round", log: RoundCalculatorService.LOG, type: .info)
let startTime = CFAbsoluteTimeGetCurrent()
isCalculating = true isCalculating = true
let currentSnapshotModel = DataService.sharedInstance.snapshotModel
var currentSnapshotModel = DataService.sharedInstance.snapshotModel for playerMove in DataService.sharedInstance.remotePlayerMoves {
addPlayerMove(playerName: playerMove.key, playerMoves: playerMove.value)
// TODO: smarter way?
for entry in DataService.sharedInstance.remotePlayerMoves {
addPlayerMove(playerName: entry.key, playerMoves: entry.value)
} }
addPlayerMove(playerName: GKLocalPlayer.local.displayName, playerMoves: DataService.sharedInstance.localPlayerMoves) addPlayerMove(playerName: GKLocalPlayer.local.displayName, playerMoves: DataService.sharedInstance.localPlayerMoves)
for entry in allPlayerMoves { for playerMove in allPlayerMoves {
for move in entry.value { for move in playerMove.value {
mapPlayerMoveToAttackedBase(playerName: entry.key, playerMove: move) mapPlayerMoveToAttackedBase(playerName: playerMove.key, playerMove: move)
} }
} }
// TODO-END:
// TODO: Refactor to a less complex way
// TODO: Refactor -> O(n*3n^2) to maybe O(n*3n)
// We might not need to map and iterate over toBase
for (key, value) in baseSpecificMoves { for (key, value) in baseSpecificMoves {
let baseId = key let baseId = key
var playerMovesByBase = value var playerMovesByBase = value
let targetBase = currentSnapshotModel?.baseEntites.filter { $0.baseId == baseId }[0] let targetBase = currentSnapshotModel?.baseEntites.filter { $0.baseId == baseId }[0]
let possiblyOwnershipMoves = playerMovesByBase.filter { $0.key == targetBase?.ownership} let possiblyOwnershipMoves = playerMovesByBase.filter { $0.key == targetBase?.ownership}
for (playerName, playerMove) in possiblyOwnershipMoves { for (playerName, playerMoves) in possiblyOwnershipMoves {
for playerMove in playerMoves {
for base in currentSnapshotModel!.baseEntites { for base in currentSnapshotModel!.baseEntites {
if base.baseId == playerMove.fromBase { if base.baseId == playerMove.fromBase {
base.unitCount -= playerMove.unitCount base.unitCount -= playerMove.unitCount
@ -55,44 +51,52 @@ class RoundCalculatorServie {
base.unitCount += playerMove.unitCount base.unitCount += playerMove.unitCount
} }
} }
}
playerMovesByBase.removeValue(forKey: playerName) playerMovesByBase.removeValue(forKey: playerName)
} }
for (_, playerMove) in playerMovesByBase { for (_, playerMoves) in playerMovesByBase {
for playerMove in playerMoves {
for base in currentSnapshotModel!.baseEntites { for base in currentSnapshotModel!.baseEntites {
if base.baseId == playerMove.fromBase { if base.baseId == playerMove.fromBase {
base.unitCount -= playerMove.unitCount base.unitCount -= playerMove.unitCount
} }
} }
} }
let sorted = playerMovesByBase.sorted { (e1: (key: String, value: PlayerMove), e2: (key: String, value: PlayerMove)) -> Bool in
e1.value.unitCount > e2.value.unitCount
} }
var max = sorted[0] var combinePotentionalForces: [String: PlayerMove] = [:]
if sorted.count == 2 {
let secMax = sorted[1] for playerMoves in playerMovesByBase {
max.value.unitCount -= secMax.value.unitCount combinePotentionalForces[playerMoves.key] = PlayerMove(fromBase: playerMoves.value[0].fromBase, toBase: playerMoves.value[0].toBase, unitCount: 0)
for move in playerMoves.value {
combinePotentionalForces[playerMoves.key]!.unitCount += move.unitCount
}
}
let sortedPotentionalCombinedForces = combinePotentionalForces.sorted { $0.1.unitCount > $1.1.unitCount }
var playerMoveWithMaxUnits = sortedPotentionalCombinedForces[0]
if playerMovesByBase.count >= 2 {
let playerMoveWithSecMaxUnits = sortedPotentionalCombinedForces[1]
playerMoveWithMaxUnits.value.unitCount -= playerMoveWithSecMaxUnits.value.unitCount
} }
for base in currentSnapshotModel!.baseEntites { for base in currentSnapshotModel!.baseEntites {
if base.baseId == max.value.toBase { if base.baseId == playerMoveWithMaxUnits.value.toBase {
base.unitCount += max.value.unitCount base.unitCount += playerMoveWithMaxUnits.value.unitCount
if max.value.unitCount == 0 { if playerMoveWithMaxUnits.value.unitCount == 0 {
base.ownership = nil base.ownership = nil
} else { } else {
base.ownership = max.key base.ownership = playerMoveWithMaxUnits.key
} }
} }
} }
baseSpecificMoves.removeValue(forKey: baseId) baseSpecificMoves.removeValue(forKey: baseId)
} }
MultiplayerNetwork.sharedInstance.sendSnapshotModelToPlayers() MultiplayerNetwork.sharedInstance.sendSnapshotModelToPlayers()
EntityManager.sharedInstance.updateSnapshotModel(snapshotModel: currentSnapshotModel!) EntityManager.sharedInstance.updateSnapshotModel(snapshotModel: currentSnapshotModel!)
let calcTime = CFAbsoluteTimeGetCurrent() - startTime os_log("Finished calculating Round", log: RoundCalculatorService.LOG, type: .info)
os_log("Finished calculating Round in %@", log: OSLog.default, type: .info)
} }
func addPlayerMove(playerName: String, playerMoves: [PlayerMove]) { func addPlayerMove(playerName: String, playerMoves: [PlayerMove]) {
@ -101,17 +105,18 @@ class RoundCalculatorServie {
func mapPlayerMoveToAttackedBase(playerName: String, playerMove: PlayerMove) { func mapPlayerMoveToAttackedBase(playerName: String, playerMove: PlayerMove) {
if self.baseSpecificMoves.keys.contains(playerMove.toBase) { if self.baseSpecificMoves.keys.contains(playerMove.toBase) {
var playerMovesForSameBase = self.baseSpecificMoves[playerMove.toBase]! if (self.baseSpecificMoves[playerMove.toBase]?.keys.contains(playerName))!{
if playerMovesForSameBase.keys.contains(playerName) { self.baseSpecificMoves[playerMove.toBase]?[playerName]?.append(playerMove)
playerMovesForSameBase[playerName] = playerMove } else {
self.baseSpecificMoves[playerMove.toBase]?.merge([playerName: [playerMove]]){(current, _) in current}
} }
} else { } else {
self.baseSpecificMoves[playerMove.toBase] = [playerName: playerMove] self.baseSpecificMoves[playerMove.toBase] = [playerName: [playerMove]]
} }
} }
func resolvePlayerMove(playerMove: PlayerMove, unitCount: Int, ownership: String?, resolveType: String) { func resolvePlayerMove(playerMove: PlayerMove, unitCount: Int, ownership: String?, resolveType: String) {
//outsource playermoves || tnx, remove, add(with calc) //TODO: outsource playermoves
} }
} }

View File

@ -23,7 +23,7 @@ class SoundManager {
do { do {
audioPlayer = try AVAudioPlayer(contentsOf: backgroundMainMenuAudio!) audioPlayer = try AVAudioPlayer(contentsOf: backgroundMainMenuAudio!)
} catch { } catch {
print("Datei nicht gefunden!") //TODO: Add logging
} }
audioPlayer.numberOfLoops = -1 audioPlayer.numberOfLoops = -1
audioPlayer.prepareToPlay() audioPlayer.prepareToPlay()