diff --git a/GoldWars/GoldWars.xcodeproj/project.pbxproj b/GoldWars/GoldWars.xcodeproj/project.pbxproj index c2a5fac..11e05ac 100644 --- a/GoldWars/GoldWars.xcodeproj/project.pbxproj +++ b/GoldWars/GoldWars.xcodeproj/project.pbxproj @@ -44,6 +44,7 @@ AB1D759D245DD18100671525 /* TwoPlayerDefaultTestMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB1D759B245DD18100671525 /* TwoPlayerDefaultTestMap.swift */; }; AB1D75A0245DEC0500671525 /* MapFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB1D759F245DEC0500671525 /* MapFactory.swift */; }; ABA03DA0244BD54F00A66916 /* Base.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABA03D9F244BD54F00A66916 /* Base.swift */; }; + AE151589245F18EF001D363E /* MatchmakingHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE151588245F18EF001D363E /* MatchmakingHelper.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -99,6 +100,7 @@ AB1D759B245DD18100671525 /* TwoPlayerDefaultTestMap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TwoPlayerDefaultTestMap.swift; sourceTree = ""; }; AB1D759F245DEC0500671525 /* MapFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapFactory.swift; sourceTree = ""; }; ABA03D9F244BD54F00A66916 /* Base.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Base.swift; sourceTree = ""; }; + AE151588245F18EF001D363E /* MatchmakingHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchmakingHelper.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -154,6 +156,7 @@ 110360DA244B101A008610AF /* GameViewController.swift */, 110360DF244B101B008610AF /* Assets.xcassets */, 110360E4244B101B008610AF /* Info.plist */, + AE151588245F18EF001D363E /* MatchmakingHelper.swift */, 3EBD242B245D8044003CECE7 /* GameCenterHelper.swift */, ); path = GoldWars; @@ -372,6 +375,7 @@ 9E78ACBA245CBDAF00526FF7 /* HUD.swift in Sources */, 11738A3B24508F68004426F1 /* Unit.swift in Sources */, 9E174C86245DD91500209FF0 /* ButtonComponent.swift in Sources */, + AE151589245F18EF001D363E /* MatchmakingHelper.swift in Sources */, 11036113244B3E30008610AF /* MenuScene.swift in Sources */, 9EA3ABE9245C6DAA006BC61D /* DefaultBaseComponent.swift in Sources */, 9E174C8A245E1A0A00209FF0 /* Background.swift in Sources */, @@ -549,8 +553,8 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = GoldWars/GoldWars.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = DDKFQG46BQ; INFOPLIST_FILE = GoldWars/Info.plist; @@ -561,7 +565,7 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = de.hft.stuttgart.ip2.goldwars; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; + PROVISIONING_PROFILE_SPECIFIER = "Developer Profile"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 2; }; @@ -572,8 +576,8 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = GoldWars/GoldWars.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = DDKFQG46BQ; INFOPLIST_FILE = GoldWars/Info.plist; @@ -584,7 +588,7 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = de.hft.stuttgart.ip2.goldwars; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; + PROVISIONING_PROFILE_SPECIFIER = "Developer Profile"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 2; }; diff --git a/GoldWars/GoldWars/GameViewController.swift b/GoldWars/GoldWars/GameViewController.swift index 9fb663d..e4a2f43 100644 --- a/GoldWars/GoldWars/GameViewController.swift +++ b/GoldWars/GoldWars/GameViewController.swift @@ -22,9 +22,10 @@ class GameViewController: UIViewController { //TODO: create dev profile or remove on delivery view.showsFPS = true view.showsNodeCount = true - - GameCenterHelper.helper.viewController = self - } + + GameCenterHelper.helper.viewController = self + MatchmakingHelper.sharedInstance.viewController = self + } } override var shouldAutorotate: Bool { diff --git a/GoldWars/GoldWars/MatchmakingHelper.swift b/GoldWars/GoldWars/MatchmakingHelper.swift new file mode 100644 index 0000000..48628ce --- /dev/null +++ b/GoldWars/GoldWars/MatchmakingHelper.swift @@ -0,0 +1,178 @@ +// +// MatchmakingHelper.swift +// GoldWars +// +// Created by Chauntalle Schüle, Ömer Özel, Eray Kör on 03.05.20. +// Copyright © 2020 SP2. All rights reserved. +// + +import GameKit + +protocol GameKitHelperDelegate { + func matchStarted() + func matchEnded() + func matchReceivedData(match: GKMatch, data: NSData, + fromPlayer player: String) + +} +/* + Diese Klasse wird für das Matchmaking über das Gamecenter gebraucht + @param: delegate: erhält alle Multiplayerevents + @param: mpMatch: repräsentiert das Netzwerk, alle Mitspieler besitzen ein gemeinsames Spiel + @param: viewController: ist nicht der ViewController des Gamecenters, sondern des Spiels + @param: mpMatchStarted: gibt an, ob ein Match schon gestartet wurde + @param: isServer: gibt an, ob der local player, als der Server fungiert + @param: spieler1: ist immer der Spieler, der der Server ist + @param: spieler2: ist immer der Spieler, der der Client ist + @param: sharedInstance: ist eine geteilte Instanz für alle Spieler + */ +class MatchmakingHelper: NSObject, GKMatchmakerViewControllerDelegate, GKMatchDelegate { + + var delegate: GameKitHelperDelegate? + var mpMatch: GKMatch? + var viewController: UIViewController? + var mpMatchStarted: Bool + var isServer: Bool + var spieler1: GKPlayer? + var nameSpieler1 = "" + var menusc: MenuScene? + let localPlayer: GKLocalPlayer = GKLocalPlayer.local + + static let sharedInstance = MatchmakingHelper() + + + static var isAuthenticated: Bool{ + return GKLocalPlayer.local.isAuthenticated + } + + override init() { + mpMatchStarted = false + isServer = false + super.init() + } + + /* + Diese Methode wechselt von der MenuScene zum GameCenter ViewController. Die Spieler werden hier gesucht und gematcht. Mit minplayers/maxplayers kann bestimmt werden, wie viele Spieler insgesamt zusammen miteinander spielen. + */ + func presentMatchmaker(scene: MenuScene) { + menusc = scene + guard GKLocalPlayer.local.isAuthenticated else { + return + } + let request = GKMatchRequest() + + request.minPlayers = 2 + request.maxPlayers = 2 + request.inviteMessage = "Willst du GoldWars spielen?" + + let matchmakerVC = GKMatchmakerViewController.init(matchRequest: request) + matchmakerVC!.matchmakerDelegate = self + viewController?.present(matchmakerVC!, animated: true, completion: nil) + } + + /* + Der User hat die Verbindung mit "Abbrechen" unterbrochen. GameCenter MatchMaking wird beendet. + */ + func matchmakerViewControllerWasCancelled(_ viewController: GKMatchmakerViewController) { + viewController.dismiss(animated: true, completion: nil) + delegate?.matchEnded() + } + + /* + Wenn GameCenter kein match erstellen kann, wird der viewcontroller dismissed. + */ + func matchmakerViewController(_ viewController: GKMatchmakerViewController, didFailWithError error: Error) { + viewController.dismiss(animated: true, completion: nil) + print("Error finding match", error.localizedDescription) + delegate?.matchEnded() + } + + /* + Gamecenter hat erfolgreich ein Match gefunden, das Spiel kann gestartet werden. + expectedPlayerCount: Die verbleibende Anzahl von Spielern, die sich noch nicht mit dem Spiel verbunden haben + z.B 0 gibt an, dass keine weiteren Spieler benötigt werden um das Match zu starten + */ + func matchmakerViewController(_ viewController: GKMatchmakerViewController, didFind match: GKMatch) { + viewController.dismiss(animated: true, completion: nil) + mpMatch = match + match.delegate = self + if !mpMatchStarted && match.expectedPlayerCount == 0 { + startMatch() + } + } + + /* + Vom match erhaltene Spielerdaten + */ + private func match(match: GKMatch!, didReceiveData data: NSData!,fromPlayer playerID: String!) { + if mpMatch != match { return } + delegate?.matchReceivedData(match: match, data: data, fromPlayer: playerID) + + } + + /* + Verbindung/Matchmaking ist fehlgeschlagen + */ + private func match(match: GKMatch!, didFailWithError error: NSError!) { + if mpMatch != match { + return + } + mpMatchStarted = false + delegate?.matchEnded() + } + + /* + Wird beim ändern des States/Zustands des Spielers aufgerufen udn gibt dessen Zustand zurück. + */ + func match(_ match: GKMatch, player: GKPlayer, didChange state: GKPlayerConnectionState) { + if mpMatch != match { + return + } + + switch (state) { + case GKPlayerConnectionState.connected: + if (!mpMatchStarted && match.expectedPlayerCount == 0) { + startMatch() + } + case GKPlayerConnectionState.disconnected: + mpMatchStarted = false + delegate?.matchEnded() + default: + delegate?.matchEnded() + } + } + + /* + Ein Spieler wird als Host für das Match gewählt. Dieser ist Spieler 1. Im Anschluss wird die GameScene geladen. + */ + func startMatch() { + + mpMatch!.chooseBestHostingPlayer(completionHandler: { + (player) in + + self.mpMatchStarted = true + + if player == GKLocalPlayer.local { + self.isServer = true + self.spieler1 = player + self.nameSpieler1 = self.spieler1!.displayName + + } else { + self.isServer = false + } + + self.delegate?.matchStarted() + self.menusc!.loadScene(scene: GameScene(size: self.menusc!.size)) + }) + } + + + /* + Trennt die Verbindung vom Match + */ + func disconnect() { + if mpMatch != nil { + mpMatch?.disconnect() + } + } +} diff --git a/GoldWars/GoldWars/Scenes/MenuScene.swift b/GoldWars/GoldWars/Scenes/MenuScene.swift index 8164439..edf6bb9 100644 --- a/GoldWars/GoldWars/Scenes/MenuScene.swift +++ b/GoldWars/GoldWars/Scenes/MenuScene.swift @@ -21,7 +21,11 @@ class MenuScene: SKScene { text: "Start Game", position: CGPoint(x: midX, y: midY), onButtonPress: { - self.loadScene(scene: GameScene(size: self.size)) + if CommandLine.arguments.contains("--no-matchmaking") { + self.loadScene(scene: GameScene(size: self.size)) + } else { + MatchmakingHelper.sharedInstance.presentMatchmaker(scene: self) + } })) entityManager.add(Button(name: "settingsButton", iconName: "",