[network] update

This commit is contained in:
2019-05-31 15:47:16 +03:00
parent 7115ea8ac2
commit fb9a28c126
29 changed files with 274 additions and 221 deletions

View File

@@ -48,6 +48,9 @@ const config = new Project.Config({
assets: [ assets: [
'src/common/resources' 'src/common/resources'
], ],
flags: [
//'proto_debug',
],
macros: [ macros: [
`CompilationOption.set('build','${dateformat(new Date(), 'yyyy-mm-dd HH:MM:ss')}')`, `CompilationOption.set('build','${dateformat(new Date(), 'yyyy-mm-dd HH:MM:ss')}')`,
] ]

View File

@@ -1,6 +1,6 @@
{ {
"name": "tankz", "name": "tankz",
"version": "0.13.1", "version": "0.13.2",
"private": true, "private": true,
"devDependencies": { "devDependencies": {
"dateformat": "^3.0.3", "dateformat": "^3.0.3",

View File

@@ -17,6 +17,7 @@ class NetworkGame extends Game {
this.network = network; this.network = network;
this.controlFactory = new NetworkControlFactory(); this.controlFactory = new NetworkControlFactory();
network.gameEventSignal.connect(onGameEventProto); network.gameEventSignal.connect(onGameEventProto);
network.stateSignal.connect(onConnectionState);
} }
private function onGameEventProto(game:GameEventResponse):Void { private function onGameEventProto(game:GameEventResponse):Void {
@@ -26,6 +27,16 @@ class NetworkGame extends Game {
gameEventSignal.emit(event); gameEventSignal.emit(event);
} }
private function onConnectionState(state:ConnectionState):Void {
switch state {
case ONLINE(user):
if (network.game != null) {
network.joinGame(network.game.id);
}
case _:
}
}
override public function start():Void { override public function start():Void {
var player = Lambda.find(network.game.players, function(player) return player.user.uuid == network.user.uuid); var player = Lambda.find(network.game.players, function(player) return player.user.uuid == network.user.uuid);
if (player != null) { if (player != null) {
@@ -37,5 +48,6 @@ class NetworkGame extends Game {
override public function dispose():Void { override public function dispose():Void {
super.dispose(); super.dispose();
network.gameEventSignal.disconnect(onGameEventProto); network.gameEventSignal.disconnect(onGameEventProto);
network.stateSignal.disconnect(onConnectionState);
} }
} }

View File

@@ -47,7 +47,11 @@ class NetworkManager {
@:provide private var connection:ClientConnection; @:provide private var connection:ClientConnection;
@:provide private var storage:MultiplayerStorage; @:provide private var storage:MultiplayerStorage;
private var reconnectTimer:Timer;
private var reconnectDelay:Int;
public function new() { public function new() {
reconnectDelay = 500;
stateSignal = new Signal(); stateSignal = new Signal();
listGameSignal = new Signal(); listGameSignal = new Signal();
gameSignal = new Signal(); gameSignal = new Signal();
@@ -56,10 +60,7 @@ class NetworkManager {
updateState(OFFLINE); updateState(OFFLINE);
connection.handler.connect(onConnectionEvent); connection.handler.connect(onConnectionEvent);
connection.receiveHandler.connect(onResponse); connection.receiveHandler.connect(onResponse);
var user = storage.user; connect();
if (user != null) {
login(user.name, user.uuid);
}
} }
private function updateState(value:ConnectionState):Void { private function updateState(value:ConnectionState):Void {
@@ -68,17 +69,12 @@ class NetworkManager {
} }
public function login(name:String, uuid:String = null):Void { 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( connection.send(new Request().setLogin(
new LoginRequest() new LoginRequest()
.setUuid(uuid) .setUuid(uuid)
.setName(name) .setName(name)
)); ));
}).catchError(function(error) {
updateState(ERROR(error));
});
} }
public function logout():Void { public function logout():Void {
@@ -93,8 +89,8 @@ class NetworkManager {
connection.send(new Request().setCreateGame(new CreateGameRequest().setType(type).setLevel(level))); connection.send(new Request().setCreateGame(new CreateGameRequest().setType(type).setLevel(level)));
} }
public function joinGame(gameId:Int):Void { public function joinGame(gameId:Int, restore:Bool = false):Void {
connection.send(new Request().setJoinGame(new JoinGameRequest().setGameId(gameId))); connection.send(new Request().setJoinGame(new JoinGameRequest().setGameId(gameId).setRestore(restore)));
} }
public function leaveGame():Void { public function leaveGame():Void {
@@ -111,18 +107,39 @@ class NetworkManager {
))); )));
} }
private function connect():Void {
if (reconnectTimer != null) {
reconnectTimer.stop();
reconnectTimer = null;
}
reconnectTimer = null;
updateState(CONNECT);
connection.connect().catchError(function(error) {});
}
private function reconnect():Void {
reconnectDelay = Std.int(Math.min(reconnectDelay * 2, 10000));
if (reconnectTimer == null) {
reconnectTimer = Timer.delay(connect, reconnectDelay);
}
}
private function onConnectionEvent(event:ConnectionEvent):Void { private function onConnectionEvent(event:ConnectionEvent):Void {
updateState(switch event { updateState(switch event {
case CONNECTED: case CONNECTED: CONNECTED;
L.d('Network', '$event'); case DISCONNECTED: OFFLINE;
CONNECTED; case ERROR(error): ERROR(error);
case DISCONNECTED:
L.d('Network', '$event');
OFFLINE;
case ERROR(error):
L.e('Network', '$error', error);
ERROR(error);
}); });
switch event {
case CONNECTED:
reconnectDelay = 500;
var user = storage.user;
if (user != null) {
login(user.name, user.uuid);
}
case DISCONNECTED | ERROR(_):
reconnect();
}
} }
private function onResponse(packet:Response):Void { private function onResponse(packet:Response):Void {

View File

@@ -3,18 +3,30 @@ package ru.m.tankz.view;
import flash.events.KeyboardEvent; import flash.events.KeyboardEvent;
import flash.ui.Keyboard; import flash.ui.Keyboard;
import haxework.resources.IResources; import haxework.resources.IResources;
import haxework.view.ButtonView;
import haxework.view.frame.FrameSwitcher; import haxework.view.frame.FrameSwitcher;
import haxework.view.LabelView;
import haxework.view.VGroupView;
import ru.m.tankz.network.NetworkManager;
import ru.m.tankz.sound.SoundManager; import ru.m.tankz.sound.SoundManager;
import ru.m.tankz.view.popup.LoginPopup;
@:template class ClientView extends FrameSwitcher { @:template class ClientView extends VGroupView {
@:view("switcher") var switcherView:FrameSwitcher;
@:view var username:LabelView;
@:view("login") var loginButton:ButtonView;
@:view("logout") var logoutButton:ButtonView;
@:provide var network:NetworkManager;
@:provide var resources:IResources; @:provide var resources:IResources;
@:provide var switcher:FrameSwitcher; @:provide var switcher:FrameSwitcher;
@:provide var soundManager:SoundManager; @:provide var soundManager:SoundManager;
public function init():Void { public function init():Void {
resources.text.put('version', '${Const.VERSION}'); resources.text.put('version', '${Const.VERSION}');
switcher = this; switcher = switcherView;
network.stateSignal.connect(onConnectionState);
onConnectionState(network.state);
} }
public function launch():Void { public function launch():Void {
@@ -22,11 +34,45 @@ import ru.m.tankz.sound.SoundManager;
content.stage.addEventListener(KeyboardEvent.KEY_UP, function(event:KeyboardEvent):Void { content.stage.addEventListener(KeyboardEvent.KEY_UP, function(event:KeyboardEvent):Void {
switch event.keyCode { switch event.keyCode {
case Keyboard.ESCAPE: case Keyboard.ESCAPE:
change(StartFrame.ID); switcher.change(StartFrame.ID);
case Keyboard.M: case Keyboard.M:
soundManager.mute = !soundManager.mute; soundManager.mute = !soundManager.mute;
} }
}); });
change(StartFrame.ID); switcher.change(StartFrame.ID);
}
private function onConnectionState(state:ConnectionState):Void {
L.d("ClientView", 'onConnectionState: ${state}');
switch state {
case ONLINE(user):
username.text = user.name;
logoutButton.visible = true;
loginButton.visible = false;
case CONNECTED:
username.text = "";
logoutButton.visible = false;
loginButton.visible = true;
case ERROR(error):
L.e("ClientView", 'onConnectionState: ERROR', error);
case _:
username.text = "";
logoutButton.visible = false;
loginButton.visible = false;
}
}
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 close():Void {
switcher.change(StartFrame.ID);
} }
} }

View File

@@ -1,5 +1,7 @@
--- ---
$type: haxework.view.frame.FrameSwitcher views:
- $type: haxework.view.frame.FrameSwitcher
id: switcher
geometry.size.stretch: true geometry.size.stretch: true
skinId: dark skinId: dark
views: views:
@@ -19,3 +21,31 @@ views:
$type: ru.m.tankz.view.network.GameListFrame $type: ru.m.tankz.view.network.GameListFrame
- id: game_room - id: game_room
$type: ru.m.tankz.view.network.GameRoomFrame $type: ru.m.tankz.view.network.GameRoomFrame
- $type: haxework.view.HGroupView
skinId: panel
layout.margin: 10
views:
- id: settings
$type: haxework.view.ButtonView
skinId: button.settings
+onPress: $code:switcher.change('settings')
- $type: haxework.view.SpriteView
geometry.size.width: 50%
- id: username
$type: haxework.view.LabelView
skinId: text
- 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.SpriteView
geometry.size.width: 50%
- id: close
$type: haxework.view.ButtonView
skinId: button.close
+onPress: $code:close()

View File

@@ -5,10 +5,3 @@ views:
views: views:
- id: game - id: game
$type: ru.m.tankz.view.game.GameView $type: ru.m.tankz.view.game.GameView
# - $type: haxework.view.HGroupView
# skinId: panel
# views:
# - id: close
# $type: haxework.view.ButtonView
# skinId: button.close
# +onPress: $code:close()

View File

@@ -16,9 +16,3 @@ views:
factory: $this:levelViewFactory factory: $this:levelViewFactory
+onItemSelect: $this:onLevelSelect +onItemSelect: $this:onLevelSelect
geometry.padding: 10 geometry.padding: 10
- $type: haxework.view.HGroupView
skinId: panel
views:
- $type: haxework.view.ButtonView
skinId: button.close
+onPress: $code:switcher.change('start')

View File

@@ -25,8 +25,4 @@ import ru.m.tankz.storage.RecordStorage;
private function onRecordSelect(item:IListItemView<GameRecordInfo>):Void { private function onRecordSelect(item:IListItemView<GameRecordInfo>):Void {
} }
private function close() {
switcher.change(StartFrame.ID);
}
} }

View File

@@ -16,9 +16,3 @@ views:
scroll: scroll:
$type: haxework.view.list.VScrollBarView $type: haxework.view.list.VScrollBarView
skinId: scroll skinId: scroll
- $type: haxework.view.HGroupView
skinId: panel
views:
- $type: haxework.view.ButtonView
skinId: button.close
+onPress: $code:close()

View File

@@ -14,7 +14,6 @@ import ru.m.tankz.view.common.LifeView;
public static var ID(default, never):String = "result"; public static var ID(default, never):String = "result";
@:view("result") var resultView:DataView<PlayerState, LifeView>; @:view("result") var resultView:DataView<PlayerState, LifeView>;
@:view("next") var nextButton:ButtonView;
@:view("level") var levelLabel:LabelView; @:view("level") var levelLabel:LabelView;
@:provide var frames:FrameSwitcher; @:provide var frames:FrameSwitcher;
@@ -37,7 +36,6 @@ import ru.m.tankz.view.common.LifeView;
public function onShow() { public function onShow() {
resultView.data = Lambda.array(state.players); resultView.data = Lambda.array(state.players);
levelLabel.text = 'Level ${state.levelId}'; levelLabel.text = 'Level ${state.levelId}';
nextButton.visible = nextState != null;
} }
private function next() { private function next() {

View File

@@ -14,16 +14,3 @@ views:
$type: haxework.view.layout.VerticalLayout $type: haxework.view.layout.VerticalLayout
hAlign: right hAlign: right
margin: 10 margin: 10
- $type: haxework.view.HGroupView
skinId: panel
views:
- id: close
$type: haxework.view.ButtonView
skinId: button.close
+onPress: $code:close()
- $type: haxework.view.SpriteView
geometry.size.width: 100%
- id: next
$type: haxework.view.ButtonView
skinId: button.next
+onPress: $code:next()

View File

@@ -1,11 +1,7 @@
package ru.m.tankz.view; package ru.m.tankz.view;
import haxework.view.frame.FrameSwitcher;
import haxework.view.VGroupView; import haxework.view.VGroupView;
@:template class SettingsFrame extends VGroupView { @:template class SettingsFrame extends VGroupView {
public static var ID(default, never):String = "settings"; public static var ID(default, never):String = "settings";
@:provide var switcher:FrameSwitcher;
} }

View File

@@ -13,9 +13,3 @@ views:
controlIndex: 0 controlIndex: 0
- $type: ru.m.tankz.view.settings.SettingsEditor - $type: ru.m.tankz.view.settings.SettingsEditor
controlIndex: 1 controlIndex: 1
- $type: haxework.view.HGroupView
skinId: panel
views:
- $type: haxework.view.ButtonView
skinId: button.close
+onPress: $code:switcher.change('start')

View File

@@ -1,12 +1,12 @@
package ru.m.tankz.view; package ru.m.tankz.view;
import haxework.view.ButtonView;
import haxework.view.LabelView;
import haxework.view.frame.FrameSwitcher; import haxework.view.frame.FrameSwitcher;
import haxework.view.VGroupView; import haxework.view.VGroupView;
import ru.m.tankz.game.GameState; import ru.m.tankz.game.GameState;
import ru.m.tankz.network.NetworkManager; import ru.m.tankz.network.NetworkManager;
import ru.m.tankz.Type.GameType; import ru.m.tankz.Type.GameType;
import ru.m.tankz.view.network.GameListFrame;
import ru.m.tankz.view.network.GameRoomFrame;
import ru.m.tankz.view.popup.FontPopup; import ru.m.tankz.view.popup.FontPopup;
import ru.m.tankz.view.popup.LoginPopup; import ru.m.tankz.view.popup.LoginPopup;
@@ -14,54 +14,34 @@ import ru.m.tankz.view.popup.LoginPopup;
public static var ID(default, never):String = "start"; 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 state:GameState;
@:provide var switcher:FrameSwitcher; @:provide var switcher:FrameSwitcher;
@:provide var network:NetworkManager; @:provide var network:NetworkManager;
private var fontPopup:FontPopup; 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 { private function startGame(type:GameType):Void {
state = new GameState(type); state = new GameState(type);
switcher.change(LevelFrame.ID); switcher.change(LevelFrame.ID);
} }
private function login():Void { private function startNetwork():Void {
LoginPopup.instance.show().then(function(user:User):Void { switch network.state {
L.d("Login", 'user: $user'); case ONLINE(user):
}); if (network.game != null) {
switcher.change(GameRoomFrame.ID);
network.joinGame(network.game.id, true);
} else {
switcher.change(GameListFrame.ID);
}
case CONNECTED:
LoginPopup.instance.show().then(function(user):Void {
if (user != null) {
switcher.change(GameListFrame.ID);
}
});
case _:
} }
private function logout():Void {
network.logout();
} }
private function choiceFont():Void { private function choiceFont():Void {

View File

@@ -28,31 +28,9 @@ views:
- id: network - id: network
$type: haxework.view.ButtonView $type: haxework.view.ButtonView
skinId: button skinId: button
+onPress: $code:switcher.change('game_list') +onPress: $code:startNetwork()
text: Network text: Network
disabled: true #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 - $type: haxework.view.LabelView
geometry.hAlign: right geometry.hAlign: right
geometry.vAlign: top geometry.vAlign: top

View File

@@ -17,27 +17,18 @@ import ru.m.tankz.proto.core.GameProto;
public function onShow():Void { public function onShow():Void {
network.listGameSignal.connect(onGameList); network.listGameSignal.connect(onGameList);
network.gameSignal.connect(onGame); network.gameSignal.connect(onGame);
network.stateSignal.connect(onState);
network.listGame(); network.listGame();
} }
public function onHide():Void { public function onHide():Void {
network.listGameSignal.disconnect(onGameList); network.listGameSignal.disconnect(onGameList);
network.gameSignal.disconnect(onGame); network.gameSignal.disconnect(onGame);
network.stateSignal.disconnect(onState);
} }
private function create():Void { private function create():Void {
network.createGame("classic", 0); network.createGame("classic", 0);
} }
private function onState(state:ConnectionState):Void {
switch state {
case ONLINE(_):
case _: switcher.change(StartFrame.ID);
}
}
private function onGameList(data:Array<GameProto>):Void { private function onGameList(data:Array<GameProto>):Void {
games.data = data; games.data = data;
} }
@@ -49,6 +40,6 @@ import ru.m.tankz.proto.core.GameProto;
} }
private function selectGame(game:GameProto):Void { private function selectGame(game:GameProto):Void {
network.joinGame(game.id); network.joinGame(game.id, true);
} }
} }

View File

@@ -19,9 +19,3 @@ views:
factory: $code:ru.m.tankz.view.network.GameItemView.factory factory: $code:ru.m.tankz.view.network.GameItemView.factory
geometry.margin: 10 geometry.margin: 10
+onItemSelect: $code:function(item) selectGame(item.data) +onItemSelect: $code:function(item) selectGame(item.data)
- $type: haxework.view.HGroupView
skinId: panel
views:
- $type: haxework.view.ButtonView
skinId: button.close
+onPress: $code:switcher.change('start')

View File

@@ -30,7 +30,9 @@ import ru.m.tankz.proto.core.PlayerProto;
info.text = '[${game.creator.name}] ${game.type} ${game.level} (${game.players.length})'; info.text = '[${game.creator.name}] ${game.type} ${game.level} (${game.players.length})';
players.data = game.players; players.data = game.players;
if (game.state == GameStateProto.STARTED) { if (game.state == GameStateProto.STARTED) {
if (this.game == null) {
this.game = new NetworkGame(network); this.game = new NetworkGame(network);
}
switcher.change(GameFrame.ID); switcher.change(GameFrame.ID);
} }
} else { } else {
@@ -41,25 +43,13 @@ import ru.m.tankz.proto.core.PlayerProto;
public function onShow():Void { public function onShow():Void {
refresh(network.game); refresh(network.game);
network.gameSignal.connect(onGame); network.gameSignal.connect(onGame);
network.stateSignal.connect(onState);
} }
public function onHide():Void { public function onHide():Void {
network.gameSignal.disconnect(onGame); network.gameSignal.disconnect(onGame);
network.stateSignal.disconnect(onState);
// ToDo:
//network.leaveGame();
} }
private function onGame(game:GameProto):Void { private function onGame(game:GameProto):Void {
refresh(game); refresh(game);
} }
private function onState(state:ConnectionState):Void {
switch state {
case ONLINE(_):
case _: switcher.change(StartFrame.ID);
}
}
} }

View File

@@ -23,9 +23,3 @@ views:
$type: haxework.view.list.VListView $type: haxework.view.list.VListView
geometry.size.stretch: true geometry.size.stretch: true
factory: $code:ru.m.tankz.view.network.PlayerItemView.factory factory: $code:ru.m.tankz.view.network.PlayerItemView.factory
- $type: haxework.view.HGroupView
skinId: panel
views:
- $type: haxework.view.ButtonView
skinId: button.close
+onPress: $code:network.leaveGame()

View File

@@ -38,8 +38,10 @@ class DesktopConnection<O:Message, I:Message> extends BaseConnection<O, I> {
connected = true; connected = true;
reader = Thread.create(_read); reader = Thread.create(_read);
connectDeferred.resolve(this); connectDeferred.resolve(this);
handler.emit(ConnectionEvent.CONNECTED);
} }
} catch (error:Dynamic) { } catch (error:Dynamic) {
handler.emit(ConnectionEvent.ERROR(error));
Timer.delay(function() connectDeferred.throwError(error), 1); Timer.delay(function() connectDeferred.throwError(error), 1);
} }
return connectDeferred.promise(); return connectDeferred.promise();

View File

@@ -49,7 +49,7 @@ class JsConnection<O:Message, I:Message> extends BaseConnection<O, I> {
} }
private function onError(event:Dynamic):Void { private function onError(event:Dynamic):Void {
socket.close(); socket.close(1000);
connected = false; connected = false;
handler.emit(ConnectionEvent.ERROR(event)); handler.emit(ConnectionEvent.ERROR(event));
} }
@@ -67,18 +67,25 @@ class JsConnection<O:Message, I:Message> extends BaseConnection<O, I> {
} }
private function onSocketData(event:Dynamic):Void { private function onSocketData(event:Dynamic):Void {
var packet:I = null;
try { try {
var bytes = Bytes.ofData(event.data); var bytes = Bytes.ofData(event.data);
var packet:I = PacketUtil.fromBytes(bytes, queue.packetClass); packet = PacketUtil.fromBytes(bytes, queue.packetClass);
receive(packet);
} catch (error:Dynamic) { } catch (error:Dynamic) {
handler.emit(ConnectionEvent.ERROR(event)); handler.emit(ConnectionEvent.ERROR(error));
}
if (packet != null) {
receive(packet);
} }
} }
override public function send(packet:O):Void { override public function send(packet:O):Void {
if (connected) {
super.send(packet); super.send(packet);
var bytes = PacketUtil.toBytes(packet); var bytes = PacketUtil.toBytes(packet);
socket.send(bytes.getData()); socket.send(bytes.getData());
} else {
L.w("Connection", "closed");
}
} }
} }

View File

@@ -48,7 +48,7 @@ class EntityBuilder {
var playerConfig = config.getPlayer(playerId); var playerConfig = config.getPlayer(playerId);
var tankConfig = config.getTank(type); var tankConfig = config.getTank(type);
var tank = new Tank(++entityId, buildRect(point, tankConfig.width, tankConfig.height), playerId, tankConfig); var tank = new Tank(++entityId, buildRect(point, tankConfig.width, tankConfig.height), playerId, tankConfig);
tank.color = color == null ? config.getColor(playerId) : color; tank.color = color == null || color.zero ? config.getColor(playerId) : color;
if (!bonusOff) { if (!bonusOff) {
tank.bonus = Math.random() < playerConfig.bonus; tank.bonus = Math.random() < playerConfig.bonus;
} }

View File

@@ -1,11 +1,13 @@
package ru.m.tankz.game; package ru.m.tankz.game;
import ru.m.tankz.core.EntityType.EntityTypeResolver; import ru.m.tankz.core.Bonus;
import ru.m.tankz.core.Entity; import ru.m.tankz.core.Bullet;
import ru.m.tankz.core.Eagle; import ru.m.tankz.core.Eagle;
import ru.m.tankz.core.Entity;
import ru.m.tankz.core.EntityType;
import ru.m.tankz.core.Tank; import ru.m.tankz.core.Tank;
import ru.m.tankz.map.Brick;
import ru.m.tankz.game.GameEvent; import ru.m.tankz.game.GameEvent;
import ru.m.tankz.map.Brick;
import ru.m.tankz.map.LevelMap; import ru.m.tankz.map.LevelMap;
class EventUtil { class EventUtil {
@@ -20,15 +22,27 @@ class EventUtil {
type: item.config.type, type: item.config.type,
} }
}); });
return GameEvent.SPAWN(BRICK(bricks)); return SPAWN(BRICK(bricks));
}
public static function buildCellsDestroyed(map:LevelMap):Array<GameEvent> {
var result = [];
for (brick in map.bricks) {
for (cell in brick.cells) {
if (cell.destroyed) {
result.push(DESTROY(CELL(brick.id, cell.cellX - brick.cellX * 2, cell.cellY - brick.cellY * 2, {tankId: -1})));
}
}
}
return result;
} }
public static function buildEagleSpawn(eagle:Eagle):GameEvent { public static function buildEagleSpawn(eagle:Eagle):GameEvent {
return GameEvent.SPAWN(EAGLE(eagle.id, eagle.rect, eagle.team)); return SPAWN(EAGLE(eagle.id, eagle.rect, eagle.team));
} }
public static function buildTankSpawn(tank:Tank):GameEvent { public static function buildTankSpawn(tank:Tank):GameEvent {
return GameEvent.SPAWN(TANK(tank.id, tank.rect.clone(), tank.playerId, { return SPAWN(TANK(tank.id, tank.rect.clone(), tank.playerId, {
type:tank.config.type, type:tank.config.type,
hits:tank.hits, hits:tank.hits,
bonus:tank.bonus, bonus:tank.bonus,
@@ -36,14 +50,22 @@ class EventUtil {
})); }));
} }
public static function buildBonusSpawn(bonus:Bonus):GameEvent {
return SPAWN(BONUS(bonus.id, bonus.rect.clone(), bonus.config.type));
}
public static function buildBulletSpawn(bullet:Bullet):GameEvent {
return SPAWN(BULLET(bullet.id, bullet.rect.clone(), bullet.playerId, bullet.config.piercing));
}
public static function buildMove(entity:Entity):GameEvent { public static function buildMove(entity:Entity):GameEvent {
return switch EntityTypeResolver.of(entity) { return switch EntityTypeResolver.of(entity) {
case EAGLE(eagle): case EAGLE(eagle):
GameEvent.MOVE(EAGLE(entity.id, entity.rect.position)); MOVE(EAGLE(entity.id, entity.rect.position));
case TANK(tank): case TANK(tank):
GameEvent.MOVE(TANK(entity.id, entity.rect.position)); MOVE(TANK(entity.id, entity.rect.position));
case BULLET(bullet): case BULLET(bullet):
GameEvent.MOVE(BULLET(entity.id, entity.rect.position)); MOVE(BULLET(entity.id, entity.rect.position));
case _: case _:
null; null;
} }

View File

@@ -222,7 +222,7 @@ class GameRunner extends Game implements EngineListener {
} }
var bonus = builder.buildBonus(point, type); var bonus = builder.buildBonus(point, type);
engine.spawn(bonus); engine.spawn(bonus);
gameEventSignal.emit(SPAWN(BONUS(bonus.id, bonus.rect.clone(), bonus.config.type))); gameEventSignal.emit(EventUtil.buildBonusSpawn(bonus));
} }
private inline function alienTank(team:TeamId):Tank->Bool { private inline function alienTank(team:TeamId):Tank->Bool {
@@ -313,7 +313,7 @@ class GameRunner extends Game implements EngineListener {
bullet.tank = tank; bullet.tank = tank;
bullet.move(bullet.rect.direction); bullet.move(bullet.rect.direction);
engine.spawn(bullet); engine.spawn(bullet);
gameEventSignal.emit(SPAWN(BULLET(bullet.id, bullet.rect.clone(), bullet.playerId, bullet.config.piercing))); gameEventSignal.emit(EventUtil.buildBulletSpawn(bullet));
} }
case ACTION(tankId, MOVE(direction)): case ACTION(tankId, MOVE(direction)):
engine.move(tankId, direction); engine.move(tankId, direction);

View File

@@ -46,11 +46,13 @@ message CreateGameResponse {
// Join Game // Join Game
message JoinGameRequest { message JoinGameRequest {
int32 game_id = 1; int32 game_id = 1;
bool restore = 2;
} }
message JoinGameResponse { message JoinGameResponse {
ru.m.tankz.proto.core.GameProto game = 1; ru.m.tankz.proto.core.GameProto game = 1;
ru.m.tankz.proto.core.UserProto user = 2; ru.m.tankz.proto.core.UserProto user = 2;
repeated string events = 3;
} }
// Leave Game // Leave Game

View File

@@ -62,6 +62,9 @@ class _GameListener implements GameListener {
var game = new ServerGame(proto); var game = new ServerGame(proto);
game.joinUser(user); game.joinUser(user);
games.push(game); games.push(game);
/*if (gamesByCreator.exists(user.uuid)) {
delete(gamesByCreator[user.uuid].proto.id);
} else*/
gamesById[game.proto.id] = game; gamesById[game.proto.id] = game;
gamesByCreator[game.proto.creator.uuid] = game; gamesByCreator[game.proto.creator.uuid] = game;
createSignal.emit(game); createSignal.emit(game);
@@ -88,9 +91,9 @@ class _GameListener implements GameListener {
} }
public function leave(user:UserProto):Void { public function leave(user:UserProto):Void {
if (gamesByCreator.exists(user.uuid)) { /*if (gamesByCreator.exists(user.uuid)) {
delete(gamesByCreator[user.uuid].proto.id); delete(gamesByCreator[user.uuid].proto.id);
} else if (gamesByUser.exists(user.uuid)) { } else*/ if (gamesByUser.exists(user.uuid)) {
var game = gamesByUser[user.uuid]; var game = gamesByUser[user.uuid];
game.leaveUser(user); game.leaveUser(user);
changeSignal.emit(game, LEAVE(user)); changeSignal.emit(game, LEAVE(user));

View File

@@ -1,12 +1,16 @@
package ru.m.tankz.server.game; package ru.m.tankz.server.game;
import ru.m.tankz.config.Config; import ru.m.tankz.config.Config;
import ru.m.tankz.core.EntityType;
import ru.m.tankz.game.EventUtil;
import ru.m.tankz.game.GameEvent;
import ru.m.tankz.game.GameRunner; import ru.m.tankz.game.GameRunner;
import ru.m.tankz.game.GameState; import ru.m.tankz.game.GameState;
import ru.m.tankz.proto.core.GameProto; import ru.m.tankz.proto.core.GameProto;
import ru.m.tankz.proto.core.PlayerProto; import ru.m.tankz.proto.core.PlayerProto;
import ru.m.tankz.proto.core.UserProto; import ru.m.tankz.proto.core.UserProto;
import ru.m.tankz.server.control.ServerControlFactory; import ru.m.tankz.server.control.ServerControlFactory;
import ru.m.tankz.Type.PlayerId;
class ServerGame extends GameRunner { class ServerGame extends GameRunner {
@@ -19,10 +23,12 @@ class ServerGame extends GameRunner {
this.proto = proto; this.proto = proto;
} }
public function joinUser(user:UserProto):Void { public function joinUser(user:UserProto, playerId:PlayerId = null):Void {
var index = proto.players.length; leaveUser(user);
var teamId = "human"; // ToDo: if (playerId == null) {
proto.players.push(new PlayerProto().setUser(user).setTeam(teamId).setIndex(index)); playerId = ["human", proto.players.length];
}
proto.players.push(new PlayerProto().setUser(user).setTeam(playerId.team).setIndex(playerId.index));
} }
public function leaveUser(user:UserProto):Void { public function leaveUser(user:UserProto):Void {
@@ -36,4 +42,21 @@ class ServerGame extends GameRunner {
}); });
super.start(); super.start();
} }
public function restore():Array<GameEvent> {
var result = [];
result.push(EventUtil.buildBricksSpawn(engine.map));
result = result.concat(EventUtil.buildCellsDestroyed(engine.map));
for (entity in engine.entities) {
switch EntityTypeResolver.of(entity) {
case EAGLE(eagle): result.push(EventUtil.buildEagleSpawn(eagle));
case TANK(tank): result.push(EventUtil.buildTankSpawn(tank));
case BONUS(bonus): result.push(EventUtil.buildBonusSpawn(bonus));
case BULLET(bullet): result.push(EventUtil.buildBulletSpawn(bullet));
case CELL(_):
}
}
result.push(START(state));
return result;
}
} }

View File

@@ -1,5 +1,6 @@
package ru.m.tankz.server.session; package ru.m.tankz.server.session;
import ru.m.tankz.proto.core.GameStateProto;
import haxe.Unserializer; import haxe.Unserializer;
import haxe.Serializer; import haxe.Serializer;
import ru.m.tankz.proto.pack.GameEventResponse; import ru.m.tankz.proto.pack.GameEventResponse;
@@ -97,6 +98,12 @@ class GameSession extends ProtoSession<Response, Request> implements GameManager
} else if (request.hasJoinGame()) { } else if (request.hasJoinGame()) {
gameId = request.joinGame.gameId; gameId = request.joinGame.gameId;
gameManager.join(request.joinGame.gameId, user); gameManager.join(request.joinGame.gameId, user);
var game = gameManager.gamesById[gameId];
if (request.joinGame.restore && game.proto.state == GameStateProto.STARTED) {
for (event in game.restore()) {
send(new Response().setGameEvent(new GameEventResponse().setTime(0).setEvent(Serializer.run(event))));
}
}
// leave // leave
} else if (request.hasLeaveGame()) { } else if (request.hasLeaveGame()) {
gameManager.leave(user); gameManager.leave(user);