software-projekt-2-gold-wars/GoldWars/GoldWars/GameCenterManager.swift

346 lines
15 KiB
Swift
Raw Normal View History

//
// GameCenter.swift
// GoldWars
//
// Created by Niko Jochim on 23.05.20.
// Copyright © 2020 SP2. All rights reserved.
//
import Foundation
import GameKit
import os
struct RandomNumber: Codable {
let number: Int
init() {
number = Int.random(in: 0...999999)
}
}
struct State: Codable {
// 0 PlayerInit fertig
// 1 RemotePlayerInit fertig
// 2 Peer hat Map erhalten
// 3 Host hat Spiel gestartet
// 4 Peer hat verloren
// 5 Peer hat gewonnen
let state: Int
}
final class GameCenterManager: NSObject, GKMatchmakerViewControllerDelegate, GKGameCenterControllerDelegate ,GKMatchDelegate,GKLocalPlayerListener{
static let sharedInstance = GameCenterManager()
let LOG = OSLog.init(subsystem: "GameCenterManager", category: "GameCenterManager")
var viewController: UIViewController?
let localPlayer: GKLocalPlayer = GKLocalPlayer.local
var myMatch: GKMatch?
var isMatchStarted = false
var isServer = false
var hostingPlayer: GKPlayer?
var peerPlayer: GKPlayer?
var menusc: MenuScene?
var entityManager = EntityManager.gameEMInstance
var localPlayerRandomNumber: RandomNumber?
var initIsFinish = false
2020-06-07 12:00:12 +02:00
var gameEnded = false
var winner:String?
var gameScene: GameScene?
static var isAuthenticated: Bool {
return GKLocalPlayer.local.isAuthenticated
}
override init() {
super.init()
localPlayer.register(self)
authUser();
localPlayerRandomNumber = RandomNumber()
}
func authUser() -> Void {
GKLocalPlayer.local.authenticateHandler = { gcAuthVC, error in
NotificationCenter.default
.post(name: .authenticationChanged, object: GKLocalPlayer.local.isAuthenticated)
if let vc = gcAuthVC {
self.viewController?.present(vc, animated: true)
}
}
}
func presentMatchmaker() {
let request = GKMatchRequest()
request.minPlayers = 2
request.maxPlayers = 2
request.defaultNumberOfPlayers = 2
request.inviteMessage = "Willst du GoldWars spielen?"
let matchmakerVC = GKMatchmakerViewController.init(matchRequest: request)
matchmakerVC!.matchmakerDelegate = self
viewController?.present(matchmakerVC!, animated: true, completion: nil)
}
func presentGameCenter() {
let gameCenterController: GKGameCenterViewController = GKGameCenterViewController.init()
gameCenterController.gameCenterDelegate = self
gameCenterController.viewState = .achievements
viewController?.present(gameCenterController, animated: true, completion: nil)
}
func addAchievementProgress(identifier: String, increasePercentComplete: Double) {
GKAchievement.loadAchievements { (achievements: [GKAchievement]?, err: Error?) in
var achievementExists: Bool = false
achievements?.forEach({ (achievement: GKAchievement) in
print(achievement.identifier)
if achievement.identifier == identifier {
achievementExists = true
achievement.percentComplete += increasePercentComplete
achievement.showsCompletionBanner = true
GKAchievement.report([achievement]) { (error) in
print(error?.localizedDescription ?? "")
}
}
})
if !achievementExists {
let newAchievement: GKAchievement = GKAchievement.init(identifier: identifier)
newAchievement.showsCompletionBanner = true
newAchievement.percentComplete = increasePercentComplete
GKAchievement.report([newAchievement]) { (error) in
print(error?.localizedDescription ?? "")
}
}
}
}
2020-06-10 18:32:31 +02:00
func submitLeaderboardScore(identifier: String, score: Int64) {
let reportedScore = GKScore(leaderboardIdentifier: identifier)
reportedScore.value = score
GKScore.report([reportedScore]) { (error) in
guard error == nil else {
print(error?.localizedDescription ?? "")
return
}
}
}
func calculateLeaderboardScore(identifier: String, scoreOffset: Int64) {
GKLeaderboard.loadLeaderboards { (leaderboards: [GKLeaderboard]?, err: Error?) in
leaderboards?.forEach({ (leaderboard: GKLeaderboard) in
if leaderboard.identifier == identifier {
leaderboard.loadScores { (scores: [GKScore]?, err: Error?) in
scores?.forEach({ (score: GKScore) in
if score.player == self.localPlayer {
score.value += scoreOffset
GKScore.report([score]) { (error) in
guard error == nil else {
print(error?.localizedDescription ?? "")
return
}
}
}
})
}
}
})
}
}
//TODO: not finished -> DELETE if not usefull
// Possible way to implement matchmaking based on 2 players
// can be changed into method with fixed leaderboard and offset, boolean hasWon: true/false to calculate outcome of a match
func calculateLeaderboardScoreExperimentalMatchmaking(identifier: String, scoreOffset: Int64, opponentPlayer: GKPlayer) {
// Iterate through leaderboards
GKLeaderboard.loadLeaderboards { (leaderboards: [GKLeaderboard]?, err: Error?) in
leaderboards?.forEach({ (leaderboard: GKLeaderboard) in
if leaderboard.identifier == identifier {
leaderboard.loadScores { (scores: [GKScore]?, err: Error?) in
// Iterate through scores
var currentScore: GKScore = GKScore.init()
currentScore.value = -1
var opponetScore: GKScore = GKScore.init()
currentScore.value = 0
// Get scores of self and opponent
scores?.forEach({ (score: GKScore) in
if score.player == self.localPlayer {
currentScore = score
opponetScore = score
}
if score.player == opponentPlayer {
opponetScore = score
}
})
// do silly calculation
currentScore.value += scoreOffset + (opponetScore.value - currentScore.value)
GKScore.report([currentScore]) { (error) in
guard error == nil else {
print(error?.localizedDescription ?? "")
return
}
}
}
}
})
}
}
func match(_ match: GKMatch, didReceive data: Data, fromRemotePlayer player: GKPlayer) {
if myMatch != match { return }
let jsonDecoder = JSONDecoder()
if let randomNumberFromPeer = try? jsonDecoder.decode(RandomNumber.self, from: data) {
os_log("Random Number des anderen Spielers erhalten", log: LOG, type: .info)
if randomNumberFromPeer.number <= localPlayerRandomNumber!.number {
isServer = true
self.hostingPlayer = GKLocalPlayer.local
self.peerPlayer = player
os_log("Dieser Spieler wurde zum HostingPlayer gewählt", log: LOG, type: .info)
} else {
isServer = false
self.hostingPlayer = player
self.peerPlayer = GKLocalPlayer.local
os_log("Dieser Spieler wurde zum PeerPlayer gewählt", log: LOG, type: .info)
}
os_log("Setzen von Peer und Host abgeschlossen", log: LOG, type: .info)
sendStateToPeers(state: State(state: 0))
os_log("State 0 wurde an anderen Spieler gesendet", log: LOG, type: .info)
}
if let state = try? jsonDecoder.decode(State.self, from: data) {
switch state.state {
2020-06-02 22:18:59 +02:00
case 0:
os_log("State 0 erhalten", log: LOG, type: .info)
sendStateToPeers(state: State(state: 1))
case 1:
os_log("State 1 erhalten", log: LOG, type: .info)
if isServer {
os_log("Peer hat Player initialisiert", log: LOG, type: .info)
initAndSendMap()
}else {
os_log("Host hat Player initialisiert", log: LOG, type: .info)
}
case 2:
os_log("State 2 erhalten", log: LOG, type: .info)
sendStateToPeers(state: State(state: 3))
initIsFinish = true
os_log("Spiel startet", log: LOG, type: .info)
case 3:
os_log("State 3 erhalten", log: LOG, type: .info)
initIsFinish = true
os_log("Spiel startet", log: LOG, type: .info)
case 4:
os_log("State 4 erhalten, Peer hat verloren", log: LOG, type: .info)
2020-06-07 12:00:12 +02:00
winner = hostingPlayer?.displayName
gameEnded = true
2020-06-02 22:18:59 +02:00
case 5:
os_log("State 5 erhalten, Peer hat gewonnen", log: LOG, type: .info)
2020-06-07 12:00:12 +02:00
winner = peerPlayer?.displayName
gameEnded = true
2020-06-02 22:18:59 +02:00
default:
break
}
}
2020-06-01 17:27:13 +02:00
if let roundData = try? jsonDecoder.decode(LocalRoundData.self, from: data) {
DataService.sharedInstance.addRemotePlayerMoves(playerName: player.displayName, localRoundData: roundData)
}
if let snapshotModel = try? jsonDecoder.decode(SnapshotModel.self, from: data) {
DataService.sharedInstance.snapshotModel = snapshotModel
2020-06-02 20:52:11 +02:00
RoundCalculatorService.sharedInstance.currentRound += 1
entityManager.getHUD()?.setCurrentRound(round: RoundCalculatorService.sharedInstance.currentRound)
entityManager.updateSnapshotModel(snapshotModel: snapshotModel)
entityManager.getHUD()?.startWithDuration()
}
if let mapModel = try? jsonDecoder.decode(MapGenerationModel.self, from: data) {
os_log("Peer hat Map erhalten", log: LOG, type: .info)
let scene = GameScene(size: self.menusc!.size)
EntityManager.gameEMInstance.setScene(scene: scene)
DataService.sharedInstance.setMapModel(model: mapModel)
os_log("Map model wurde gesetzt", log: LOG, type: .info)
GameCenterManager.sharedInstance.isMatchStarted = true
self.gameScene = scene
sendStateToPeers(state: State(state: 2))
os_log("State 2 wurde an Host gesendet", log: LOG, type: .info)
initIsFinish = true
os_log("Peer startet Spiel", log: LOG, type: .info)
}
2020-06-06 18:59:26 +02:00
if let notification = try? jsonDecoder.decode(NotificationModel.self, from: data) {
os_log("Notification erhalten", log: LOG, type: .info)
NotificationCenter.default.post(name: Notification.Name(rawValue: notification.name), object: nil)
}
MultiplayerNetwork.sharedInstance.isSending = false
}
func initAndSendMap() -> Void {
self.gameScene = GameScene(size: self.menusc!.size)
let mapModel = MapFactory(scene: self.gameScene!, entityManager: entityManager).load()
os_log("Map wurde erstellt", log: LOG, type: .info)
MultiplayerNetwork.sharedInstance.sendMapModelToPlayers(mapModel: mapModel)
os_log("Map wurde an Peer gesendet", log: LOG, type: .info)
DataService.sharedInstance.setSnapshotModel(snapshotModel: entityManager.getSnapshotModel())
os_log("SnapshotModel wurde erstellt", log: LOG, type: .info)
}
func getGKPlayerByUsername(displayName: String) -> GKPlayer? {
let nilGK : GKPlayer? = nil
if GKLocalPlayer.local.displayName == displayName {
return GKLocalPlayer.local
}
for player in myMatch!.players {
if player.displayName == displayName {
return player
}
}
return nilGK
}
func sendStateToPeers(state: State){
let encoder = JSONEncoder()
let encoded = (try? encoder.encode(state))!
MultiplayerNetwork.sharedInstance.sendData(data: encoded)
}
func sendRandomNumberToAllPeers(in match: GKMatch){
let encoder = JSONEncoder()
let encoded = (try? encoder.encode(localPlayerRandomNumber))!
MultiplayerNetwork.sharedInstance.sendData(data: encoded)
}
func player(_ player: GKPlayer, didAccept invite: GKInvite) {
os_log("Einladung angenommen", log: LOG, type: .info)
let matchmakerVC = GKMatchmakerViewController.init(invite: invite)
matchmakerVC!.matchmakerDelegate = self
viewController?.present(matchmakerVC!, animated: true, completion: nil)
}
func matchmakerViewControllerWasCancelled(_ viewController: GKMatchmakerViewController) {
viewController.dismiss(animated: true, completion: nil)
}
func matchmakerViewController(_ viewController: GKMatchmakerViewController, didFailWithError error: Error) {
viewController.dismiss(animated: true, completion: nil)
}
func matchmakerViewController(_ viewController: GKMatchmakerViewController, didFind match: GKMatch) {
viewController.dismiss(animated: true, completion: nil)
myMatch = match
if !isMatchStarted && match.expectedPlayerCount == 0 {
myMatch?.delegate = self
sendRandomNumberToAllPeers(in: self.myMatch!)
os_log("Random Number wurde an den anderen Spieler gesendet", log: LOG, type: .info)
}
}
func gameCenterViewControllerDidFinish(_ gameCenterViewController: GKGameCenterViewController) {
gameCenterViewController.dismiss(animated: true, completion: nil)
}
func disconnect() {
if myMatch != nil {
myMatch?.disconnect()
}
}
}
extension Notification.Name {
static let presentGame = Notification.Name(rawValue: "presentGame")
static let authenticationChanged = Notification.Name(rawValue: "authenticationChanged")
}