diff --git a/GoldWars/.idea/.gitignore b/GoldWars/.idea/.gitignore new file mode 100644 index 0000000..73f69e0 --- /dev/null +++ b/GoldWars/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/GoldWars/.idea/GoldWars.iml b/GoldWars/.idea/GoldWars.iml new file mode 100644 index 0000000..74121dc --- /dev/null +++ b/GoldWars/.idea/GoldWars.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/GoldWars/.idea/misc.xml b/GoldWars/.idea/misc.xml new file mode 100644 index 0000000..28a804d --- /dev/null +++ b/GoldWars/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/GoldWars/.idea/modules.xml b/GoldWars/.idea/modules.xml new file mode 100644 index 0000000..3bd1ffb --- /dev/null +++ b/GoldWars/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/GoldWars/.idea/runConfigurations/GoldWars.xml b/GoldWars/.idea/runConfigurations/GoldWars.xml new file mode 100644 index 0000000..447b8da --- /dev/null +++ b/GoldWars/.idea/runConfigurations/GoldWars.xml @@ -0,0 +1,11 @@ + + + + + + + + + + \ No newline at end of file diff --git a/GoldWars/.idea/vcs.xml b/GoldWars/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/GoldWars/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/GoldWars/.idea/xcode.xml b/GoldWars/.idea/xcode.xml new file mode 100644 index 0000000..62ab3fc --- /dev/null +++ b/GoldWars/.idea/xcode.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/GoldWars/GoldWars.xcodeproj/project.pbxproj b/GoldWars/GoldWars.xcodeproj/project.pbxproj index 9f1328a..58ae06f 100644 --- a/GoldWars/GoldWars.xcodeproj/project.pbxproj +++ b/GoldWars/GoldWars.xcodeproj/project.pbxproj @@ -20,6 +20,8 @@ 2086465C2461B66200817C23 /* TimerComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2086465B2461B66200817C23 /* TimerComponent.swift */; }; 3EBD242C245D8044003CECE7 /* GameCenterHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EBD242B245D8044003CECE7 /* GameCenterHelper.swift */; }; 3EBD242E245D9332003CECE7 /* Team.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EBD242D245D9332003CECE7 /* Team.swift */; }; + 3F745DF0246F48FC00CE7375 /* PlayerMoveType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F745DEF246F48FC00CE7375 /* PlayerMoveType.swift */; }; + 3FE19DB5246C7A22004827AB /* RoundCalculatorService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FE19DB4246C7A22004827AB /* RoundCalculatorService.swift */; }; 9E04AFAF245E2B73002D5CFC /* AttackActionComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E04AFAE245E2B73002D5CFC /* AttackActionComponent.swift */; }; 9E11FF79245CD81100EED3BE /* Fire.sks in Resources */ = {isa = PBXBuildFile; fileRef = 9E11FF77245CD81100EED3BE /* Fire.sks */; }; 9E174C82245DD81D00209FF0 /* ButtonNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E174C81245DD81D00209FF0 /* ButtonNode.swift */; }; @@ -38,6 +40,7 @@ 9EA3ABED245C8143006BC61D /* ModalBackgroundComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EA3ABEC245C8143006BC61D /* ModalBackgroundComponent.swift */; }; 9EA3ABEF245C834B006BC61D /* ModalContentComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EA3ABEE245C834B006BC61D /* ModalContentComponent.swift */; }; 9EBFD7552462CF5A00E1E219 /* SliderComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EBFD7542462CF5A00E1E219 /* SliderComponent.swift */; }; + 9EC239E1246878A900952F74 /* MultiplayerNetwork.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EC239E0246878A900952F74 /* MultiplayerNetwork.swift */; }; 9EC7E48B2461FBF700396BCD /* SliderNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EC7E48A2461FBF700396BCD /* SliderNode.swift */; }; 9EC86B9F245C88A300796EF3 /* Modal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EC86B9E245C88A300796EF3 /* Modal.swift */; }; 9EC86BA6245C8AD000796EF3 /* ModalType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EC86BA5245C8AD000796EF3 /* ModalType.swift */; }; @@ -56,6 +59,7 @@ C064E9A8246C0EA50022B228 /* LabelNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = C064E9A7246C0EA50022B228 /* LabelNode.swift */; }; C064E9AA246C114C0022B228 /* LabelComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C064E9A9246C114C0022B228 /* LabelComponent.swift */; }; C064E9AC246C151F0022B228 /* Label.swift in Sources */ = {isa = PBXBuildFile; fileRef = C064E9AB246C151F0022B228 /* Label.swift */; }; + C099579C246C5E5C0016AA22 /* DataService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C099579B246C5E5C0016AA22 /* DataService.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -86,6 +90,8 @@ 2086465B2461B66200817C23 /* TimerComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimerComponent.swift; sourceTree = ""; }; 3EBD242B245D8044003CECE7 /* GameCenterHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameCenterHelper.swift; sourceTree = ""; }; 3EBD242D245D9332003CECE7 /* Team.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Team.swift; sourceTree = ""; }; + 3F745DEF246F48FC00CE7375 /* PlayerMoveType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerMoveType.swift; sourceTree = ""; }; + 3FE19DB4246C7A22004827AB /* RoundCalculatorService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundCalculatorService.swift; sourceTree = ""; }; 9E04AFAE245E2B73002D5CFC /* AttackActionComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttackActionComponent.swift; sourceTree = ""; }; 9E11FF77245CD81100EED3BE /* Fire.sks */ = {isa = PBXFileReference; lastKnownFileType = file.sks; path = Fire.sks; sourceTree = ""; }; 9E174C81245DD81D00209FF0 /* ButtonNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ButtonNode.swift; sourceTree = ""; }; @@ -99,11 +105,12 @@ 9E78ACBD245CC9C000526FF7 /* AtkBoostSkillComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AtkBoostSkillComponent.swift; sourceTree = ""; }; 9E78ACC1245CC9EE00526FF7 /* DefBoostSkillComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefBoostSkillComponent.swift; sourceTree = ""; }; 9E78ACC3245CCA3600526FF7 /* SpySkillComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpySkillComponent.swift; sourceTree = ""; }; - 9EA3ABE8245C6DAA006BC61D /* DefaultBaseComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultBaseComponent.swift; sourceTree = ""; }; + 9EA3ABE8245C6DAA006BC61D /* DefaultBaseComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultBaseComponent.swift; sourceTree = ""; wrapsLines = 1; }; 9EA3ABEA245C6DFA006BC61D /* BaseNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseNode.swift; sourceTree = ""; }; 9EA3ABEC245C8143006BC61D /* ModalBackgroundComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalBackgroundComponent.swift; sourceTree = ""; }; 9EA3ABEE245C834B006BC61D /* ModalContentComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalContentComponent.swift; sourceTree = ""; }; 9EBFD7542462CF5A00E1E219 /* SliderComponent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SliderComponent.swift; sourceTree = ""; }; + 9EC239E0246878A900952F74 /* MultiplayerNetwork.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiplayerNetwork.swift; sourceTree = ""; }; 9EC7E48A2461FBF700396BCD /* SliderNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SliderNode.swift; sourceTree = ""; }; 9EC86B9E245C88A300796EF3 /* Modal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modal.swift; sourceTree = ""; }; 9EC86BA5245C8AD000796EF3 /* ModalType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalType.swift; sourceTree = ""; }; @@ -123,6 +130,7 @@ C064E9A7246C0EA50022B228 /* LabelNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelNode.swift; sourceTree = ""; }; C064E9A9246C114C0022B228 /* LabelComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelComponent.swift; sourceTree = ""; }; C064E9AB246C151F0022B228 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = ""; }; + C099579B246C5E5C0016AA22 /* DataService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataService.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -179,8 +187,11 @@ C05FAED52468559D0006AF2E /* SoundManager.swift */, 110360DA244B101A008610AF /* GameViewController.swift */, 110360DF244B101B008610AF /* Assets.xcassets */, + 9EC239E0246878A900952F74 /* MultiplayerNetwork.swift */, + C099579B246C5E5C0016AA22 /* DataService.swift */, 110360E4244B101B008610AF /* Info.plist */, AE151588245F18EF001D363E /* MatchmakingHelper.swift */, + 3FE19DB4246C7A22004827AB /* RoundCalculatorService.swift */, 3EBD242B245D8044003CECE7 /* GameCenterHelper.swift */, AEBF3B00246EB187004F7CD5 /* CancelBtnComponent.swift */, AEBF3AFE246EB146004F7CD5 /* CancelBtnNode.swift */, @@ -260,6 +271,7 @@ 11738A3A24508F68004426F1 /* Unit.swift */, 9EC86BA5245C8AD000796EF3 /* ModalType.swift */, 3EBD242D245D9332003CECE7 /* Team.swift */, + 3F745DEF246F48FC00CE7375 /* PlayerMoveType.swift */, ); path = Enums; sourceTree = ""; @@ -397,7 +409,9 @@ files = ( 9E78ACB8245CB75B00526FF7 /* TeamComponent.swift in Sources */, 9EA3ABEF245C834B006BC61D /* ModalContentComponent.swift in Sources */, + 3FE19DB5246C7A22004827AB /* RoundCalculatorService.swift in Sources */, 9EC86BA6245C8AD000796EF3 /* ModalType.swift in Sources */, + 9EC239E1246878A900952F74 /* MultiplayerNetwork.swift in Sources */, 9E78ACBE245CC9C000526FF7 /* AtkBoostSkillComponent.swift in Sources */, 9E78ACC4245CCA3600526FF7 /* SpySkillComponent.swift in Sources */, 9EA3ABEB245C6DFA006BC61D /* BaseNode.swift in Sources */, @@ -414,11 +428,13 @@ 9E174C86245DD91500209FF0 /* ButtonComponent.swift in Sources */, AE151589245F18EF001D363E /* MatchmakingHelper.swift in Sources */, 11036113244B3E30008610AF /* MenuScene.swift in Sources */, + C099579C246C5E5C0016AA22 /* DataService.swift in Sources */, 9EA3ABE9245C6DAA006BC61D /* DefaultBaseComponent.swift in Sources */, 9E174C8A245E1A0A00209FF0 /* Background.swift in Sources */, 9EA3ABED245C8143006BC61D /* ModalBackgroundComponent.swift in Sources */, C064E9A8246C0EA50022B228 /* LabelNode.swift in Sources */, 3EBD242C245D8044003CECE7 /* GameCenterHelper.swift in Sources */, + 3F745DF0246F48FC00CE7375 /* PlayerMoveType.swift in Sources */, AB1D759C245DD18100671525 /* MapProtocol.swift in Sources */, AEBF3B01246EB187004F7CD5 /* CancelBtnComponent.swift in Sources */, AB1D75A0245DEC0500671525 /* MapFactory.swift in Sources */, @@ -597,8 +613,8 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = GoldWars/GoldWars.entitlements; - CODE_SIGN_IDENTITY = "iPhone Developer"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_TEAM = DDKFQG46BQ; INFOPLIST_FILE = GoldWars/Info.plist; @@ -609,7 +625,7 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = de.hft.stuttgart.ip2.goldwars; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = "Developer Profile"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 2; }; @@ -620,8 +636,8 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = GoldWars/GoldWars.entitlements; - CODE_SIGN_IDENTITY = "iPhone Developer"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_TEAM = DDKFQG46BQ; INFOPLIST_FILE = GoldWars/Info.plist; diff --git a/GoldWars/GoldWars/Components/TeamComponent.swift b/GoldWars/GoldWars/Components/TeamComponent.swift index 2369f7b..db67deb 100644 --- a/GoldWars/GoldWars/Components/TeamComponent.swift +++ b/GoldWars/GoldWars/Components/TeamComponent.swift @@ -11,9 +11,9 @@ import GameplayKit import GameKit class TeamComponent: GKComponent { - let team: Team + var team: Team + var player: GKPlayer let fire: SKEmitterNode - let player: GKPlayer init(team: Team, player: GKPlayer, position: CGPoint) { fire = SKEmitterNode(fileNamed: "Fire")! diff --git a/GoldWars/GoldWars/Components/TimerComponent.swift b/GoldWars/GoldWars/Components/TimerComponent.swift index 816ca04..52c51b6 100644 --- a/GoldWars/GoldWars/Components/TimerComponent.swift +++ b/GoldWars/GoldWars/Components/TimerComponent.swift @@ -9,12 +9,12 @@ import GameplayKit class TimerComponent: GKComponent { - + let labelNode :SKLabelNode var endTime :Date! var duration :Double - - init(text: String, anchorPoint: CGPoint, duration: TimeInterval) { + + init(text: String, anchorPoint: CGPoint, duration: TimeInterval) { self.labelNode = SKLabelNode(text: text) self.labelNode.fontColor = UIColor.black self.labelNode.fontSize = CGFloat(45) @@ -26,6 +26,7 @@ class TimerComponent: GKComponent { func startWithDuration(duration: TimeInterval){ endTime = Date().addingTimeInterval(duration) + RoundCalculatorService.sharedInstance.isCalculating = false } func timeLeft() -> Int { @@ -45,12 +46,20 @@ class TimerComponent: GKComponent { if(isFinished()){ self.labelNode.text = "Synching" + if !MultiplayerNetwork.sharedInstance.isSending { + MultiplayerNetwork.sharedInstance.sendPlayerMoves(playerMoves: DataService.sharedInstance.localPlayerMoves) + } + if !RoundCalculatorService.sharedInstance.isCalculating + && DataService.sharedInstance.didReceiveAllData() + && MatchmakingHelper.sharedInstance.isServer { + RoundCalculatorService.sharedInstance.calculateRound() + } } } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + } diff --git a/GoldWars/GoldWars/DataService.swift b/GoldWars/GoldWars/DataService.swift new file mode 100644 index 0000000..e06dff6 --- /dev/null +++ b/GoldWars/GoldWars/DataService.swift @@ -0,0 +1,72 @@ +// +// PlayerMovesService.swift +// GoldWars +// +// Created by Tim Herbst on 13.05.20. +// Copyright © 2020 SP2. All rights reserved. +// + +struct PlayerMove: Codable{ + let fromBase: Int + let toBase: Int + var unitCount: Int +} + +struct Host: Codable { + let playerName: String +} + +class SnapshotModel: Codable { + var baseEntites: [BaseEntityModel] + + init(baseEntites: [BaseEntityModel]) { + self.baseEntites = baseEntites + } +} + +class BaseEntityModel: Codable { + let baseId: Int + var unitCount: Int + var ownership: String? + + init(baseId: Int, unitCount: Int, ownership: String?) { + self.baseId = baseId + self.unitCount = unitCount + self.ownership = ownership + } +} + +class DataService { + static let sharedInstance = DataService() + var localPlayerMoves: [PlayerMove] = [] + var remotePlayerMoves: [String: [PlayerMove]] = [:] + var snapshotModel: SnapshotModel? + var gameHost: Host? + + func addMove(playerMove: PlayerMove) { + var equalMove = localPlayerMoves.filter { (ele) -> Bool in + ele.fromBase == playerMove.fromBase && ele.toBase == playerMove.toBase + } + if equalMove.count == 1 { + equalMove[0].unitCount = Int(equalMove[0].unitCount) + Int(playerMove.unitCount) + } else { + self.localPlayerMoves.append(playerMove) + } + } + + func addRemotePlayerMoves(playerName: String, playerMoves: [PlayerMove]) { + self.remotePlayerMoves[playerName] = playerMoves + } + + func didReceiveAllData() -> Bool { + return remotePlayerMoves.count == MatchmakingHelper.sharedInstance.mpMatch?.players.count + } + + func setGameHost(host: Host) { + self.gameHost = host + } + + func setSnapshotModel(snapshotModel: SnapshotModel) { + self.snapshotModel = snapshotModel + } +} diff --git a/GoldWars/GoldWars/Entities/Base.swift b/GoldWars/GoldWars/Entities/Base.swift index 6cb0158..ad05629 100644 --- a/GoldWars/GoldWars/Entities/Base.swift +++ b/GoldWars/GoldWars/Entities/Base.swift @@ -10,18 +10,21 @@ import SpriteKit import GameplayKit import GameKit -class Base: GKEntity { - +class Base: GKEntity{ + static var BASE_ID_COUNT: Int = 0 var unitCount: Int var adjacencyList: Array var changeOwnership: Bool var ownershipPlayer: GKPlayer? + var baseID: Int init(position: CGPoint, player: GKPlayer! = nil, team: Team! = nil) { self.unitCount = 0 self.adjacencyList = [Base]() self.changeOwnership = false self.ownershipPlayer = player + self.baseID = Base.BASE_ID_COUNT + Base.BASE_ID_COUNT += 1 super.init() addComponent(DefaultBaseComponent(texture: SKTexture(imageNamed: "Base"), position: position)) @@ -31,11 +34,14 @@ class Base: GKEntity { } } - func attackBase(base: Base, units:Int) -> [GKEntity]{ + func doPlayerMoveTypeToBase(base: Base, playerMoveType: PlayerMoveType, units: Int) -> [GKEntity]{ base.changeOwnership = true base.ownershipPlayer = self.ownershipPlayer self.unitCount -= units base.unitCount += units + DataService.sharedInstance.addMove(playerMove: PlayerMove(fromBase: self.baseID, + toBase: base.baseID, + unitCount: units * playerMoveType.rawValue)) return [self, base] } diff --git a/GoldWars/GoldWars/Entities/EntityManager.swift b/GoldWars/GoldWars/Entities/EntityManager.swift index b7d5de6..bc1964b 100644 --- a/GoldWars/GoldWars/Entities/EntityManager.swift +++ b/GoldWars/GoldWars/Entities/EntityManager.swift @@ -117,17 +117,55 @@ class EntityManager { let base = (entity as! Base) if base.changeOwnership { + //FIX-ME: Find a way to update the Component without deleting it upfront + //TODO: outsource component handling to a generic function + //base.removeComponent(ofType: TeamComponent.self) base.addComponent(TeamComponent( team: (entities[0] as! Base).component(ofType: TeamComponent.self)!.team, player: (entities[0] as! Base).component(ofType: TeamComponent.self)!.player, position: (base.component(ofType: DefaultBaseComponent.self)?.spriteNode.position)! ) ) + if let fire = entity.component(ofType: TeamComponent.self)?.fire{ + scene.addChild(fire) + } base.changeOwnership = false - scene.addChild(base.component(ofType: TeamComponent.self)!.fire) } } - + } + + func updateSnapshotModel(snapshotModel: SnapshotModel) { + let bases = entities.filter{$0 is Base} + for entity in bases{ + let base = entity as! Base + let snapBase = self.getSnapshotBaseById(baseId: base.baseID, snapshotModel: snapshotModel) + var getOwnerBySnapBase: GKPlayer? = nil + base.unitCount = snapBase.unitCount + + if snapBase.ownership != nil { + getOwnerBySnapBase = MatchmakingHelper.sharedInstance.getGKPlayerByUsername(displayName: snapBase.ownership!) + } + if getOwnerBySnapBase != nil { + base.changeOwnership = true + base.ownershipPlayer = getOwnerBySnapBase + //FIX-ME: Find a way to update the Component without deleting it upfront + //TODO: outsource component handling to a generic function + //entity.removeComponent(ofType: TeamComponent.self) + entity.addComponent(TeamComponent( + team: getTeamByPlayer(playerName: snapBase.ownership!), + player: getOwnerBySnapBase!, + position: (entity.component(ofType: DefaultBaseComponent.self)?.spriteNode.position)! + ) + ) + if let fire = entity.component(ofType: TeamComponent.self)?.fire{ + scene.addChild(fire) + } + } + } + } + + func getSnapshotBaseById(baseId: Int, snapshotModel: SnapshotModel) -> BaseEntityModel{ + return snapshotModel.baseEntites.filter { $0.baseId == baseId }[0] } func getBaseByPlayer(for player: GKPlayer) -> GKEntity? { @@ -175,6 +213,19 @@ class EntityManager { return nil } + func getTeamByPlayer(playerName: String) -> Team { + return entities.filter { $0 is Base && ($0 as! Base).component(ofType: TeamComponent.self)?.player.displayName == playerName }[0].component(ofType: TeamComponent.self)!.team + } + + func getBasebyID(id: Int) -> Base?{ + for entity in entities { + if entity is Base && (entity as! Base).baseID == id { + return (entity as! Base) + } + } + return nil + } + func getBackground() -> GKEntity? { return entities.filter{$0 is Background}[0] } @@ -190,4 +241,18 @@ class EntityManager { func getHUD() -> GKEntity? { return entities.filter{$0 is HUD}[0] } + + + + func getSnapshotModel() -> SnapshotModel { + let bases = entities.filter{$0 is Base} + var snapBase: [BaseEntityModel] = [] + + for entity in bases { + let base = entity as! Base + snapBase.append(BaseEntityModel(baseId: base.baseID, unitCount: base.unitCount, ownership: base.ownershipPlayer?.displayName)) + } + + return SnapshotModel(baseEntites: snapBase) + } } diff --git a/GoldWars/GoldWars/Entities/Modal.swift b/GoldWars/GoldWars/Entities/Modal.swift index aadd374..c01999e 100644 --- a/GoldWars/GoldWars/Entities/Modal.swift +++ b/GoldWars/GoldWars/Entities/Modal.swift @@ -57,7 +57,7 @@ class Modal: GKEntity{ func sendUnits(currentDraggedBase: Base?, touchLocation: CGPoint, gameScene: GameScene, collisionBase: Base?){ for base in currentDraggedBase!.adjacencyList { if base == collisionBase { - EntityManager.sharedInstance.update((currentDraggedBase?.attackBase(base: base, units: Int(GameScene.sendUnits)))!) + EntityManager.sharedInstance.update((currentDraggedBase?.doPlayerMoveTypeToBase(base: base, playerMoveType: PlayerMoveType.AtkMove, units: Int(GameScene.sendUnits)))!) } } } diff --git a/GoldWars/GoldWars/Enums/PlayerMoveType.swift b/GoldWars/GoldWars/Enums/PlayerMoveType.swift new file mode 100644 index 0000000..2f1bdd6 --- /dev/null +++ b/GoldWars/GoldWars/Enums/PlayerMoveType.swift @@ -0,0 +1,13 @@ +// +// PlayerMoveType.swift +// GoldWars +// +// Created by Aldin Duraki on 16.05.20. +// Copyright © 2020 SP2. All rights reserved. +// + +enum PlayerMoveType: Int, Codable{ + case AtkMove = 1 + case TxnMove = -1 +} + diff --git a/GoldWars/GoldWars/MatchmakingHelper.swift b/GoldWars/GoldWars/MatchmakingHelper.swift index a99bb31..a695d39 100644 --- a/GoldWars/GoldWars/MatchmakingHelper.swift +++ b/GoldWars/GoldWars/MatchmakingHelper.swift @@ -33,6 +33,8 @@ class MatchmakingHelper: NSObject, GKMatchmakerViewControllerDelegate, GKMatchDe var viewController: UIViewController? var mpMatchStarted: Bool var isServer: Bool + var spieler1: GKPlayer? + var nameSpieler1 = "" var menusc: MenuScene? let localPlayer: GKLocalPlayer = GKLocalPlayer.local @@ -100,12 +102,27 @@ class MatchmakingHelper: NSObject, GKMatchmakerViewControllerDelegate, GKMatchDe } /* - Vom match erhaltene Spielerdaten - */ - private func match(match: GKMatch!, didReceiveData data: NSData!,fromPlayer playerID: String!) { + Vom match erhaltene Spielerdaten + */ + func match(_ match: GKMatch, didReceive data: Data, fromRemotePlayer player: GKPlayer) { if mpMatch != match { return } - delegate?.matchReceivedData(match: match, data: data, fromPlayer: playerID) + + let jsonDecoder = JSONDecoder() + if let playerMoves = try? jsonDecoder.decode([PlayerMove].self, from: data) { + DataService.sharedInstance.addRemotePlayerMoves(playerName: player.displayName, playerMoves: playerMoves) + } + + if let message = try? jsonDecoder.decode(Host.self, from: data) { + DataService.sharedInstance.gameHost = message + } + + if let snapshotModel = try? jsonDecoder.decode(SnapshotModel.self, from: data) { + DataService.sharedInstance.snapshotModel = snapshotModel + EntityManager.sharedInstance.updateSnapshotModel(snapshotModel: snapshotModel) + } + + MultiplayerNetwork.sharedInstance.isSending = false } /* @@ -152,15 +169,33 @@ class MatchmakingHelper: NSObject, GKMatchmakerViewControllerDelegate, GKMatchDe if player == GKLocalPlayer.local { self.isServer = true + self.spieler1 = player + self.nameSpieler1 = self.spieler1!.displayName + DataService.sharedInstance.setGameHost(host: Host(playerName: player!.displayName)) } else { self.isServer = false } self.delegate?.matchStarted() self.menusc!.loadScene(scene: GameScene(size: self.menusc!.size)) + MultiplayerNetwork.sharedInstance.sendHostIdentifier() }) } + func getGKPlayerByUsername(displayName: String) -> GKPlayer? { + let nilGK : GKPlayer? = nil + + if GKLocalPlayer.local.displayName == displayName { + return GKLocalPlayer.local + } + + for player in mpMatch!.players { + if player.displayName == displayName { + return player + } + } + return nilGK + } /* Trennt die Verbindung vom Match diff --git a/GoldWars/GoldWars/MultiplayerNetwork.swift b/GoldWars/GoldWars/MultiplayerNetwork.swift new file mode 100644 index 0000000..7ffbc37 --- /dev/null +++ b/GoldWars/GoldWars/MultiplayerNetwork.swift @@ -0,0 +1,64 @@ +// +// MultiplayerNetwork.swift +// GoldWars +// +// Created by Niko Jochim on 10.05.20. +// Copyright © 2020 SP2. All rights reserved. +// + +import GameplayKit +import Foundation +import GameKit + +class MultiplayerNetwork{ + static let sharedInstance = MultiplayerNetwork() + + var isSending = false + + func sendData(data: Data) { + let mmHelper = MatchmakingHelper.sharedInstance + if let multiplayerMatch = mmHelper.mpMatch { + do { + try multiplayerMatch.sendData(toAllPlayers: data, with: .reliable) + } catch { + //TODO: Add logging + } + } + } + + func sendDataToHost(data: Data) { + let mmHelper = MatchmakingHelper.sharedInstance + let hostGKPlayer = MatchmakingHelper.sharedInstance.mpMatch?.players.filter{ $0.displayName == DataService.sharedInstance.gameHost!.playerName}[0] + + if let multiplayerMatch = mmHelper.mpMatch{ + do { + try multiplayerMatch.send(data, to: [hostGKPlayer!], dataMode: .reliable) + } catch { + //TODO: Add logging + } + } + } + + func sendPlayerMoves(playerMoves: [PlayerMove]) { + if MatchmakingHelper.sharedInstance.isServer == false { + self.isSending = true + let encoder = JSONEncoder() + let encoded = (try? encoder.encode(playerMoves))! + sendDataToHost(data: encoded) + } + DataService.sharedInstance.localPlayerMoves.removeAll() + } + + func sendHostIdentifier() { + let encoder = JSONEncoder() + let encoded = (try? encoder.encode(DataService.sharedInstance.gameHost))! + sendData(data: encoded) + } + + func sendSnapshotModelToPlayers() { + let encoder = JSONEncoder() + let encoded = (try? encoder.encode(DataService.sharedInstance.snapshotModel))! + sendData(data: encoded) + } + +} diff --git a/GoldWars/GoldWars/PlayerMovesService.swift b/GoldWars/GoldWars/PlayerMovesService.swift new file mode 100644 index 0000000..3c0f41d --- /dev/null +++ b/GoldWars/GoldWars/PlayerMovesService.swift @@ -0,0 +1,32 @@ +// +// PlayerMovesService.swift +// GoldWars +// +// Created by Tim Herbst on 13.05.20. +// Copyright © 2020 SP2. All rights reserved. +// + +struct PlayerMove : Codable{ + let fromBase: Int + let toBase: Int + let unitCount: Int +} + +struct Host: Codable { + let playerName: String +} + +class DataService { + static let sharedInstance = DataService() + var playerMoves: [PlayerMove] = [] + var gameHost: Host? + + + func addMove(playerMove: PlayerMove) { + self.playerMoves.append(playerMove) + } + + func setGameHost(host: Host) { + self.gameHost = host + } +} diff --git a/GoldWars/GoldWars/RoundCalculatorService.swift b/GoldWars/GoldWars/RoundCalculatorService.swift new file mode 100644 index 0000000..3704ec8 --- /dev/null +++ b/GoldWars/GoldWars/RoundCalculatorService.swift @@ -0,0 +1,124 @@ +// +// RoundCalculatorService.swift +// GoldWars +// +// Created by Aldin Duraki on 13.05.20. +// Copyright © 2020 SP2. All rights reserved. +// + +import GameKit +import os + +class RoundCalculatorService { + static let sharedInstance = RoundCalculatorService() + static let LOG = OSLog.init(subsystem: "Round Calculator", category: "RoundCalculatorService") + + var allPlayerMoves: [String: [PlayerMove]] = [:] + var baseSpecificMoves: [Int: [String: [PlayerMove]]] = [:] + + var isCalculating = false + + func calculateRound() { + os_log("Started calculating Round", log: RoundCalculatorService.LOG, type: .info) + isCalculating = true + let currentSnapshotModel = DataService.sharedInstance.snapshotModel + + for playerMove in DataService.sharedInstance.remotePlayerMoves { + addPlayerMove(playerName: playerMove.key, playerMoves: playerMove.value) + } + addPlayerMove(playerName: GKLocalPlayer.local.displayName, playerMoves: DataService.sharedInstance.localPlayerMoves) + + for playerMove in allPlayerMoves { + for move in playerMove.value { + mapPlayerMoveToAttackedBase(playerName: playerMove.key, playerMove: move) + } + } + + // TODO: Refactor to a less complex way + for (key, value) in baseSpecificMoves { + let baseId = key + var playerMovesByBase = value + let targetBase = currentSnapshotModel?.baseEntites.filter { $0.baseId == baseId }[0] + let possiblyOwnershipMoves = playerMovesByBase.filter { $0.key == targetBase?.ownership} + + for (playerName, playerMoves) in possiblyOwnershipMoves { + for playerMove in playerMoves { + for base in currentSnapshotModel!.baseEntites { + if base.baseId == playerMove.fromBase { + base.unitCount -= playerMove.unitCount + } + if base.baseId == playerMove.toBase { + base.unitCount += playerMove.unitCount + } + } + } + playerMovesByBase.removeValue(forKey: playerName) + } + + for (_, playerMoves) in playerMovesByBase { + for playerMove in playerMoves { + for base in currentSnapshotModel!.baseEntites { + if base.baseId == playerMove.fromBase { + base.unitCount -= playerMove.unitCount + } + } + } + } + + var combinePotentionalForces: [String: PlayerMove] = [:] + + for playerMoves in playerMovesByBase { + 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 + } + } + if(combinePotentionalForces.count > 0) { + 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 { + if base.baseId == playerMoveWithMaxUnits.value.toBase { + base.unitCount += playerMoveWithMaxUnits.value.unitCount + if playerMoveWithMaxUnits.value.unitCount == 0 { + base.ownership = nil + } else { + base.ownership = playerMoveWithMaxUnits.key + } + } + } + } + baseSpecificMoves.removeValue(forKey: baseId) + } + allPlayerMoves.removeAll() + MultiplayerNetwork.sharedInstance.sendSnapshotModelToPlayers() + EntityManager.sharedInstance.updateSnapshotModel(snapshotModel: currentSnapshotModel!) + os_log("Finished calculating Round", log: RoundCalculatorService.LOG, type: .info) + } + + func addPlayerMove(playerName: String, playerMoves: [PlayerMove]) { + self.allPlayerMoves[playerName] = playerMoves + } + + func mapPlayerMoveToAttackedBase(playerName: String, playerMove: PlayerMove) { + if self.baseSpecificMoves.keys.contains(playerMove.toBase) { + if (self.baseSpecificMoves[playerMove.toBase]?.keys.contains(playerName))!{ + self.baseSpecificMoves[playerMove.toBase]?[playerName]?.append(playerMove) + } else { + self.baseSpecificMoves[playerMove.toBase]?.merge([playerName: [playerMove]]){(current, _) in current} + } + } else { + self.baseSpecificMoves[playerMove.toBase] = [playerName: [playerMove]] + } + } + + func resolvePlayerMove(playerMove: PlayerMove, unitCount: Int, ownership: String?, resolveType: String) { + //TODO: outsource playermoves + } + +} diff --git a/GoldWars/GoldWars/Scenes/GameScene.swift b/GoldWars/GoldWars/Scenes/GameScene.swift index fb229f8..7b91850 100644 --- a/GoldWars/GoldWars/Scenes/GameScene.swift +++ b/GoldWars/GoldWars/Scenes/GameScene.swift @@ -27,6 +27,7 @@ class GameScene: SKScene{ func initMap() { MapFactory(scene: self, entityManager: EntityManager.sharedInstance).loadMap(playerCount: 2) + DataService.sharedInstance.setSnapshotModel(snapshotModel: EntityManager.sharedInstance.getSnapshotModel()) } override func touchesEnded(_ touches: Set, with event: UIEvent?) { diff --git a/GoldWars/GoldWars/SoundManager.swift b/GoldWars/GoldWars/SoundManager.swift index 4f8f34d..b6cb5d4 100644 --- a/GoldWars/GoldWars/SoundManager.swift +++ b/GoldWars/GoldWars/SoundManager.swift @@ -23,7 +23,7 @@ class SoundManager { do { audioPlayer = try AVAudioPlayer(contentsOf: backgroundMainMenuAudio!) } catch { - print("Datei nicht gefunden!") + //TODO: Add logging } audioPlayer.numberOfLoops = -1 audioPlayer.prepareToPlay() diff --git a/GoldWars/GoldWars/Storyboards/RoundSimulatorService.swift b/GoldWars/GoldWars/Storyboards/RoundSimulatorService.swift new file mode 100644 index 0000000..4847831 --- /dev/null +++ b/GoldWars/GoldWars/Storyboards/RoundSimulatorService.swift @@ -0,0 +1,13 @@ +// +// RoundSimulatorService.swift +// GoldWars +// +// Created by student on 13.05.20. +// Copyright © 2020 SP2. All rights reserved. +// + +import Foundation + +class RoundSimulatorService { + +}