diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..8afe0da --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +# Editor configuration, see http://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 4 +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/gulpfile.js b/gulpfile.js index 559ed4a..2420423 100755 --- a/gulpfile.js +++ b/gulpfile.js @@ -124,7 +124,7 @@ const server = new Project( config.branch({ name: 'server', sources: ['src/server/haxe'], - main: 'ru.m.tankz.server.Server', + main: 'ru.m.tankz.server.TestServer', }) ).bind(module, gulp); diff --git a/src/client/haxe/ru/m/tankz/Init.hx b/src/client/haxe/ru/m/tankz/Init.hx index 40e4ca2..79c447c 100644 --- a/src/client/haxe/ru/m/tankz/Init.hx +++ b/src/client/haxe/ru/m/tankz/Init.hx @@ -79,7 +79,7 @@ class Init { #elseif html5 connection = new ru.m.connect.js.JsConnection(host, 5000, Response); #else - connection = new ru.m.connect.fake.FakeConnection(Response); + connection = new ru.m.connect.desktop.DesktopConnection(host, 5000, Response); #end networkManager = new NetworkManager(); } diff --git a/src/client/haxe/ru/m/tankz/Style.hx b/src/client/haxe/ru/m/tankz/Style.hx index 562fa50..c9c76cc 100644 --- a/src/client/haxe/ru/m/tankz/Style.hx +++ b/src/client/haxe/ru/m/tankz/Style.hx @@ -146,5 +146,7 @@ class Style { registerButton("close", "times-circle-solid.svg"); registerButton("next", "arrow-alt-circle-right-solid.svg"); registerButton("start", "play-circle-solid.svg"); + registerButton("login", "sign-in-solid.svg"); + registerButton("logout", "sign-out-solid.svg"); } } diff --git a/src/client/haxe/ru/m/tankz/network/NetworkManager.hx b/src/client/haxe/ru/m/tankz/network/NetworkManager.hx index 612ede0..aedc9f0 100644 --- a/src/client/haxe/ru/m/tankz/network/NetworkManager.hx +++ b/src/client/haxe/ru/m/tankz/network/NetworkManager.hx @@ -1,70 +1,80 @@ package ru.m.tankz.network; -import ru.m.tankz.storage.MultiplayerStorage; -import haxework.storage.IStorage; import haxework.signal.Signal; -import ru.m.tankz.proto.pack.GameRequest; -import ru.m.tankz.proto.core.GameProto; -import ru.m.tankz.proto.pack.StartGameRequest; -import ru.m.tankz.proto.game.GameChangeProto; -import ru.m.tankz.proto.game.GameActionTypeProto; -import ru.m.tankz.proto.pack.GameUpdateRequest; +import ru.m.connect.IConnection; import ru.m.tankz.control.Control; +import ru.m.tankz.proto.core.GameInfoProto; +import ru.m.tankz.proto.game.GameActionTypeProto; +import ru.m.tankz.proto.game.GameChangeProto; +import ru.m.tankz.proto.pack.CreateGameRequest; +import ru.m.tankz.proto.pack.GameUpdateRequest; import ru.m.tankz.proto.pack.JoinGameRequest; import ru.m.tankz.proto.pack.LeaveGameRequest; -import ru.m.tankz.proto.pack.CreateGameRequest; -import ru.m.connect.IConnection; -import ru.m.tankz.proto.core.GameInfoProto; import ru.m.tankz.proto.pack.ListGameRequest; import ru.m.tankz.proto.pack.LoginRequest; +import ru.m.tankz.proto.pack.LogoutRequest; import ru.m.tankz.proto.pack.Request; import ru.m.tankz.proto.pack.Response; +import ru.m.tankz.proto.pack.StartGameRequest; +import ru.m.tankz.storage.MultiplayerStorage; typedef ClientConnection = IConnection; +enum ConnectionState { + OFFLINE; + CONNECT; + CONNECTED; + LOGIN; + ONLINE(user:User); + ERROR(error:Dynamic); +} + class NetworkManager { - public var state(default, null):String; - public var stateSignal:Signal; + public var state(default, null):ConnectionState; + public var stateSignal:Signal; public var listGameSignal:Signal>; public var gameSignal:Signal; public var gameUpdateSignal:Signal>; - public var user(default, null):User; - public var game(default, set):NetworkGame; @:provide private var connection:ClientConnection; @:provide private var storage:MultiplayerStorage; public function new() { - stateSignal = new Signal(); - listGameSignal = new Signal>(); - gameSignal = new Signal(); - gameUpdateSignal = new Signal>(); - updateState('offline'); + stateSignal = new Signal(); + listGameSignal = new Signal(); + gameSignal = new Signal(); + gameUpdateSignal = new Signal(); + updateState(OFFLINE); connection.handler.connect(onConnectionEvent); connection.receiveHandler.connect(onResponse); - user = storage.user; - if (user == null) { - user = {name: 'User', uuid: null}; + var user = storage.user; + if (user != null) { + login(user.name, user.uuid); } } - private function updateState(value:String):Void { + private function updateState(value:ConnectionState):Void { state = value; stateSignal.emit(value); } - public function login(name:String):Void { - user.name = name; - updateState('connect...'); + public function login(name:String, uuid:String = null):Void { + updateState(CONNECT); connection.connect().then(function(c:ClientConnection) { - updateState('login...'); + updateState(LOGIN); c.send(new Request().setLogin( new LoginRequest() - .setUuid(user.uuid) - .setName(user.name) + .setUuid(uuid) + .setName(name) )); - }).catchError(function(_) {}); + }).catchError(function(error) { + updateState(ERROR(error)); + }); + } + + public function logout():Void { + connection.send(new Request().setLogout(new LogoutRequest())); } public function listGame():Void { @@ -108,30 +118,30 @@ class NetworkManager { } private function onConnectionEvent(event:ConnectionEvent):Void { - updateState(switch (event) { - case ConnectionEvent.CONNECTED: + updateState(switch event { + case CONNECTED: L.d('Network', '$event'); - 'connected'; - case ConnectionEvent.DISCONNECTED: + CONNECTED; + case DISCONNECTED: L.d('Network', '$event'); - 'offline'; - case ConnectionEvent.ERROR(error): + OFFLINE; + case ERROR(error): L.e('Network', '$error', error); - 'error'; + ERROR(error); }); } private function onResponse(packet:Response):Void { if (packet.hasLogin()) { - user = { + var user = { uuid: packet.login.user.uuid, name: packet.login.user.name, }; storage.user = user; - updateState('online'); + updateState(ONLINE(user)); } else if (packet.hasLogout()) { - user = null; - updateState('connected'); + storage.user = null; + updateState(CONNECTED); } else if (packet.hasListGame()) { listGameSignal.emit(packet.listGame.games); } else if (packet.hasCreateGame()) { @@ -145,13 +155,7 @@ class NetworkManager { } else if (packet.hasUpdateGame()) { gameUpdateSignal.emit(packet.updateGame.changes); } else if (packet.hasGame()) { - game.load(packet.game.game); + //game.load(packet.game.game); } } - - private function set_game(value:NetworkGame):NetworkGame { - this.game = value; - connection.send(new Request().setGame(new GameRequest())); - return this.game; - } } diff --git a/src/client/haxe/ru/m/tankz/view/StartFrame.hx b/src/client/haxe/ru/m/tankz/view/StartFrame.hx index ed80309..bc0b4fa 100644 --- a/src/client/haxe/ru/m/tankz/view/StartFrame.hx +++ b/src/client/haxe/ru/m/tankz/view/StartFrame.hx @@ -1,25 +1,69 @@ package ru.m.tankz.view; +import haxework.view.ButtonView; +import haxework.view.LabelView; import haxework.view.frame.FrameSwitcher; import haxework.view.VGroupView; import ru.m.tankz.game.GameState; +import ru.m.tankz.network.NetworkManager; import ru.m.tankz.Type.GameType; import ru.m.tankz.view.popup.FontPopup; +import ru.m.tankz.view.popup.LoginPopup; @:template class StartFrame extends VGroupView { public static var ID(default, never):String = "start"; + @:view var username:LabelView; + @:view("login") var loginButton:ButtonView; + @:view("logout") var logoutButton:ButtonView; + @:view("network") var networkButton:ButtonView; + @:provide var state:GameState; @:provide var switcher:FrameSwitcher; + @:provide var network:NetworkManager; private var fontPopup:FontPopup; + public function onShow():Void { + onConnectionState(network.state); + network.stateSignal.connect(onConnectionState); + } + + public function onHide():Void { + network.stateSignal.disconnect(onConnectionState); + } + + private function onConnectionState(state:ConnectionState):Void { + trace("state", state); + setUser(switch state { + case ONLINE(user): user; + case _: null; + }); + } + + private function setUser(value:User):Void { + username.text = value == null ? "" : value.name; + loginButton.visible = value == null; + logoutButton.visible = value != null; + networkButton.disabled = value == null; + } + private function startGame(type:GameType):Void { state = new GameState(type); switcher.change(LevelFrame.ID); } + private function login():Void { + LoginPopup.instance.show().then(function(user:User):Void { + L.d("Login", 'user: $user'); + }); + } + + private function logout():Void { + network.logout(); + } + private function choiceFont():Void { if (fontPopup == null) { fontPopup = new FontPopup(); diff --git a/src/client/haxe/ru/m/tankz/view/StartFrame.yaml b/src/client/haxe/ru/m/tankz/view/StartFrame.yaml index 96014ec..d9ec040 100644 --- a/src/client/haxe/ru/m/tankz/view/StartFrame.yaml +++ b/src/client/haxe/ru/m/tankz/view/StartFrame.yaml @@ -1,40 +1,63 @@ --- views: - - $type: haxework.view.VGroupView - skinId: container - layout.margin: 10 - views: - - $type: haxework.view.LabelView - text: Tank'z - skinId: font - fontSize: 100 - geometry.margin.bottom: 30 - - $type: haxework.view.ButtonView - skinId: button - +onPress: $code:startGame('classic') - text: Classic - - $type: haxework.view.ButtonView - skinId: button - +onPress: $code:startGame('dota') - text: DotA - - $type: haxework.view.ButtonView - skinId: button - +onPress: $code:startGame('death') - text: DeathMatch - - $type: haxework.view.ButtonView - skinId: button - +onPress: $code:switcher.change('record') - text: Records - - $type: haxework.view.HGroupView - skinId: panel - views: - - id: settings - $type: haxework.view.ButtonView - skinId: button.settings - +onPress: $code:switcher.change('settings') - - $type: haxework.view.SpriteView - geometry.size.width: 100% - - $type: haxework.view.LabelView - geometry.padding: [20, 5] - skinId: text.box - text: $r:text:version + - $type: haxework.view.VGroupView + skinId: container + layout.margin: 10 + views: + - $type: haxework.view.LabelView + text: Tank'z + skinId: font + fontSize: 100 + geometry.margin.bottom: 30 + - $type: haxework.view.ButtonView + skinId: button + +onPress: $code:startGame('classic') + text: Classic + - $type: haxework.view.ButtonView + skinId: button + +onPress: $code:startGame('dota') + text: DotA + - $type: haxework.view.ButtonView + skinId: button + +onPress: $code:startGame('death') + text: DeathMatch + - $type: haxework.view.ButtonView + skinId: button + +onPress: $code:switcher.change('record') + text: Records + - id: network + $type: haxework.view.ButtonView + skinId: button + # +onPress: $code:switcher.change('record') + text: Network + disabled: true + - $type: haxework.view.HGroupView + skinId: panel + views: + - id: settings + $type: haxework.view.ButtonView + skinId: button.settings + +onPress: $code:switcher.change('settings') + - $type: haxework.view.SpriteView + geometry.size.width: 100% + - id: username + $type: haxework.view.LabelView + skinId: text + geometry.margin.right: 10 + - id: login + $type: haxework.view.ButtonView + skinId: button.login + +onPress: $code:login() + - id: logout + $type: haxework.view.ButtonView + skinId: button.logout + +onPress: $code:logout() + visible: false + - $type: haxework.view.LabelView + geometry.hAlign: right + geometry.vAlign: top + geometry.padding: [20, 5] + geometry.position: absolute + geometry.margin: [0, 20, 20, 0] + skinId: text.box + text: $r:text:version diff --git a/src/client/haxe/ru/m/tankz/view/popup/FontPopup.yaml b/src/client/haxe/ru/m/tankz/view/popup/FontPopup.yaml index 43ae0b5..f25b897 100644 --- a/src/client/haxe/ru/m/tankz/view/popup/FontPopup.yaml +++ b/src/client/haxe/ru/m/tankz/view/popup/FontPopup.yaml @@ -1,27 +1,27 @@ --- view: - $type: haxework.view.VGroupView - geometry.size.width: 400 - geometry.size.height: 80% - geometry.padding: 10 - geometry.hAlign: center - geometry.vAlign: middle - skinId: dark - views: - - id: fonts - $type: haxework.view.list.VListView - geometry.size.stretch: true - factory: $this:fontViewFactory - +onItemSelect: $code:function(item) close(item.data) - scroll: - $type: haxework.view.list.VScrollBarView - skinId: scroll.vertical - - $type: haxework.view.HGroupView - geometry.size.width: 100% - geometry.margin.top: 10 - layout.hAlign: right - views: - - $type: haxework.view.ButtonView - skinId: button.simple - text: Cancel - +onPress: $code:reject('cancel') + $type: haxework.view.VGroupView + geometry.size.width: 400 + geometry.size.height: 80% + geometry.padding: 10 + geometry.hAlign: center + geometry.vAlign: middle + skinId: dark + views: + - id: fonts + $type: haxework.view.list.VListView + geometry.size.stretch: true + factory: $this:fontViewFactory + +onItemSelect: $code:function(item) close(item.data) + scroll: + $type: haxework.view.list.VScrollBarView + skinId: scroll.vertical + - $type: haxework.view.HGroupView + geometry.size.width: 100% + geometry.margin.top: 10 + layout.hAlign: right + views: + - $type: haxework.view.ButtonView + skinId: button.simple + text: Cancel + +onPress: $code:reject('cancel') diff --git a/src/client/haxe/ru/m/tankz/view/popup/LevelPopup.yaml b/src/client/haxe/ru/m/tankz/view/popup/LevelPopup.yaml index 0ba2854..fc584c3 100644 --- a/src/client/haxe/ru/m/tankz/view/popup/LevelPopup.yaml +++ b/src/client/haxe/ru/m/tankz/view/popup/LevelPopup.yaml @@ -2,34 +2,34 @@ layout.hAlign: center layout.vAlign: middle view: - $type: haxework.view.VGroupView - layout.hAlign: center - geometry.size.width: 400 - geometry.size.height: 400 - skinId: window - views: - - $type: haxework.view.HGroupView - geometry.size.width: 100% - geometry.padding: 10 - layout.vAlign: middle - views: - - id: name - $type: haxework.view.LabelView - geometry.size.width: 100% - geometry.margin.left: 10 - layout.hAlign: left - skinId: text - - $type: haxework.view.ButtonView - skinId: window.close - +onPress: $code:reject('close') - - $type: haxework.view.SpriteView - geometry.size.height: 100% - - id: presets - $type: haxework.view.DataView - factory: $this:presetViewFactory - +onDataSelect: $this:onPresetSelect - layout: - $type: haxework.view.layout.HorizontalLayout - hAlign: center - margin: 5 - skinId: panel + $type: haxework.view.VGroupView + layout.hAlign: center + geometry.size.width: 400 + geometry.size.height: 400 + skinId: window + views: + - $type: haxework.view.HGroupView + geometry.size.width: 100% + geometry.padding: 10 + layout.vAlign: middle + views: + - id: name + $type: haxework.view.LabelView + geometry.size.width: 100% + geometry.margin.left: 10 + layout.hAlign: left + skinId: text + - $type: haxework.view.ButtonView + skinId: window.close + +onPress: $code:reject('close') + - $type: haxework.view.SpriteView + geometry.size.height: 100% + - id: presets + $type: haxework.view.DataView + factory: $this:presetViewFactory + +onDataSelect: $this:onPresetSelect + layout: + $type: haxework.view.layout.HorizontalLayout + hAlign: center + margin: 5 + skinId: panel diff --git a/src/client/haxe/ru/m/tankz/view/popup/LoginPopup.hx b/src/client/haxe/ru/m/tankz/view/popup/LoginPopup.hx new file mode 100644 index 0000000..c44e070 --- /dev/null +++ b/src/client/haxe/ru/m/tankz/view/popup/LoginPopup.hx @@ -0,0 +1,47 @@ +package ru.m.tankz.view.popup; + +import haxework.log.BaseLogger.LoggerUtil; +import haxework.view.InputView; +import haxework.view.popup.PopupView; +import haxework.view.TextView; +import ru.m.tankz.network.NetworkManager; + +@:template class LoginPopup extends PopupView { + + @:view var username:InputView; + @:view var password:InputView; + @:view var error:TextView; + + @:provide static var network:NetworkManager; + + override private function onShow():Void { + super.onShow(); + network.stateSignal.connect(onStateChange); + } + + override private function onClose():Void { + super.onClose(); + network.stateSignal.disconnect(onStateChange); + } + + private function onStateChange(state:ConnectionState):Void { + switch state { + case ONLINE(user): close(user); + case ERROR(error): this.error.text = LoggerUtil.printError(error); + case _: this.error.text = null; + } + } + + private function submit():Void { + network.login(username.text); + } + + public static var instance(get, null):LoginPopup; + + private static function get_instance():LoginPopup { + if (instance == null) { + instance = new LoginPopup(); + } + return instance; + } +} diff --git a/src/client/haxe/ru/m/tankz/view/popup/LoginPopup.yaml b/src/client/haxe/ru/m/tankz/view/popup/LoginPopup.yaml new file mode 100644 index 0000000..bccd258 --- /dev/null +++ b/src/client/haxe/ru/m/tankz/view/popup/LoginPopup.yaml @@ -0,0 +1,64 @@ +--- +layout.hAlign: center +layout.vAlign: middle +view: + $type: haxework.view.VGroupView + layout.hAlign: center + geometry.size.width: 400 + # geometry.size.height: 400 + skinId: window + views: + - $type: haxework.view.HGroupView + geometry.size.width: 100% + geometry.padding: 10 + layout.vAlign: middle + views: + - id: name + $type: haxework.view.LabelView + geometry.size.width: 100% + geometry.margin.left: 10 + layout.hAlign: left + skinId: text + text: Login + - $type: haxework.view.ButtonView + skinId: window.close + +onPress: $code:reject('close') + - $type: haxework.view.VGroupView + geometry.size.width: 100% + # geometry.size.stretch: true + geometry.padding: 20 + layout.margin: 5 + views: + - $type: haxework.view.LabelView + geometry.size.width: 100% + skinId: text + text: Username + - id: username + $type: haxework.view.InputView + geometry.size.width: 100% + geometry.size.height: 28 + skinId: text.box + - $type: haxework.view.LabelView + geometry.size.width: 100% + skinId: text + text: Password + - id: password + $type: haxework.view.InputView + textField.displayAsPassword: true + geometry.size.width: 100% + geometry.size.height: 28 + skinId: text.box + - id: error + $type: haxework.view.TextView + geometry.size.width: 100% + skinId: text + fill: false + - $type: haxework.view.HGroupView + layout.hAlign: center + layout.margin: 5 + skinId: panel + views: + - $type: haxework.view.ButtonView + skinId: button.simple + text: Submit + +onPress: $code:submit() diff --git a/src/client/resources/image/icon/sign-in-solid.svg b/src/client/resources/image/icon/sign-in-solid.svg new file mode 100644 index 0000000..a6b7b3d --- /dev/null +++ b/src/client/resources/image/icon/sign-in-solid.svg @@ -0,0 +1 @@ + diff --git a/src/client/resources/image/icon/sign-out-solid.svg b/src/client/resources/image/icon/sign-out-solid.svg new file mode 100644 index 0000000..efbfbbb --- /dev/null +++ b/src/client/resources/image/icon/sign-out-solid.svg @@ -0,0 +1 @@ + diff --git a/src/common/haxe/ru/m/connect/BaseConnection.hx b/src/common/haxe/ru/m/connect/BaseConnection.hx index a718faf..0990ec1 100755 --- a/src/common/haxe/ru/m/connect/BaseConnection.hx +++ b/src/common/haxe/ru/m/connect/BaseConnection.hx @@ -7,7 +7,6 @@ import promhx.Promise; import protohx.Message; import ru.m.connect.IConnection; - class BaseConnection implements IConnection { public var handler(default, null):Signal; public var sendHandler(default, null):Signal; @@ -17,12 +16,11 @@ class BaseConnection implements IConnection { private var connectDeferred:Deferred>; - public function new(i:Class) { - queue = new PacketQueue(i); + public function new(inputFactory:Class) { + queue = new PacketQueue(inputFactory); handler = new Signal(); sendHandler = new Signal(); receiveHandler = new Signal(); - connectDeferred = new Deferred(); } public function connect():Promise> { diff --git a/src/common/haxe/ru/m/connect/IConnection.hx b/src/common/haxe/ru/m/connect/IConnection.hx index 596394f..1ccfe21 100755 --- a/src/common/haxe/ru/m/connect/IConnection.hx +++ b/src/common/haxe/ru/m/connect/IConnection.hx @@ -5,7 +5,6 @@ import haxe.io.Bytes; import promhx.Promise; import protohx.Message; - enum ConnectionEvent { CONNECTED; DISCONNECTED; diff --git a/src/common/haxe/ru/m/connect/desktop/DesktopConnection.hx b/src/common/haxe/ru/m/connect/desktop/DesktopConnection.hx new file mode 100644 index 0000000..08862bd --- /dev/null +++ b/src/common/haxe/ru/m/connect/desktop/DesktopConnection.hx @@ -0,0 +1,78 @@ +package ru.m.connect.desktop; + +import cpp.vm.Thread; +import haxe.io.BytesOutput; +import haxe.Timer; +import promhx.Deferred; +import promhx.Promise; +import protohx.Message; +import ru.m.connect.IConnection; +import sys.net.Host; +import sys.net.Socket; + +class DesktopConnection extends BaseConnection { + + private var host:String; + private var port:Int; + private var socket:Socket; + private var reader:Thread; + + public function new(host:String, port:Int, inputFactory:Class) { + super(inputFactory); + this.host = host; + this.port = port; + connected = false; + socket = new Socket(); + socket.setFastSend(true); + socket.output.bigEndian = false; + socket.input.bigEndian = false; + sendHandler.connect(_send); + } + + override public function connect():Promise> { + connectDeferred = new Deferred(); + try { + if (connected) { + connectDeferred.resolve(this); + } else { + socket.connect(new Host(host), port); + connected = true; + reader = Thread.create(_read); + connectDeferred.resolve(this); + } + } catch (error:Dynamic) { + Timer.delay(function() connectDeferred.throwError(error), 1); + } + return connectDeferred.promise(); + } + + override public function disconnect():Void { + socket.close(); + connected = false; + handler.emit(DISCONNECTED); + } + + private function _read():Void { + try { + while (connected) { + socket.waitForRead(); + var size = socket.input.readUInt16(); + var data = socket.input.read(size); + var packet:I = Type.createInstance(queue.packetClass, []); + packet.mergeFrom(data); + receiveHandler.emit(packet); + } + } catch (error:Dynamic) { + handler.emit(ERROR(error)); + } + } + + private function _send(packet:O):Void { + var out = new BytesOutput(); + packet.writeTo(out); + var bytes = out.getBytes(); + socket.output.writeUInt16(bytes.length); + socket.output.write(bytes); + socket.output.flush(); + } +} diff --git a/src/common/haxe/ru/m/connect/flash/FlashConnection.hx b/src/common/haxe/ru/m/connect/flash/FlashConnection.hx index 70bffcd..0269d90 100755 --- a/src/common/haxe/ru/m/connect/flash/FlashConnection.hx +++ b/src/common/haxe/ru/m/connect/flash/FlashConnection.hx @@ -1,18 +1,18 @@ package ru.m.connect.flash; -import ru.m.connect.IConnection.ConnectionEvent; -import promhx.Promise; -import flash.utils.Endian; -import haxe.io.BytesOutput; -import protohx.Message; -import haxe.io.Bytes; import flash.events.ErrorEvent; -import flash.events.ProgressEvent; import flash.events.Event; -import flash.events.SecurityErrorEvent; import flash.events.IOErrorEvent; +import flash.events.ProgressEvent; +import flash.events.SecurityErrorEvent; import flash.net.Socket; - +import flash.utils.Endian; +import haxe.io.Bytes; +import haxe.io.BytesOutput; +import promhx.Deferred; +import promhx.Promise; +import protohx.Message; +import ru.m.connect.IConnection; class FlashConnection extends BaseConnection { @@ -20,8 +20,8 @@ class FlashConnection extends BaseConnection { private var port:Int; private var socket:Socket; - public function new(host:String, port:Int, i:Class) { - super(i); + public function new(host:String, port:Int, inputFactory:Class) { + super(inputFactory); this.host = host; this.port = port; connected = false; @@ -37,14 +37,15 @@ class FlashConnection extends BaseConnection { override public function connect():Promise> { socket.connect(host, port); + connectDeferred = new Deferred(); return connectDeferred.promise(); } override public function disconnect():Void { if (socket.connected) { socket.close(); - //connected = false; - //handler.emit(ConnectionEvent.DISCONNECTED); + connected = false; + handler.emit(ConnectionEvent.DISCONNECTED); } } @@ -52,13 +53,19 @@ class FlashConnection extends BaseConnection { socket.close(); connected = false; handler.emit(ConnectionEvent.ERROR(event)); - connectDeferred.throwError(event); + if (connectDeferred != null) { + connectDeferred.throwError(event); + connectDeferred = null; + } } private function onConnect(_):Void { connected = true; handler.emit(ConnectionEvent.CONNECTED); - connectDeferred.resolve(this); + if (connectDeferred != null) { + connectDeferred.resolve(this); + connectDeferred = null; + } } private function onClose(_):Void { @@ -86,4 +93,4 @@ class FlashConnection extends BaseConnection { socket.writeBytes(cast bytes.getData()); socket.flush(); } -} \ No newline at end of file +} diff --git a/src/common/haxe/ru/m/connect/js/JsConnection.hx b/src/common/haxe/ru/m/connect/js/JsConnection.hx index 6315596..9d5b763 100644 --- a/src/common/haxe/ru/m/connect/js/JsConnection.hx +++ b/src/common/haxe/ru/m/connect/js/JsConnection.hx @@ -2,11 +2,11 @@ package ru.m.connect.js; import js.Browser; import js.html.WebSocket; +import promhx.Deferred; import promhx.Promise; import protohx.Message; import ru.m.Base64; -import ru.m.connect.IConnection.ConnectionEvent; - +import ru.m.connect.IConnection; class JsConnection extends BaseConnection { @@ -14,8 +14,8 @@ class JsConnection extends BaseConnection { private var port:Int; private var socket:WebSocket; - public function new(host:String, port:Int, i:Class) { - super(i); + public function new(host:String, port:Int, inputFactory:Class) { + super(inputFactory); this.host = host; this.port = port; connected = false; @@ -38,11 +38,12 @@ class JsConnection extends BaseConnection { socket.onclose = this.onClose; socket.onerror = this.onError; socket.onmessage = this.onSocketData; + connectDeferred = new Deferred(); return connectDeferred.promise(); } override public function disconnect():Void { - socket.close(); + socket.close(1000); connected = false; } @@ -78,4 +79,4 @@ class JsConnection extends BaseConnection { super.send(packet); socket.send(WebSocketTools.packet2string(packet)); } -} \ No newline at end of file +} diff --git a/src/common/haxe/ru/m/connect/neko/NekoConnection.hx b/src/common/haxe/ru/m/connect/neko/NekoConnection.hx index e83749a..f6709bd 100755 --- a/src/common/haxe/ru/m/connect/neko/NekoConnection.hx +++ b/src/common/haxe/ru/m/connect/neko/NekoConnection.hx @@ -4,7 +4,6 @@ import haxe.io.BytesOutput; import protohx.Message; import sys.net.Socket; - class NekoConnection extends BaseConnection { public var socket(default, null):Socket; @@ -30,4 +29,4 @@ class NekoConnection extends BaseConnection { L.e('Proto', 'Error send packet: ${packet}', error); } } -} \ No newline at end of file +} diff --git a/src/common/haxe/ru/m/connect/neko/NekoWebConnection.hx b/src/common/haxe/ru/m/connect/neko/NekoWebConnection.hx index cb67524..ea797f8 100644 --- a/src/common/haxe/ru/m/connect/neko/NekoWebConnection.hx +++ b/src/common/haxe/ru/m/connect/neko/NekoWebConnection.hx @@ -2,11 +2,10 @@ package ru.m.connect.neko; import haxe.crypto.BaseCode; import haxe.crypto.Sha1; -import protohx.Message; import haxe.io.Bytes; +import protohx.Message; import sys.net.Socket; - class NekoWebConnection extends NekoConnection { private var opened:Bool; @@ -180,4 +179,4 @@ class NekoWebConnection extends NekoConnection { } return null; } -} \ No newline at end of file +} diff --git a/src/common/haxe/ru/m/tankz/network/NetworkGame.hx b/src/common/haxe/ru/m/tankz/network/NetworkGame.hx deleted file mode 100644 index a057924..0000000 --- a/src/common/haxe/ru/m/tankz/network/NetworkGame.hx +++ /dev/null @@ -1,18 +0,0 @@ -package ru.m.tankz.network; - -import ru.m.tankz.game.Game; -import ru.m.tankz.proto.core.GameProto; - -class NetworkGame extends Game { - - private static var TAG(default, never):String = 'NetworkGame'; - - public function load(proto:GameProto):Void { - L.w(TAG, 'load: ${proto}'); - // ToDo: - } - - public function export():GameProto { - return new GameProto(); - } -} diff --git a/src/server/haxe/ru/m/tankz/server/Server.hx b/src/server/haxe/ru/m/tankz/server/Server.hx index f96ff7c..7455dcf 100755 --- a/src/server/haxe/ru/m/tankz/server/Server.hx +++ b/src/server/haxe/ru/m/tankz/server/Server.hx @@ -11,12 +11,12 @@ import ru.m.tankz.bundle.IConfigBundle; import ru.m.tankz.bundle.ILevelBundle; import ru.m.tankz.server.bundle.ServerConfigBundle; import ru.m.tankz.server.bundle.ServerLevelBundle; -import ru.m.tankz.server.session.Session; +import ru.m.tankz.server.session._Session; import sys.net.Socket; #if debug import haxework.log.SocketLogger; #end -class Server extends ThreadServer { +class Server extends ThreadServer<_Session, Bytes> { private static inline var TAG = 'Server'; @@ -24,23 +24,23 @@ class Server extends ThreadServer { super(); } - override public function clientConnected(s:Socket):Session { - var session = new Session(s); + override public function clientConnected(s:Socket):_Session { + var session = new _Session(s); L.d(TAG, 'Client connected'); return session; } - override public function clientDisconnected(session:Session) { + override public function clientDisconnected(session:_Session) { L.d(TAG, 'Client disconnected'); session.connection.handler.emit(ConnectionEvent.DISCONNECTED); } - override public function readClientMessage(session:Session, buf:Bytes, pos:Int, len:Int) { + override public function readClientMessage(session:_Session, buf:Bytes, pos:Int, len:Int) { //L.d(TAG, 'Client message: ${buf}'); return {msg: buf.sub(pos, len), bytes: len}; } - override public function clientMessage(session:Session, bytes:Bytes) { + override public function clientMessage(session:_Session, bytes:Bytes) { try { session.pushData(bytes); } catch (error:Dynamic) { @@ -64,4 +64,4 @@ class Server extends ThreadServer { L.i(TAG, 'Start on ${host}:${port}'); wserver.run(host, port); } -} \ No newline at end of file +} diff --git a/src/server/haxe/ru/m/tankz/server/TestServer.hx b/src/server/haxe/ru/m/tankz/server/TestServer.hx new file mode 100755 index 0000000..35f848a --- /dev/null +++ b/src/server/haxe/ru/m/tankz/server/TestServer.hx @@ -0,0 +1,46 @@ +package ru.m.tankz.server; + +import haxe.io.Bytes; +import haxework.log.TraceLogger; +import neko.net.ThreadServer; +import ru.m.tankz.server.session.GameSession; +import sys.net.Socket; + +class TestServer extends ThreadServer { + + private static inline var TAG = 'Server'; + + public function new() { + super(); + } + + override public function clientConnected(socket:Socket):GameSession { + var session = new GameSession(socket); + L.d(TAG, 'Client connected'); + return session; + } + + override public function clientDisconnected(session:GameSession) { + L.d(TAG, 'Client disconnected'); + session.disconnect(); + } + + override public function readClientMessage(session:GameSession, buf:Bytes, pos:Int, len:Int) { + //L.d(TAG, 'Client message: ${buf}'); + return {msg: buf.sub(pos, len), bytes: len}; + } + + override public function clientMessage(session:GameSession, bytes:Bytes) { + session.pushData(bytes); + } + + public static function main() { + L.push(new TraceLogger()); + L.d(TAG, 'Running'); + var host:String = Sys.args().length > 0 ? Sys.args()[0] : "localhost"; + var port:Int = Sys.args().length > 1 ? Std.parseInt(Sys.args()[1]) : 5000; + var wserver = new TestServer(); + L.i(TAG, 'Start on ${host}:${port}'); + wserver.run(host, port); + } +} diff --git a/src/server/haxe/ru/m/tankz/server/bundle/ServerConfigBundle.hx b/src/server/haxe/ru/m/tankz/server/bundle/ServerConfigBundle.hx index c774ea5..cf1e67b 100644 --- a/src/server/haxe/ru/m/tankz/server/bundle/ServerConfigBundle.hx +++ b/src/server/haxe/ru/m/tankz/server/bundle/ServerConfigBundle.hx @@ -8,7 +8,6 @@ import sys.io.File; import yaml.Parser; import yaml.Yaml; - class ServerConfigBundle implements IConfigBundle { public function new() {} diff --git a/src/server/haxe/ru/m/tankz/server/game/GameManager.hx b/src/server/haxe/ru/m/tankz/server/game/GameManager.hx index 93f9a5c..e9f0057 100644 --- a/src/server/haxe/ru/m/tankz/server/game/GameManager.hx +++ b/src/server/haxe/ru/m/tankz/server/game/GameManager.hx @@ -1,7 +1,8 @@ package ru.m.tankz.server.game; +import ru.m.tankz.game.Game; +import ru.m.tankz.game.IGame; import ru.m.tankz.game.GameState; -import ru.m.tankz.network.NetworkGame; import ru.m.tankz.preset.ClassicGame; import ru.m.tankz.proto.pack.StartGameResponse; import ru.m.tankz.proto.pack.LeaveGameResponse; @@ -13,7 +14,7 @@ import ru.m.tankz.proto.core.GameStateProto; import ru.m.tankz.proto.core.GameInfoProto; import ru.m.tankz.proto.core.UserProto; import ru.m.tankz.server.session.Thread; -import ru.m.tankz.server.session.Session; +import ru.m.tankz.server.session._Session; /** @@ -58,7 +59,7 @@ class GameManager { private static var idCounter:Int = 0; public var gameInfo(default, null):GameInfoProto; - public var game(default, null):NetworkGame; + public var game(default, null):IGame; private var timer:NekoTimer; @@ -83,14 +84,14 @@ class GameManager { public function broadcastGames() { var packet = new Response().setListGame(new ListGameResponse().setGames(getReadyGames())); for (personId in subscribers.keys()) { - var session:Session = Session.sessions.get(personId); + var session:_Session = _Session.sessions.get(personId); session.send(packet); } } public function broadcast(packet:Response) { for (player in gameInfo.players) { - var session:Session = Session.sessions.get(player.uuid); + var session:_Session = _Session.sessions.get(player.uuid); session.send(packet); } } @@ -105,7 +106,7 @@ class GameManager { gameInfo.setPlayers(gameInfo.players.filter(function(p:UserProto) return p.uuid != user.uuid)); byPersonId.remove(user.uuid); var packet = new Response().setLeaveGame(new LeaveGameResponse().setGame(gameInfo)); - Session.sessions.get(user.uuid).send(packet); + _Session.sessions.get(user.uuid).send(packet); if (gameInfo.players.length == 0/* || person.id == game.creator.id*/) { stop(); } else { @@ -115,8 +116,9 @@ class GameManager { public function start() { gameInfo.setState(GameStateProto.STARTED); - game = new NetworkGame(ClassicGame.TYPE); - game.start(new GameState(ClassicGame.TYPE,ClassicGame.PLAYER1)); + var state = new GameState(ClassicGame.TYPE); + game = new Game(state); + //game.start(new GameState(ClassicGame.TYPE,ClassicGame.PLAYER1)); timer = new NekoTimer(30); timer.run = update; broadcast(new Response().setStartGame(new StartGameResponse().setGame(gameInfo))); @@ -136,7 +138,7 @@ class GameManager { } private function update() { - game.engine.update(); + //game.engine.update(); /*var changes = engine.update(); changes = this.changes.concat(changes); this.changes = []; diff --git a/src/server/haxe/ru/m/tankz/server/session/GameSession.hx b/src/server/haxe/ru/m/tankz/server/session/GameSession.hx new file mode 100644 index 0000000..eb78792 --- /dev/null +++ b/src/server/haxe/ru/m/tankz/server/session/GameSession.hx @@ -0,0 +1,42 @@ +package ru.m.tankz.server.session; + +import ru.m.tankz.proto.pack.LogoutResponse; +import ru.m.tankz.proto.pack.LogoutRequest; +import com.hurlant.crypto.extra.UUID; +import com.hurlant.crypto.prng.Random; +import ru.m.tankz.proto.core.UserProto; +import ru.m.tankz.proto.pack.LoginRequest; +import ru.m.tankz.proto.pack.LoginResponse; +import ru.m.tankz.proto.pack.Request; +import ru.m.tankz.proto.pack.Response; +import sys.net.Socket; + +class GameSession extends ProtoSession { + private static inline var TAG = "Session"; + + public var user(default, null):UserProto; + + public function new(socket:Socket) { + super(socket, Request); + } + + private function onLogin(request:LoginRequest):LoginResponse { + user = new UserProto() + .setUuid(request.uuid != null ? request.uuid : UUID.generateRandom(new Random()).toString()) + .setName(request.name); + return new LoginResponse().setUser(user); + } + + private function onLogout(request:LogoutRequest):LogoutResponse { + return new LogoutResponse(); + } + + override private function onRequest(request:Request):Void { + L.d(TAG, 'onRequest: ${request}'); + if (request.hasLogin()) { + send(new Response().setLogin(onLogin(request.login))); + } else if (request.hasLogout()) { + send(new Response().setLogout(onLogout(request.logout))); + } + } +} diff --git a/src/server/haxe/ru/m/tankz/server/session/ISession.hx b/src/server/haxe/ru/m/tankz/server/session/ISession.hx new file mode 100644 index 0000000..dc18339 --- /dev/null +++ b/src/server/haxe/ru/m/tankz/server/session/ISession.hx @@ -0,0 +1,8 @@ +package ru.m.tankz.server.session; + +import haxe.io.Bytes; + +interface ISession { + public function pushData(bytes:Bytes):Void; + public function disconnect():Void; +} diff --git a/src/server/haxe/ru/m/tankz/server/session/ProtoSession.hx b/src/server/haxe/ru/m/tankz/server/session/ProtoSession.hx new file mode 100644 index 0000000..b3c4189 --- /dev/null +++ b/src/server/haxe/ru/m/tankz/server/session/ProtoSession.hx @@ -0,0 +1,73 @@ +package ru.m.tankz.server.session; + +import ru.m.connect.neko.NekoWebConnection; +import haxe.io.Bytes; +import protohx.Message; +import ru.m.connect.IConnection; +import ru.m.connect.neko.NekoConnection; +import sys.net.Socket; + +class ProtoSession implements ISession { + private static inline var TAG = "Session"; + + private static var POLICY_FILE:String = [ + "", + "", + "", + "", + "", + "" + ].join("\n"); + + public var connection(default, null):IConnection; + private var socket:Socket; + private var request:Class; + + public function new(socket:Socket, request:Class) { + this.socket = socket; + this.request = request; + } + + private function buildConnection(bytes:Bytes):IConnection { + var str:String = bytes.getString(0, bytes.length); + if (str == "" + String.fromCharCode(0)) { + L.d(TAG, "policy-file-request"); + socket.output.writeString(POLICY_FILE + String.fromCharCode(0)); + socket.output.flush(); + return null; + } + if (StringTools.startsWith(str, "GET")) { + connection = new NekoWebConnection(socket, request); + } else { + connection = new NekoConnection(socket, request); + } + connection.handler.connect(onConnectionEvent); + connection.receiveHandler.connect(onRequest); + return connection; + } + + public function send(packet:O):Void { + connection.send(packet); + } + + public function pushData(bytes:Bytes):Void { + if (connection == null) { + connection = buildConnection(bytes); + } + if (connection != null) { + connection.pushData(bytes); + } + } + + public function disconnect():Void { + connection.handler.emit(ConnectionEvent.DISCONNECTED); + } + + private function onConnectionEvent(event:ConnectionEvent):Void { + L.d(TAG, 'onConnectionEvent: ${event}'); + } + + private function onRequest(request:I):Void { + L.d(TAG, 'onRequest: ${request}'); + } +} diff --git a/src/server/haxe/ru/m/tankz/server/session/Session.hx b/src/server/haxe/ru/m/tankz/server/session/_Session.hx similarity index 96% rename from src/server/haxe/ru/m/tankz/server/session/Session.hx rename to src/server/haxe/ru/m/tankz/server/session/_Session.hx index 7556aa0..d6d6c74 100755 --- a/src/server/haxe/ru/m/tankz/server/session/Session.hx +++ b/src/server/haxe/ru/m/tankz/server/session/_Session.hx @@ -31,7 +31,7 @@ import sys.net.Socket; typedef ServerConnection = IConnection; -class Session { +class _Session { private static inline var TAG = 'Session'; private static var POLICY_FILE:String = [ @@ -43,7 +43,7 @@ class Session { '' ].join('\n'); - public static var sessions:Map = new Map(); + public static var sessions:Map = new Map(); public var user(default, null):UserProto; public var gameId(default, null):Int = -1; @@ -150,6 +150,7 @@ class Session { private function game(request:GameRequest):GameResponse { var gameManager:GameManager = GameManager.byPersonId.get(user.uuid); - return new GameResponse().setGame(gameManager.game.export()); + return new GameResponse(); + //return new GameResponse().setGame(gameManager.game.export()); } -} \ No newline at end of file +}