diff --git a/src/client/haxe/ru/m/tankz/control/ClientNetworkControl.hx b/src/client/haxe/ru/m/tankz/control/ClientNetworkControl.hx new file mode 100644 index 0000000..02607fe --- /dev/null +++ b/src/client/haxe/ru/m/tankz/control/ClientNetworkControl.hx @@ -0,0 +1,19 @@ +package ru.m.tankz.control; + +import haxework.provider.Provider; +import ru.m.tankz.network.NetworkManager; +import ru.m.tankz.control.Control; + + +class ClientNetworkControl extends HumanControl { + + private var network(get, never):NetworkManager; + + inline private function get_network():NetworkManager { + return Provider.get(NetworkManager); + } + + override public function action(action:TankAction):Void { + network.action(action); + } +} diff --git a/src/client/haxe/ru/m/tankz/frame/GameFrame.hx b/src/client/haxe/ru/m/tankz/frame/GameFrame.hx index 6658653..760728d 100755 --- a/src/client/haxe/ru/m/tankz/frame/GameFrame.hx +++ b/src/client/haxe/ru/m/tankz/frame/GameFrame.hx @@ -42,11 +42,10 @@ class GameFrame extends VGroupView implements ViewBuilder implements GameFrameLa if (game == null) { throw 'Unsupported game type "${save.state.type}"'; } - game.engine.listeners.push(render); - game.engine.listeners.push(Provider.get(SoundManager)); + game.engine.connect(render); + game.engine.connect(Provider.get(SoundManager)); game.start(save).then(onGameStateChange).endThen(onGameComplete); content.addEventListener(Event.ENTER_FRAME, redraw); - //Provider.get(IConnection).packetHandler.addListener(this); render.draw(game.engine); timer = new Timer(10); timer.run = updateEngine; @@ -55,7 +54,6 @@ class GameFrame extends VGroupView implements ViewBuilder implements GameFrameLa } private function stop():Void { - //Provider.get(IConnection).packetHandler.removeListener(this); if (timer != null) { timer.stop(); timer = null; diff --git a/src/client/haxe/ru/m/tankz/network/NetworkManager.hx b/src/client/haxe/ru/m/tankz/network/NetworkManager.hx index 6ade3ed..5e270be 100644 --- a/src/client/haxe/ru/m/tankz/network/NetworkManager.hx +++ b/src/client/haxe/ru/m/tankz/network/NetworkManager.hx @@ -1,5 +1,9 @@ package ru.m.tankz.network; +import ru.m.tankz.proto.game.GameChange; +import ru.m.tankz.proto.game.GameActionType; +import ru.m.tankz.proto.pack.GameUpdateRequest; +import ru.m.tankz.control.Control; import ru.m.tankz.proto.pack.JoinGameRequest; import ru.m.tankz.proto.pack.LeaveGameRequest; import ru.m.tankz.proto.pack.CreateGameRequest; @@ -22,6 +26,7 @@ class NetworkManager { public var stateSignal:Signal; public var listGameSignal:Signal>; public var gameSignal:Signal; + public var gameUpdateSignal:Signal>; public var user(default, null):User; private var connection(get, never):ClientConnection; @@ -39,6 +44,7 @@ class NetworkManager { stateSignal = new Signal(); listGameSignal = new Signal>(); gameSignal = new Signal(); + gameUpdateSignal = new Signal>(); updateState('offline'); connection.handler.connect(onConnectionEvent); connection.receiveHandler.connect(onResponse); @@ -82,6 +88,18 @@ class NetworkManager { connection.send(new Request().setLeaveGame(new LeaveGameRequest())); } + public function action(action:TankAction):Void { + var update:GameUpdateRequest = switch action { + case TankAction.MOVE(direction): new GameUpdateRequest().setType(GameActionType.MOVE).setDirectionX(direction.x).setDirectionY(direction.y); + case TankAction.STOP: new GameUpdateRequest().setType(GameActionType.STOP); + case TankAction.SHOT: new GameUpdateRequest().setType(GameActionType.SHOT); + case _: null; + } + if (update != null) { + connection.send(new Request().setUpdateGame(update)); + } + } + private function onConnectionEvent(event:ConnectionEvent):Void { L.d('Network', '${event}'); updateState(switch (event) { @@ -110,6 +128,8 @@ class NetworkManager { gameSignal.emit(packet.joinGame.game); } else if (packet.hasLeaveGame()) { gameSignal.emit(null); + }else if (packet.hasUpdateGame()) { + gameUpdateSignal.emit(packet.updateGame.changes); } } } diff --git a/src/client/haxe/ru/m/tankz/render/Render.hx b/src/client/haxe/ru/m/tankz/render/Render.hx index bf46c50..fb37eed 100755 --- a/src/client/haxe/ru/m/tankz/render/Render.hx +++ b/src/client/haxe/ru/m/tankz/render/Render.hx @@ -12,7 +12,7 @@ import ru.m.tankz.engine.Engine; import ru.m.tankz.render.RenderItem; -class Render extends SpriteView implements EngineListener { +class Render extends SpriteView { private var backgroundLayer:Sprite; private var groundLayer:Sprite; @@ -126,7 +126,7 @@ class Render extends SpriteView implements EngineListener { } } - public function onChange(entity:EntityType, ?change:EntityChange):Void {} + public function onChange(entity:EntityType, change:EntityChange):Void {} public function onCollision(entity:EntityType, with:EntityType):Void { switch [entity, with] { diff --git a/src/client/haxe/ru/m/tankz/sound/SoundManager.hx b/src/client/haxe/ru/m/tankz/sound/SoundManager.hx index b825956..f975994 100644 --- a/src/client/haxe/ru/m/tankz/sound/SoundManager.hx +++ b/src/client/haxe/ru/m/tankz/sound/SoundManager.hx @@ -1,12 +1,12 @@ package ru.m.tankz.sound; -import ru.m.tankz.core.EntityType; import ru.m.tankz.engine.Engine; import openfl.media.Sound; import openfl.utils.Assets; +import ru.m.tankz.core.EntityType; -class SoundManager implements EngineListener { +class SoundManager { private static var TAG(default, never):String = 'SoundManager'; #if flash @@ -37,12 +37,12 @@ class SoundManager implements EngineListener { } } - public function onChange(entity:EntityType, ?change:EntityChange):Void { + public function onChange(entity:EntityType, change:EntityChange):Void { switch [entity, change] { case [EntityType.TANK(_), EntityChange.HIT]: play('bullet_hit'); - case [EntityType.TANK(_), EntityChange.LIVE_UP]: - play('live'); + //case [EntityType.TANK(_), EntityChange.LIVE_UP]: + // play('live'); case [EntityType.EAGLE(_), EntityChange.DEATH]: play('boom_player'); case _: diff --git a/src/common/haxe/ru/m/signal/Signal.hx b/src/common/haxe/ru/m/signal/Signal.hx index a60692a..31a0cbe 100644 --- a/src/common/haxe/ru/m/signal/Signal.hx +++ b/src/common/haxe/ru/m/signal/Signal.hx @@ -1,27 +1,53 @@ package ru.m.signal; +typedef Signal = Signal1; -typedef Receiver = T -> Void; +typedef Receiver1 = A -> Void; -class Signal { +class Signal1 { - private var receivers:Array>; + private var receivers:Array>; public function new() { receivers = []; } - public function connect(receiver:Receiver):Void { + public function connect(receiver:Receiver1):Void { receivers.push(receiver); } - public function disconnect(receiver:Receiver):Void { + public function disconnect(receiver:Receiver1):Void { receivers.remove(receiver); } - public function emit(value:T):Void { + public function emit(a:A):Void { for (receiver in receivers) { - receiver(value); + receiver(a); + } + } +} + +typedef Receiver2 = A -> B -> Void; + +class Signal2 { + + private var receivers:Array>; + + public function new() { + receivers = []; + } + + public function connect(receiver:Receiver2):Void { + receivers.push(receiver); + } + + public function disconnect(receiver:Receiver2):Void { + receivers.remove(receiver); + } + + public function emit(a:A, b:B):Void { + for (receiver in receivers) { + receiver(a, b); } } } diff --git a/src/common/haxe/ru/m/tankz/control/Control.hx b/src/common/haxe/ru/m/tankz/control/Control.hx index 1470f91..c9bb803 100644 --- a/src/common/haxe/ru/m/tankz/control/Control.hx +++ b/src/common/haxe/ru/m/tankz/control/Control.hx @@ -13,7 +13,6 @@ enum TankAction { SHOT; } - class Control { public static var NONE(default, never):ControlType = 'none'; public static var HUMAN(default, never):ControlType = 'human'; diff --git a/src/common/haxe/ru/m/tankz/engine/Engine.hx b/src/common/haxe/ru/m/tankz/engine/Engine.hx index c8fe5e3..efe62a1 100755 --- a/src/common/haxe/ru/m/tankz/engine/Engine.hx +++ b/src/common/haxe/ru/m/tankz/engine/Engine.hx @@ -1,5 +1,6 @@ package ru.m.tankz.engine; +import ru.m.signal.Signal; import ru.m.geom.Line; import ru.m.geom.Point; import ru.m.tankz.config.Config; @@ -13,32 +14,29 @@ import ru.m.tankz.map.LevelMap; enum EntityChange { - DEATH; HIT; - LIVE_UP; TYPE; + DEATH; + PROTECT; + FREEZING; } - -interface EngineListener { +typedef EngineListener = { public function onSpawn(entity:EntityType):Void; - public function onChange(entity:EntityType, ?change:EntityChange):Void; public function onCollision(entity:EntityType, with:EntityType):Void; public function onDestroy(entity:EntityType):Void; + public function onChange(entity:EntityType, change:EntityChange):Void; } -class CollisionProcessor implements EngineListener { +class CollisionProcessor { private var engine:Engine; public function new(engine:Engine) { this.engine = engine; + engine.collisionSignal.connect(onCollision); } - public function onSpawn(entity:EntityType):Void {} - - public function onChange(entity:EntityType, ?change:EntityChange):Void {} - public function onCollision(entity:EntityType, with:EntityType):Void { switch [entity, with] { case [EntityType.TANK(tank), EntityType.TANK(other_tank)]: @@ -74,47 +72,75 @@ class CollisionProcessor implements EngineListener { engine.destroy(bullet); if (!eagle.protect.active) { eagle.death = true; + // ToDo: change engine.change(eagle, EntityChange.DEATH); } case _: } } - public function onDestroy(entity:EntityType):Void { } + public function dispose():Void { + if (engine != null) { + engine.collisionSignal.disconnect(onCollision); + engine = null; + } + } +} + + +class EngineDispatcher { + public var spawnSignal(default, null):Signal1; + public var collisionSignal(default, null):Signal2; + public var destroySignal(default, null):Signal1; + public var changeSignal(default, null):Signal2; + + public function new() { + spawnSignal = new Signal1(); + collisionSignal = new Signal2(); + destroySignal = new Signal1(); + changeSignal = new Signal2(); + } + + public function connect(listener:EngineListener) { + spawnSignal.connect(listener.onSpawn); + collisionSignal.connect(listener.onCollision); + destroySignal.connect(listener.onDestroy); + changeSignal.connect(listener.onChange); + } + + public function disconnect(listener:EngineListener) { + spawnSignal.disconnect(listener.onSpawn); + collisionSignal.disconnect(listener.onCollision); + destroySignal.disconnect(listener.onDestroy); + changeSignal.disconnect(listener.onChange); + } } @:yield -class Engine implements ControlHandler { +class Engine extends EngineDispatcher implements ControlHandler { public var config(default, default):Config; public var map(default, null):LevelMap; public var entities(default, null):Map; - public var listeners(default, null):Array; private var collision:CollisionProcessor; private var time:Float; public function new(config:Config) { + super(); this.config = config; - listeners = []; map = new LevelMap(config.map); entities = new Map(); time = Date.now().getTime(); collision = new CollisionProcessor(this); - listeners.push(collision); } public function spawn(entity:Entity):Void { entities.set(entity.id, entity); var type = EntityTypeResolver.of(entity); - for (l in listeners) l.onSpawn(type); - } - - public function change(entity:Entity, ?change:EntityChange):Void { - var type = EntityTypeResolver.of(entity); - for (l in listeners) l.onChange(type, change); + spawnSignal.emit(type); } public function destroy(entity:Entity):Void { @@ -126,7 +152,7 @@ class Engine implements ControlHandler { if (tank != null) tank.onDestroyBullet(); case _: } - for (l in listeners) l.onDestroy(type); + destroySignal.emit(type); entities.remove(entity.id); } } @@ -152,6 +178,11 @@ class Engine implements ControlHandler { } } + public function change(entity:Entity, change:EntityChange):Void { + var type = EntityTypeResolver.of(entity); + changeSignal.emit(type, change); + } + public function update():Void { var newTime:Float = Date.now().getTime(); var d:Float = newTime - time; @@ -197,7 +228,7 @@ class Engine implements ControlHandler { entity.rect.lean(cell.rect); collision = true; var with = EntityTypeResolver.of(cell); - for (l in listeners) l.onCollision(entityType, with); + collisionSignal.emit(entityType, with); isStop = true; break; } @@ -207,7 +238,7 @@ class Engine implements ControlHandler { if (other != ent && other != null) { if (other.rect.intersection2(side)) { var with = EntityTypeResolver.of(other); - for (l in listeners) l.onCollision(entityType, with); + collisionSignal.emit(entityType, with); isStop = true; } } @@ -241,7 +272,10 @@ class Engine implements ControlHandler { } public function dispose():Void { - listeners = []; + if (collision != null) { + collision.dispose(); + collision = null; + } entities = new Map(); } diff --git a/src/common/haxe/ru/m/tankz/game/Game.hx b/src/common/haxe/ru/m/tankz/game/Game.hx index aed43f7..50067d4 100644 --- a/src/common/haxe/ru/m/tankz/game/Game.hx +++ b/src/common/haxe/ru/m/tankz/game/Game.hx @@ -24,7 +24,7 @@ import ru.m.tankz.game.Spawner; import ru.m.tankz.Type; -class Game implements EngineListener { +class Game { private static var TAG(default, never):String = 'Game'; @@ -43,7 +43,7 @@ class Game implements EngineListener { this.type = type; this.config = Provider.get(IConfigBundle).get(type); this.engine = new Engine(config); - engine.listeners.push(this); + engine.connect(this); } public function getTeam(teamId:TeamId):Team { @@ -160,7 +160,7 @@ class Game implements EngineListener { } } - public function onChange(entity:EntityType, ?change:EntityChange):Void { + public function onChange(entity:EntityType, change:EntityChange):Void { switch [entity, change] { case [EntityType.EAGLE(eagle), EntityChange.DEATH]: if (eagle.death) { @@ -264,11 +264,11 @@ class Game implements EngineListener { } case 'helmet': tank.protect.on(bonus.config.duration); - engine.change(tank); + engine.change(tank, EntityChange.PROTECT); case 'clock': for (t in engine.iterTanks(alienTank(tank.playerId.team))) { t.freezing.on(bonus.config.duration); - engine.change(t); + engine.change(t, EntityChange.FREEZING); } case 'shovel': // ToDo: protect eagle/area @@ -276,7 +276,7 @@ class Game implements EngineListener { if (team.eagleId > 0) { var eagle:Eagle = cast(engine.entities[team.eagleId], Eagle); eagle.protect.on(bonus.config.duration); - engine.change(eagle); + engine.change(eagle, EntityChange.PROTECT); } case _: engine.destroy(tank); // :-D diff --git a/src/common/proto/pack.proto b/src/common/proto/pack.proto index 65a9be4..ae55cb7 100644 --- a/src/common/proto/pack.proto +++ b/src/common/proto/pack.proto @@ -100,6 +100,6 @@ message Response { JoinGameResponse joinGame = 5; LeaveGameResponse leaveGame = 6; StartGameResponse startGame = 7; - GameUpdateResponse udpateGame = 8; + GameUpdateResponse updateGame = 8; } }