[proto] add rooms and slots

This commit is contained in:
2019-06-04 12:13:27 +03:00
parent 4c8ae66624
commit 78bbf5264e
25 changed files with 405 additions and 403 deletions

View File

@@ -3,9 +3,10 @@
"protoFiles": [ "protoFiles": [
"src/common/proto/core.proto", "src/common/proto/core.proto",
"src/common/proto/game.proto", "src/common/proto/game.proto",
"src/common/proto/room.proto",
"src/common/proto/pack.proto" "src/common/proto/pack.proto"
], ],
"cleanOut": true, "cleanOut": true,
"haxeOut": "src-gen/haxe", "haxeOut": "src-gen/haxe",
"javaOut": null "javaOut": null
} }

View File

@@ -7,13 +7,14 @@ import ru.m.tankz.game.GameEvent;
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.proto.pack.GameEventResponse; import ru.m.tankz.proto.pack.GameEventResponse;
import ru.m.tankz.proto.room.RoomSlotProto;
class NetworkGame extends Game { class NetworkGame extends Game {
private var network:NetworkManager; private var network:NetworkManager;
public function new(network:NetworkManager) { public function new(network:NetworkManager) {
super(new GameState(network.game.type, 0, network.game.level)); super(new GameState(network.room.game.type, 0, network.room.game.level));
this.network = network; this.network = network;
this.controlFactory = new NetworkControlFactory(); this.controlFactory = new NetworkControlFactory();
network.gameEventSignal.connect(onGameEventProto); network.gameEventSignal.connect(onGameEventProto);
@@ -30,17 +31,17 @@ class NetworkGame extends Game {
private function onConnectionState(state:ConnectionState):Void { private function onConnectionState(state:ConnectionState):Void {
switch state { switch state {
case ONLINE(user): case ONLINE(user):
if (network.game != null) { if (network.room != null) {
network.joinGame(network.game.id); network.joinGame(network.room.game.id);
} }
case _: 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 slot:RoomSlotProto = Lambda.find(network.room.slots, function(slot:RoomSlotProto) return slot.hasUser() && slot.user.uuid == network.user.uuid);
if (player != null) { if (slot != null) {
state.controls.push({playerId: [player.team, player.index], control: "human-0"}); state.controls.push({playerId: [slot.slot.team, slot.slot.index], control: "human-0"});
} }
super.start(); super.start();
} }

View File

@@ -1,24 +1,26 @@
package ru.m.tankz.network; package ru.m.tankz.network;
import ru.m.tankz.proto.room.SlotProto;
import ru.m.tankz.proto.room.SlotRequest;
import haxe.Serializer; import haxe.Serializer;
import haxework.signal.Signal; import haxework.signal.Signal;
import ru.m.connect.IConnection; import ru.m.connect.IConnection;
import ru.m.tankz.control.Control; import ru.m.tankz.control.Control;
import ru.m.tankz.game.GameEvent; import ru.m.tankz.game.GameEvent;
import ru.m.tankz.proto.core.GameProto;
import ru.m.tankz.proto.core.UserProto; import ru.m.tankz.proto.core.UserProto;
import ru.m.tankz.proto.game.GameChangeProto;
import ru.m.tankz.proto.pack.CreateGameRequest;
import ru.m.tankz.proto.pack.GameEventRequest; import ru.m.tankz.proto.pack.GameEventRequest;
import ru.m.tankz.proto.pack.GameEventResponse; import ru.m.tankz.proto.pack.GameEventResponse;
import ru.m.tankz.proto.pack.JoinGameRequest;
import ru.m.tankz.proto.pack.LeaveGameRequest;
import ru.m.tankz.proto.pack.ListGameRequest;
import ru.m.tankz.proto.pack.LoginRequest; import ru.m.tankz.proto.pack.LoginRequest;
import ru.m.tankz.proto.pack.LogoutRequest; import ru.m.tankz.proto.pack.LogoutRequest;
import ru.m.tankz.proto.pack.Request; import ru.m.tankz.proto.pack.Request;
import ru.m.tankz.proto.pack.Response; import ru.m.tankz.proto.pack.Response;
import ru.m.tankz.proto.pack.StartGameRequest; import ru.m.tankz.proto.room.CreateRequest;
import ru.m.tankz.proto.room.JoinRequest;
import ru.m.tankz.proto.room.LeaveRequest;
import ru.m.tankz.proto.room.RoomListRequest;
import ru.m.tankz.proto.room.RoomProto;
import ru.m.tankz.proto.room.RoomRequest;
import ru.m.tankz.proto.room.StartRequest;
import ru.m.tankz.storage.MultiplayerStorage; import ru.m.tankz.storage.MultiplayerStorage;
typedef ClientConnection = IConnection<Request, Response>; typedef ClientConnection = IConnection<Request, Response>;
@@ -35,13 +37,12 @@ enum ConnectionState {
class NetworkManager { class NetworkManager {
public var state(default, null):ConnectionState; public var state(default, null):ConnectionState;
public var game(default, null):GameProto; public var room(default, null):RoomProto;
public var user(default, null):UserProto; public var user(default, null):UserProto;
public var stateSignal:Signal<ConnectionState>; public var stateSignal:Signal<ConnectionState>;
public var listGameSignal:Signal<Array<GameProto>>; public var listRoomSignal:Signal<Array<RoomProto>>;
public var gameSignal:Signal<GameProto>; public var roomSignal:Signal<RoomProto>;
public var gameUpdateSignal:Signal<Array<GameChangeProto>>;
public var gameEventSignal:Signal<GameEventResponse>; public var gameEventSignal:Signal<GameEventResponse>;
@:provide private var connection:ClientConnection; @:provide private var connection:ClientConnection;
@@ -53,9 +54,8 @@ class NetworkManager {
public function new() { public function new() {
reconnectDelay = 500; reconnectDelay = 500;
stateSignal = new Signal(); stateSignal = new Signal();
listGameSignal = new Signal(); listRoomSignal = new Signal();
gameSignal = new Signal(); roomSignal = new Signal();
gameUpdateSignal = new Signal();
gameEventSignal = new Signal(); gameEventSignal = new Signal();
updateState(OFFLINE); updateState(OFFLINE);
connection.handler.connect(onConnectionEvent); connection.handler.connect(onConnectionEvent);
@@ -81,24 +81,28 @@ class NetworkManager {
connection.send(new Request().setLogout(new LogoutRequest())); connection.send(new Request().setLogout(new LogoutRequest()));
} }
public function listGame():Void { public function listGame(subscribe:Bool):Void {
connection.send(new Request().setListGame(new ListGameRequest())); connection.send(new Request().setRoomList(new RoomListRequest().setSubscribe(subscribe)));
} }
public function createGame(type:String, level:Int):Void { public function createGame(type:String, level:Int):Void {
connection.send(new Request().setCreateGame(new CreateGameRequest().setType(type).setLevel(level))); connection.send(new Request().setRoom(new RoomRequest().setCreate(new CreateRequest().setType(type).setLevel(level))));
} }
public function joinGame(gameId:Int, restore:Bool = false):Void { public function joinGame(gameId:Int, restore:Bool = false):Void {
connection.send(new Request().setJoinGame(new JoinGameRequest().setGameId(gameId).setRestore(restore))); connection.send(new Request().setRoom(new RoomRequest().setJoin(new JoinRequest().setGameId(gameId))));
}
public function selectSlot(slot:SlotProto):Void {
connection.send(new Request().setRoom(new RoomRequest().setSlot(new SlotRequest().setSlot(slot))));
} }
public function leaveGame():Void { public function leaveGame():Void {
connection.send(new Request().setLeaveGame(new LeaveGameRequest())); connection.send(new Request().setRoom(new RoomRequest().setLeave(new LeaveRequest())));
} }
public function startGame():Void { public function startGame():Void {
connection.send(new Request().setStartGame(new StartGameRequest())); connection.send(new Request().setRoom(new RoomRequest().setStart(new StartRequest())));
} }
public function action(tankId:Int, action:TankAction):Void { public function action(tankId:Int, action:TankAction):Void {
@@ -154,25 +158,11 @@ class NetworkManager {
} else if (packet.hasLogout()) { } else if (packet.hasLogout()) {
storage.user = null; storage.user = null;
updateState(CONNECTED); updateState(CONNECTED);
} else if (packet.hasListGame()) { } else if (packet.hasRoomList()) {
listGameSignal.emit(packet.listGame.games); listRoomSignal.emit(packet.roomList.rooms);
} else if (packet.hasCreateGame()) { } else if (packet.hasRoom()) {
game = packet.createGame.game; room = packet.room.room;
gameSignal.emit(game); roomSignal.emit(room);
} else if (packet.hasJoinGame()) {
game = packet.joinGame.game;
gameSignal.emit(game);
} else if (packet.hasLeaveGame()) {
if (packet.leaveGame.user.uuid == user.uuid) {
game = null;
gameSignal.emit(null);
} else {
game = packet.leaveGame.game;
gameSignal.emit(game);
}
} else if (packet.hasStartGame()) {
game = packet.startGame.game;
gameSignal.emit(game);
} else if (packet.hasGameEvent()) { } else if (packet.hasGameEvent()) {
gameEventSignal.emit(packet.gameEvent); gameEventSignal.emit(packet.gameEvent);
} }

View File

@@ -17,10 +17,10 @@ views:
$type: ru.m.tankz.view.SettingsFrame $type: ru.m.tankz.view.SettingsFrame
- id: record - id: record
$type: ru.m.tankz.view.RecordFrame $type: ru.m.tankz.view.RecordFrame
- id: game_list - id: room_list
$type: ru.m.tankz.view.network.GameListFrame $type: ru.m.tankz.view.network.RoomListFrame
- id: game_room - id: room
$type: ru.m.tankz.view.network.GameRoomFrame $type: ru.m.tankz.view.network.RoomFrame
- $type: haxework.view.HGroupView - $type: haxework.view.HGroupView
skinId: panel skinId: panel
layout.margin: 10 layout.margin: 10

View File

@@ -5,8 +5,8 @@ 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.RoomListFrame;
import ru.m.tankz.view.network.GameRoomFrame; import ru.m.tankz.view.network.RoomFrame;
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;
@@ -28,16 +28,16 @@ import ru.m.tankz.view.popup.LoginPopup;
private function startNetwork():Void { private function startNetwork():Void {
switch network.state { switch network.state {
case ONLINE(user): case ONLINE(user):
if (network.game != null) { if (network.room != null) {
switcher.change(GameRoomFrame.ID); switcher.change(RoomFrame.ID);
network.joinGame(network.game.id, true); network.joinGame(network.room.game.id, true);
} else { } else {
switcher.change(GameListFrame.ID); switcher.change(RoomListFrame.ID);
} }
case CONNECTED: case CONNECTED:
LoginPopup.instance.show().then(function(user):Void { LoginPopup.instance.show().then(function(user):Void {
if (user != null) { if (user != null) {
switcher.change(GameListFrame.ID); switcher.change(RoomListFrame.ID);
} }
}); });
case _: case _:

View File

@@ -1,24 +0,0 @@
package ru.m.tankz.view.network;
import haxework.view.LabelView;
import haxework.view.HGroupView;
import haxework.view.list.ListView;
import ru.m.tankz.proto.core.GameProto;
@:template class GameItemView extends HGroupView implements IListItemView<GameProto> {
public var item_index(default, default):Int;
public var data(default, set):GameProto;
@:view var label:LabelView;
private function set_data(value:GameProto):GameProto {
data = value;
label.text = '[${value.creator.name}] ${value.type} ${value.level} (${value.players.length})';
return data;
}
public static function factory():GameItemView {
return new GameItemView();
}
}

View File

@@ -1,45 +0,0 @@
package ru.m.tankz.view.network;
import haxework.view.frame.FrameSwitcher;
import haxework.view.list.VListView;
import haxework.view.VGroupView;
import ru.m.tankz.network.NetworkManager;
import ru.m.tankz.proto.core.GameProto;
@:template class GameListFrame extends VGroupView {
public static inline var ID = "game_list";
@:view var games:VListView<GameProto>;
@:provide var switcher:FrameSwitcher;
@:provide var network:NetworkManager;
public function onShow():Void {
network.listGameSignal.connect(onGameList);
network.gameSignal.connect(onGame);
network.listGame();
}
public function onHide():Void {
network.listGameSignal.disconnect(onGameList);
network.gameSignal.disconnect(onGame);
}
private function create():Void {
network.createGame("classic", 0);
}
private function onGameList(data:Array<GameProto>):Void {
games.data = data;
}
private function onGame(game:GameProto):Void {
if (game != null) {
switcher.change(GameRoomFrame.ID);
}
}
private function selectGame(game:GameProto):Void {
network.joinGame(game.id, true);
}
}

View File

@@ -1,55 +0,0 @@
package ru.m.tankz.view.network;
import haxework.view.ButtonView;
import haxework.view.frame.FrameSwitcher;
import haxework.view.list.VListView;
import haxework.view.TextView;
import haxework.view.VGroupView;
import ru.m.tankz.game.IGame;
import ru.m.tankz.game.NetworkGame;
import ru.m.tankz.network.NetworkManager;
import ru.m.tankz.proto.core.GameProto;
import ru.m.tankz.proto.core.GameStateProto;
import ru.m.tankz.proto.core.PlayerProto;
@:template class GameRoomFrame extends VGroupView {
public static inline var ID = "game_room";
@:view var start:ButtonView;
@:view var info:TextView;
@:view var players:VListView<PlayerProto>;
@:provide var switcher:FrameSwitcher;
@:provide var network:NetworkManager;
@:provide var game:IGame;
private function refresh(game:GameProto):Void {
if (game != null) {
start.visible = game.creator.uuid == network.user.uuid;
info.text = '[${game.creator.name}] ${game.type} ${game.level} (${game.players.length})';
players.data = game.players;
if (game.state == GameStateProto.STARTED) {
if (this.game == null) {
this.game = new NetworkGame(network);
}
switcher.change(GameFrame.ID);
}
} else {
Timer.delay(function() switcher.change(GameListFrame.ID), 1);
}
}
public function onShow():Void {
refresh(network.game);
network.gameSignal.connect(onGame);
}
public function onHide():Void {
network.gameSignal.disconnect(onGame);
}
private function onGame(game:GameProto):Void {
refresh(game);
}
}

View File

@@ -1,24 +0,0 @@
package ru.m.tankz.view.network;
import haxework.view.HGroupView;
import haxework.view.LabelView;
import haxework.view.list.ListView;
import ru.m.tankz.proto.core.PlayerProto;
@:template class PlayerItemView extends HGroupView implements IListItemView<PlayerProto> {
public var item_index(default, default):Int;
public var data(default, set):PlayerProto;
@:view var label:LabelView;
private function set_data(value:PlayerProto):PlayerProto {
data = value;
label.text = '${value.user.name}';
return data;
}
public static function factory():PlayerItemView {
return new PlayerItemView();
}
}

View File

@@ -0,0 +1,54 @@
package ru.m.tankz.view.network;
import haxework.view.ButtonView;
import haxework.view.frame.FrameSwitcher;
import haxework.view.list.VListView;
import haxework.view.TextView;
import haxework.view.VGroupView;
import ru.m.tankz.game.IGame;
import ru.m.tankz.game.NetworkGame;
import ru.m.tankz.network.NetworkManager;
import ru.m.tankz.proto.room.RoomProto;
import ru.m.tankz.proto.room.RoomSlotProto;
@:template class RoomFrame extends VGroupView {
public static inline var ID = "room";
@:view var start:ButtonView;
@:view var info:TextView;
@:view var slots:VListView<RoomSlotProto>;
@:provide var switcher:FrameSwitcher;
@:provide var network:NetworkManager;
@:provide var game:IGame;
private function refresh(room:RoomProto):Void {
if (room != null) {
start.visible = room.creator.uuid == network.user.uuid;
info.text = '[${room.creator.name}] ${room.game.type} ${room.game.level} (${room.users.length})';
slots.data = room.slots;
if (room.game.started) {
if (this.game == null) {
this.game = new NetworkGame(network);
}
switcher.change(GameFrame.ID);
}
} else {
Timer.delay(function() switcher.change(RoomListFrame.ID), 1);
}
}
private function selectSlot(slot:RoomSlotProto):Void {
network.selectSlot(slot.slot);
}
public function onShow():Void {
refresh(network.room);
network.roomSignal.connect(refresh);
}
public function onHide():Void {
network.roomSignal.disconnect(refresh);
}
}

View File

@@ -19,7 +19,8 @@ views:
$type: haxework.view.LabelView $type: haxework.view.LabelView
geometry.size.width: 100% geometry.size.width: 100%
skinId: text.box skinId: text.box
- id: players - id: slots
$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.SlotItemView.factory
+onItemSelect: $code:function(item) selectSlot(item.data)

View File

@@ -0,0 +1,24 @@
package ru.m.tankz.view.network;
import haxework.view.HGroupView;
import haxework.view.LabelView;
import haxework.view.list.ListView;
import ru.m.tankz.proto.room.RoomProto;
@:template class RoomItemView extends HGroupView implements IListItemView<RoomProto> {
public var item_index(default, default):Int;
public var data(default, set):RoomProto;
@:view var label:LabelView;
private function set_data(value:RoomProto):RoomProto {
data = value;
label.text = '[${value.creator.name}] ${value.game.type} ${value.game.level} (${value.users.length})';
return data;
}
public static function factory():RoomItemView {
return new RoomItemView();
}
}

View File

@@ -0,0 +1,47 @@
package ru.m.tankz.view.network;
import haxework.view.frame.FrameSwitcher;
import haxework.view.list.VListView;
import haxework.view.VGroupView;
import ru.m.tankz.network.NetworkManager;
import ru.m.tankz.proto.room.RoomProto;
@:template class RoomListFrame extends VGroupView {
public static inline var ID = "room_list";
@:view var games:VListView<RoomProto>;
@:provide var switcher:FrameSwitcher;
@:provide var network:NetworkManager;
public function onShow():Void {
network.listRoomSignal.connect(onRoomList);
network.roomSignal.connect(onRoom);
network.listGame(true);
}
public function onHide():Void {
network.listRoomSignal.disconnect(onRoomList);
network.roomSignal.disconnect(onRoom);
network.listGame(false);
}
private function create():Void {
network.createGame("classic", 0);
}
private function onRoomList(data:Array<RoomProto>):Void {
games.data = data;
}
private function onRoom(room:RoomProto):Void {
if (room != null) {
switcher.change(RoomFrame.ID);
}
}
private function selectRoom(room:RoomProto):Void {
network.joinGame(room.game.id, true);
}
}

View File

@@ -16,6 +16,6 @@ views:
- id: games - id: games
$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.GameItemView.factory factory: $code:ru.m.tankz.view.network.RoomItemView.factory
geometry.margin: 10 geometry.margin: 10
+onItemSelect: $code:function(item) selectGame(item.data) +onItemSelect: $code:function(item) selectRoom(item.data)

View File

@@ -0,0 +1,24 @@
package ru.m.tankz.view.network;
import haxework.view.HGroupView;
import haxework.view.LabelView;
import haxework.view.list.ListView;
import ru.m.tankz.proto.room.RoomSlotProto;
@:template class SlotItemView extends HGroupView implements IListItemView<RoomSlotProto> {
public var item_index(default, default):Int;
public var data(default, set):RoomSlotProto;
@:view var label:LabelView;
private function set_data(value:RoomSlotProto):RoomSlotProto {
data = value;
label.text = '${value.slot.team}-${value.slot.index} ${value.hasUser() ? value.user.name : '(NONE)'}';
return data;
}
public static function factory():SlotItemView {
return new SlotItemView();
}
}

View File

@@ -7,23 +7,9 @@ message UserProto {
string name = 2; string name = 2;
} }
enum GameStateProto {
READY = 0;
STARTED = 1;
ENDED = 2;
}
message PlayerProto {
UserProto user = 1;
string team = 2;
int32 index = 3;
}
message GameProto { message GameProto {
int32 id = 1; int32 id = 1;
string type = 2; string type = 2;
int32 level = 3; int32 level = 3;
UserProto creator = 4; bool started = 4;
repeated PlayerProto players = 5;
GameStateProto state = 6;
} }

View File

@@ -1,28 +1,3 @@
syntax = "proto3"; syntax = "proto3";
package ru.m.tankz.proto.game; package ru.m.tankz.proto.game;
enum GameActionTypeProto {
MOVE = 0;
SHOT = 1;
STOP = 2;
}
enum GameChangeTypeProto {
MOVED = 0;
DESTROED = 1;
MODIFIED = 2;
APPEND = 3;
DIRECTION = 4;
}
message GameChangeProto {
GameChangeTypeProto type = 1;
string entityType = 2;
int32 entityId = 3;
float x = 4;
float y = 5;
int32 directionX = 6;
int32 directionY = 7;
}

View File

@@ -2,16 +2,15 @@ syntax = "proto3";
import "core.proto"; import "core.proto";
import "game.proto"; import "game.proto";
import "room.proto";
package ru.m.tankz.proto.pack; package ru.m.tankz.proto.pack;
message ErrorResponse { message ErrorResponse {
int32 code = 1; int32 code = 1;
string message = 2; string message = 2;
} }
// Login
message LoginRequest { message LoginRequest {
string uuid = 1; string uuid = 1;
string name = 2; string name = 2;
@@ -21,56 +20,10 @@ message LoginResponse {
ru.m.tankz.proto.core.UserProto user = 1; ru.m.tankz.proto.core.UserProto user = 1;
} }
// Logout
message LogoutRequest {} message LogoutRequest {}
message LogoutResponse {} message LogoutResponse {}
// List Game
message ListGameRequest {}
message ListGameResponse {
repeated ru.m.tankz.proto.core.GameProto games = 1;
}
// Create Game
message CreateGameRequest {
string type = 1;
int32 level = 2;
}
message CreateGameResponse {
ru.m.tankz.proto.core.GameProto game = 1;
}
// Join Game
message JoinGameRequest {
int32 game_id = 1;
bool restore = 2;
}
message JoinGameResponse {
ru.m.tankz.proto.core.GameProto game = 1;
ru.m.tankz.proto.core.UserProto user = 2;
repeated string events = 3;
}
// Leave Game
message LeaveGameRequest {}
message LeaveGameResponse {
ru.m.tankz.proto.core.GameProto game = 1;
ru.m.tankz.proto.core.UserProto user = 2;
}
// Start Game
message StartGameRequest {}
message StartGameResponse {
ru.m.tankz.proto.core.GameProto game = 1;
}
// Game
message GameEventRequest { message GameEventRequest {
int32 time = 1; int32 time = 1;
string event = 2; string event = 2;
@@ -81,31 +34,23 @@ message GameEventResponse {
string event = 2; string event = 2;
} }
// Request
message Request { message Request {
oneof content { oneof content {
LoginRequest login = 1; LoginRequest login = 1;
LogoutRequest logout = 2; LogoutRequest logout = 2;
ListGameRequest listGame = 3; ru.m.tankz.proto.room.RoomRequest room = 3;
CreateGameRequest createGame = 4; ru.m.tankz.proto.room.RoomListRequest roomList = 4;
JoinGameRequest joinGame = 5; GameEventRequest gameEvent = 6;
LeaveGameRequest leaveGame = 6;
StartGameRequest startGame = 7;
GameEventRequest gameEvent = 8;
} }
} }
// Response
message Response { message Response {
oneof content { oneof content {
LoginResponse login = 1; LoginResponse login = 1;
LogoutResponse logout = 2; LogoutResponse logout = 2;
ListGameResponse listGame = 3; ru.m.tankz.proto.room.RoomResponse room = 3;
CreateGameResponse createGame = 4; ru.m.tankz.proto.room.RoomListResponse roomList = 4;
JoinGameResponse joinGame = 5; GameEventResponse gameEvent = 6;
LeaveGameResponse leaveGame = 6;
StartGameResponse startGame = 7;
GameEventResponse gameEvent = 8;
ErrorResponse error = 999; ErrorResponse error = 999;
} }

View File

@@ -0,0 +1,63 @@
syntax = "proto3";
import "core.proto";
package ru.m.tankz.proto.room;
message SlotProto {
string team = 3;
int32 index = 4;
}
message RoomSlotProto {
SlotProto slot = 1;
ru.m.tankz.proto.core.UserProto user = 2;
}
message RoomProto {
ru.m.tankz.proto.core.GameProto game = 1;
ru.m.tankz.proto.core.UserProto creator = 2;
repeated ru.m.tankz.proto.core.UserProto users = 3;
repeated RoomSlotProto slots = 4;
}
message CreateRequest {
string type = 2;
int32 level = 3;
}
message JoinRequest {
int32 gameId = 1;
}
message LeaveRequest {
}
message SlotRequest {
SlotProto slot = 3;
}
message StartRequest {
}
message RoomRequest {
oneof content {
CreateRequest create = 1;
JoinRequest join = 2;
LeaveRequest leave = 3;
SlotRequest slot = 4;
StartRequest start = 5;
}
}
message RoomResponse {
RoomProto room = 1;
}
message RoomListRequest {
bool subscribe = 1;
}
message RoomListResponse {
repeated RoomProto rooms = 1;
}

View File

@@ -1,10 +1,13 @@
package ru.m.tankz.server.game; package ru.m.tankz.server.game;
import ru.m.tankz.config.Config.PlayerControl;
import ru.m.tankz.proto.room.RoomSlotProto;
import ru.m.tankz.proto.room.SlotProto;
import ru.m.tankz.proto.room.RoomProto;
import ru.m.tankz.game.GameEvent; import ru.m.tankz.game.GameEvent;
import ru.m.tankz.game.IGame.GameListener; import ru.m.tankz.game.IGame.GameListener;
import ru.m.tankz.game.IGame; import ru.m.tankz.game.IGame;
import ru.m.tankz.proto.core.GameProto; import ru.m.tankz.proto.core.GameProto;
import ru.m.tankz.proto.core.GameStateProto;
import ru.m.tankz.proto.core.UserProto; import ru.m.tankz.proto.core.UserProto;
import ru.m.tankz.server.game.IGameManager; import ru.m.tankz.server.game.IGameManager;
@@ -21,7 +24,7 @@ class _GameListener implements GameListener {
dispatcher.dispatchEvent(game, event); dispatcher.dispatchEvent(game, event);
switch event { switch event {
case COMPLETE(_, _): case COMPLETE(_, _):
dispatcher.delete(game.proto.id); dispatcher.delete(game.id);
dispose(); dispose();
case _: case _:
} }
@@ -52,29 +55,36 @@ class _GameListener implements GameListener {
public function create(user:UserProto, type:String, level:Int):ServerGame { public function create(user:UserProto, type:String, level:Int):ServerGame {
if (gamesByCreator.exists(user.uuid)) { if (gamesByCreator.exists(user.uuid)) {
delete(gamesByCreator[user.uuid].proto.id); delete(gamesByCreator[user.uuid].id);
} }
var proto = new GameProto() var room = new RoomProto()
.setId(++counter) .setGame(
.setCreator(user) new GameProto()
.setType(type) .setId(++counter)
.setLevel(level); .setType(type)
var game = new ServerGame(proto); .setLevel(level)
game.joinUser(user); )
.setCreator(user);
var game = new ServerGame(room);
var slots:Array<RoomSlotProto> = [];
for (team in game.state.teams) {
for (player in team.players) {
slots.push(new RoomSlotProto().setSlot(new SlotProto().setTeam(player.id.team).setIndex(player.id.index)));
}
}
game.room.setSlots(slots);
games.push(game); games.push(game);
/*if (gamesByCreator.exists(user.uuid)) { gamesById[game.id] = game;
delete(gamesByCreator[user.uuid].proto.id); gamesByCreator[game.room.creator.uuid] = game;
} else*/
gamesById[game.proto.id] = game;
gamesByCreator[game.proto.creator.uuid] = game;
createSignal.emit(game); createSignal.emit(game);
join(game.id, user);
return game; return game;
} }
public function join(gameId:Int, user:UserProto):Void { public function join(gameId:Int, user:UserProto):Void {
if (gamesById.exists(gameId)) { if (gamesById.exists(gameId)) {
var game = gamesById[gameId]; var game = gamesById[gameId];
game.joinUser(user); game.join(user);
gamesByUser[user.uuid] = game; gamesByUser[user.uuid] = game;
changeSignal.emit(game, JOIN(user)); changeSignal.emit(game, JOIN(user));
} }
@@ -84,8 +94,8 @@ class _GameListener implements GameListener {
if (gamesById.exists(gameId)) { if (gamesById.exists(gameId)) {
var game = gamesById[gameId]; var game = gamesById[gameId];
games.remove(game); games.remove(game);
gamesById.remove(game.proto.id); gamesById.remove(game.id);
gamesByCreator.remove(game.proto.creator.uuid); gamesByCreator.remove(game.room.creator.uuid);
deleteSignal.emit(game); deleteSignal.emit(game);
} }
} }
@@ -95,15 +105,24 @@ class _GameListener implements GameListener {
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); gamesByUser.remove(user.uuid);
game.leave(user);
changeSignal.emit(game, LEAVE(user)); changeSignal.emit(game, LEAVE(user));
} }
} }
public function slot(user:UserProto, slot:SlotProto):Void {
if (gamesByUser.exists(user.uuid)) {
var game = gamesByUser[user.uuid];
game.slot(user, slot);
changeSignal.emit(game, SLOT(user, slot));
}
}
public function start(gameId:Int):Void { public function start(gameId:Int):Void {
if (gamesById.exists(gameId)) { if (gamesById.exists(gameId)) {
var game:ServerGame = gamesById[gameId]; var game:ServerGame = gamesById[gameId];
game.proto.setState(GameStateProto.STARTED); game.room.game.setStarted(true);
changeSignal.emit(game, START); changeSignal.emit(game, START);
game.connect(new _GameListener(game, this)); game.connect(new _GameListener(game, this));
game.start(); game.start();

View File

@@ -1,12 +1,14 @@
package ru.m.tankz.server.game; package ru.m.tankz.server.game;
import ru.m.tankz.game.GameEvent;
import haxework.signal.Signal; import haxework.signal.Signal;
import ru.m.tankz.game.GameEvent;
import ru.m.tankz.proto.core.UserProto; import ru.m.tankz.proto.core.UserProto;
import ru.m.tankz.proto.room.SlotProto;
enum GameChange { enum GameChange {
JOIN(user:UserProto); JOIN(user:UserProto);
LEAVE(user:UserProto); LEAVE(user:UserProto);
SLOT(user:UserProto, slot:SlotProto);
START(); START();
} }
@@ -35,6 +37,7 @@ interface IGameManager {
public function create(user:UserProto, type:String, level:Int):ServerGame; public function create(user:UserProto, type:String, level:Int):ServerGame;
public function delete(gameId:Int):Void; public function delete(gameId:Int):Void;
public function join(gameId:Int, user:UserProto):Void; public function join(gameId:Int, user:UserProto):Void;
public function slot(user:UserProto, slot:SlotProto):Void;
public function leave(user:UserProto):Void; public function leave(user:UserProto):Void;
public function start(gameId:Int):Void; public function start(gameId:Int):Void;
} }

View File

@@ -1,44 +1,69 @@
package ru.m.tankz.server.game; package ru.m.tankz.server.game;
import ru.m.tankz.proto.room.RoomSlotProto;
import ru.m.tankz.config.Config; import ru.m.tankz.config.Config;
import ru.m.tankz.core.EntityType; import ru.m.tankz.core.EntityType;
import ru.m.tankz.game.EventUtil; import ru.m.tankz.game.EventUtil;
import ru.m.tankz.game.GameEvent; 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.PlayerProto;
import ru.m.tankz.proto.core.UserProto; import ru.m.tankz.proto.core.UserProto;
import ru.m.tankz.proto.room.RoomProto;
import ru.m.tankz.proto.room.SlotProto;
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 {
public var runner(default, null):GameRunner; public var runner(default, null):GameRunner;
public var proto(default, null):GameProto; public var room(default, null):RoomProto;
public var id(get, null):Int;
public function new(proto:GameProto) { public function new(room:RoomProto) {
super(new GameState(proto.type, 0, proto.level)); super(new GameState(room.game.type, 0, room.game.level));
this.controlFactory = new ServerControlFactory(); this.controlFactory = new ServerControlFactory();
this.proto = proto; this.room = room;
} }
public function joinUser(user:UserProto, playerId:PlayerId = null):Void { private inline function get_id():Int {
leaveUser(user); return room.game.id;
if (playerId == null) { }
playerId = ["human", proto.players.length];
public function join(user:UserProto):Void {
leave(user);
room.users.push(user);
}
public function slot(user:UserProto, slot:SlotProto):Void {
for (s in room.slots) {
if (s.hasUser() && s.user.uuid == user.uuid) {
s.clearUser();
break;
}
}
for (s in room.slots) {
if (s.slot.team == slot.team && s.slot.index == slot.index) {
s.setUser(user);
break;
}
} }
proto.players.push(new PlayerProto().setUser(user).setTeam(playerId.team).setIndex(playerId.index));
} }
public function leaveUser(user:UserProto):Void { public function leave(user:UserProto):Void {
proto.setPlayers(proto.players.filter(function(player) return player.user.uuid != user.uuid)); for (slot in room.slots) {
if (slot.user != null && slot.user.uuid == user.uuid) {
slot.clearUser();
break;
}
}
room.setUsers(room.users.filter(function(u:UserProto) return u.uuid != user.uuid));
} }
override public function start():Void { override public function start():Void {
state.controls = proto.players.map(function(player):PlayerControl return { state.controls = room.slots
playerId: [player.team, player.index], .filter(function(slot:RoomSlotProto) return slot.hasUser())
control: 'human-0' .map(function(slot:RoomSlotProto):PlayerControl return {
playerId: [slot.slot.team, slot.slot.index],
control: "human-0",
}); });
super.start(); super.start();
} }

View File

@@ -1,24 +1,20 @@
package ru.m.tankz.server.session; package ru.m.tankz.server.session;
import ru.m.tankz.proto.core.GameStateProto;
import haxe.Unserializer;
import haxe.Serializer;
import ru.m.tankz.proto.pack.GameEventResponse;
import ru.m.tankz.game.GameEvent;
import com.hurlant.crypto.extra.UUID; import com.hurlant.crypto.extra.UUID;
import com.hurlant.crypto.prng.Random; import com.hurlant.crypto.prng.Random;
import haxe.Serializer;
import haxe.Unserializer;
import haxework.log.BaseLogger.LoggerUtil; import haxework.log.BaseLogger.LoggerUtil;
import ru.m.tankz.game.GameEvent;
import ru.m.tankz.proto.core.UserProto; import ru.m.tankz.proto.core.UserProto;
import ru.m.tankz.proto.pack.CreateGameResponse;
import ru.m.tankz.proto.pack.ErrorResponse; import ru.m.tankz.proto.pack.ErrorResponse;
import ru.m.tankz.proto.pack.JoinGameResponse; import ru.m.tankz.proto.pack.GameEventResponse;
import ru.m.tankz.proto.pack.LeaveGameResponse;
import ru.m.tankz.proto.pack.ListGameResponse;
import ru.m.tankz.proto.pack.LoginResponse; import ru.m.tankz.proto.pack.LoginResponse;
import ru.m.tankz.proto.pack.LogoutResponse; import ru.m.tankz.proto.pack.LogoutResponse;
import ru.m.tankz.proto.pack.Request; import ru.m.tankz.proto.pack.Request;
import ru.m.tankz.proto.pack.Response; import ru.m.tankz.proto.pack.Response;
import ru.m.tankz.proto.pack.StartGameResponse; import ru.m.tankz.proto.room.RoomListResponse;
import ru.m.tankz.proto.room.RoomResponse;
import ru.m.tankz.server.game.IGameManager; import ru.m.tankz.server.game.IGameManager;
import ru.m.tankz.server.game.ServerGame; import ru.m.tankz.server.game.ServerGame;
import sys.net.Socket; import sys.net.Socket;
@@ -31,6 +27,7 @@ class GameSession extends ProtoSession<Response, Request> implements GameManager
public var user(default, null):UserProto; public var user(default, null):UserProto;
public var gameId(default, null):Int; public var gameId(default, null):Int;
private var subscribed:Bool;
private var tag(get, never):String; private var tag(get, never):String;
private function get_tag():String { private function get_tag():String {
@@ -46,9 +43,9 @@ class GameSession extends ProtoSession<Response, Request> implements GameManager
send(new Response().setError(new ErrorResponse().setCode(code).setMessage(message))); send(new Response().setError(new ErrorResponse().setCode(code).setMessage(message)));
} }
private function listGame():ListGameResponse { private function listGame():RoomListResponse {
var games = gameManager.games; var games = gameManager.games;
return new ListGameResponse().setGames([for (game in games) game.proto]); return new RoomListResponse().setRooms([for (game in games) game.room]);
} }
override public function send(packet:Response):Void { override public function send(packet:Response):Void {
@@ -86,30 +83,28 @@ class GameSession extends ProtoSession<Response, Request> implements GameManager
} else if (request.hasLogout()) { } else if (request.hasLogout()) {
logout(); logout();
send(new Response().setLogout(new LogoutResponse())); send(new Response().setLogout(new LogoutResponse()));
// create // room
} else if (request.hasCreateGame()) { } else if (request.hasRoom()) {
var game = gameManager.create(user, request.createGame.type, request.createGame.level); if (request.room.hasCreate()) {
gameId = game.proto.id; var game = gameManager.create(user, request.room.create.type, request.room.create.level);
send(new Response().setCreateGame(new CreateGameResponse().setGame(game.proto))); gameId = game.id;
// list send(new Response().setRoom(new RoomResponse().setRoom(game.room)));
} else if (request.hasListGame()) { } else if (request.room.hasJoin()) {
send(new Response().setListGame(listGame())); gameId = request.room.join.gameId;
// join gameManager.join(gameId, user);
} else if (request.hasJoinGame()) { } else if (request.room.hasLeave()) {
gameId = request.joinGame.gameId; gameManager.leave(user);
gameManager.join(request.joinGame.gameId, user); } else if (request.room.hasSlot()) {
var game = gameManager.gamesById[gameId]; gameManager.slot(user, request.room.slot.slot);
if (request.joinGame.restore && game.proto.state == GameStateProto.STARTED) { } else if (request.room.hasStart()) {
for (event in game.restore()) { gameManager.start(gameId);
send(new Response().setGameEvent(new GameEventResponse().setTime(0).setEvent(Serializer.run(event)))); }
} // room list
} else if (request.hasRoomList()) {
subscribed = request.roomList.subscribe;
if (subscribed) {
send(new Response().setRoomList(listGame()));
} }
// leave
} else if (request.hasLeaveGame()) {
gameManager.leave(user);
// start
} else if (request.hasStartGame()) {
gameManager.start(gameId);
} else if (request.hasGameEvent()) { } else if (request.hasGameEvent()) {
if (gameManager.gamesById.exists(gameId)) { if (gameManager.gamesById.exists(gameId)) {
var event:GameEvent = Unserializer.run(request.gameEvent.event); var event:GameEvent = Unserializer.run(request.gameEvent.event);
@@ -129,44 +124,41 @@ class GameSession extends ProtoSession<Response, Request> implements GameManager
} }
public function onCreate(game:ServerGame):Void { public function onCreate(game:ServerGame):Void {
if (gameId == -1) { if (subscribed) {
send(new Response().setListGame(listGame())); send(new Response().setRoomList(listGame()));
} }
} }
public function onChange(game:ServerGame, change:GameChange):Void { public function onChange(game:ServerGame, change:GameChange):Void {
if (gameId == game.proto.id) { if (gameId == game.id) {
switch change { switch change {
case JOIN(user):
if (user.uuid == this.user.uuid) {
gameId = game.proto.id;
}
send(new Response().setJoinGame(new JoinGameResponse().setGame(game.proto).setUser(user)));
case LEAVE(user): case LEAVE(user):
if (user.uuid == this.user.uuid) { if (user.uuid == this.user.uuid) {
gameId = -1; gameId = -1;
send(new Response().setRoom(new RoomResponse()));
return;
} }
send(new Response().setLeaveGame(new LeaveGameResponse().setGame(game.proto).setUser(user))); case _:
case START:
send(new Response().setStartGame(new StartGameResponse().setGame(game.proto)));
} }
} else { send(new Response().setRoom(new RoomResponse().setRoom(game.room)));
// ToDo: change game event }
send(new Response().setListGame(listGame())); if (subscribed) {
send(new Response().setRoomList(listGame()));
} }
} }
public function onDelete(game:ServerGame):Void { public function onDelete(game:ServerGame):Void {
if (gameId == -1) { if (gameId == game.id) {
send(new Response().setListGame(listGame()));
} else if (gameId == game.proto.id) {
gameId = -1; gameId = -1;
send(new Response().setLeaveGame(new LeaveGameResponse().setGame(game.proto).setUser(user))); send(new Response().setRoom(new RoomResponse()));
}
if (subscribed) {
send(new Response().setRoomList(listGame()));
} }
} }
public function onEvent(game:ServerGame, event:GameEvent):Void { public function onEvent(game:ServerGame, event:GameEvent):Void {
if (gameId == game.proto.id) { if (gameId == game.id) {
send(new Response().setGameEvent(new GameEventResponse().setTime(0).setEvent(Serializer.run(event)))); send(new Response().setGameEvent(new GameEventResponse().setTime(0).setEvent(Serializer.run(event))));
} }
} }