[client] add GameRoomFrame
This commit is contained in:
@@ -78,6 +78,7 @@ const client = new Project(
|
||||
height: 768,
|
||||
},
|
||||
flags: [
|
||||
//'proto_debug',
|
||||
//'dom',
|
||||
//'dev_layout',
|
||||
//'bitmap_text',
|
||||
@@ -125,7 +126,8 @@ const server = new Project(
|
||||
name: 'server',
|
||||
sources: ['src/server/haxe'],
|
||||
main: 'ru.m.tankz.server.Server',
|
||||
})
|
||||
}),
|
||||
module.exports.generate
|
||||
).bind(module, gulp);
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package ru.m.tankz.network;
|
||||
|
||||
import ru.m.tankz.proto.core.UserProto;
|
||||
import haxework.signal.Signal;
|
||||
import ru.m.connect.IConnection;
|
||||
import ru.m.tankz.control.Control;
|
||||
import ru.m.tankz.proto.core.GameProto;
|
||||
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.JoinGameRequest;
|
||||
@@ -31,6 +31,9 @@ enum ConnectionState {
|
||||
class NetworkManager {
|
||||
|
||||
public var state(default, null):ConnectionState;
|
||||
public var game(default, null):GameProto;
|
||||
public var user(default, null):UserProto;
|
||||
|
||||
public var stateSignal:Signal<ConnectionState>;
|
||||
public var listGameSignal:Signal<Array<GameProto>>;
|
||||
public var gameSignal:Signal<GameProto>;
|
||||
@@ -80,8 +83,8 @@ class NetworkManager {
|
||||
connection.send(new Request().setListGame(new ListGameRequest()));
|
||||
}
|
||||
|
||||
public function createGame(type:String):Void {
|
||||
connection.send(new Request().setCreateGame(new CreateGameRequest().setType(type)));
|
||||
public function createGame(type:String, level:Int):Void {
|
||||
connection.send(new Request().setCreateGame(new CreateGameRequest().setType(type).setLevel(level)));
|
||||
}
|
||||
|
||||
public function joinGame(gameId:Int):Void {
|
||||
@@ -116,9 +119,10 @@ class NetworkManager {
|
||||
|
||||
private function onResponse(packet:Response):Void {
|
||||
if (packet.hasLogin()) {
|
||||
this.user = packet.login.user;
|
||||
var user = {
|
||||
uuid: packet.login.user.uuid,
|
||||
name: packet.login.user.name,
|
||||
uuid: this.user.uuid,
|
||||
name: this.user.name,
|
||||
};
|
||||
storage.user = user;
|
||||
updateState(ONLINE(user));
|
||||
@@ -128,11 +132,17 @@ class NetworkManager {
|
||||
} else if (packet.hasListGame()) {
|
||||
listGameSignal.emit(packet.listGame.games);
|
||||
} else if (packet.hasCreateGame()) {
|
||||
game = packet.createGame.game;
|
||||
gameSignal.emit(packet.createGame.game);
|
||||
} else if (packet.hasJoinGame()) {
|
||||
game = packet.createGame.game;
|
||||
gameSignal.emit(packet.joinGame.game);
|
||||
} else if (packet.hasLeaveGame()) {
|
||||
if (packet.leaveGame.user.uuid == user.uuid) {
|
||||
gameSignal.emit(null);
|
||||
} else {
|
||||
gameSignal.emit(packet.leaveGame.game);
|
||||
}
|
||||
} else if (packet.hasStartGame()) {
|
||||
gameSignal.emit(packet.startGame.game);
|
||||
}
|
||||
|
||||
@@ -17,3 +17,5 @@ views:
|
||||
$type: ru.m.tankz.view.RecordFrame
|
||||
- id: game_list
|
||||
$type: ru.m.tankz.view.network.GameListFrame
|
||||
- id: game_room
|
||||
$type: ru.m.tankz.view.network.GameRoomFrame
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package ru.m.tankz.view.network;
|
||||
|
||||
import haxework.view.list.VListView;
|
||||
import ru.m.tankz.proto.core.GameProto;
|
||||
import ru.m.tankz.network.NetworkManager;
|
||||
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";
|
||||
@@ -16,14 +16,37 @@ import haxework.view.VGroupView;
|
||||
|
||||
public function onShow():Void {
|
||||
network.listGameSignal.connect(onGameList);
|
||||
network.gameSignal.connect(onGame);
|
||||
network.stateSignal.connect(onState);
|
||||
network.listGame();
|
||||
}
|
||||
|
||||
public function onHide():Void {
|
||||
network.listGameSignal.disconnect(onGameList);
|
||||
network.gameSignal.disconnect(onGame);
|
||||
network.stateSignal.disconnect(onState);
|
||||
}
|
||||
|
||||
private function create():Void {
|
||||
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 {
|
||||
games.data = data;
|
||||
}
|
||||
|
||||
private function onGame(game:GameProto):Void {
|
||||
switcher.change(GameRoomFrame.ID);
|
||||
}
|
||||
|
||||
private function selectGame(game:GameProto):Void {
|
||||
network.joinGame(game.id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,14 +6,19 @@ views:
|
||||
views:
|
||||
- id: header
|
||||
$type: haxework.view.LabelView
|
||||
geometry.margin.bottom: 20
|
||||
skinId: text.header
|
||||
text: Games List
|
||||
- $type: haxework.view.ButtonView
|
||||
skinId: button.simple
|
||||
text: Create
|
||||
+onPress: $code:create()
|
||||
- id: games
|
||||
$type: haxework.view.list.VListView
|
||||
geometry.size.stretch: true
|
||||
geometry.margin.top: 20
|
||||
factory: $code:ru.m.tankz.view.network.GameItemView.factory
|
||||
geometry.margin: 10
|
||||
+onItemSelect: $code:function(item) selectGame(item.data)
|
||||
- $type: haxework.view.HGroupView
|
||||
skinId: panel
|
||||
views:
|
||||
|
||||
45
src/client/haxe/ru/m/tankz/view/network/GameRoomFrame.hx
Normal file
45
src/client/haxe/ru/m/tankz/view/network/GameRoomFrame.hx
Normal file
@@ -0,0 +1,45 @@
|
||||
package ru.m.tankz.view.network;
|
||||
|
||||
import haxework.view.frame.FrameSwitcher;
|
||||
import haxework.view.TextView;
|
||||
import haxework.view.VGroupView;
|
||||
import ru.m.tankz.network.NetworkManager;
|
||||
import ru.m.tankz.proto.core.GameProto;
|
||||
|
||||
@:template class GameRoomFrame extends VGroupView {
|
||||
|
||||
public static inline var ID = "game_room";
|
||||
|
||||
@:view var info:TextView;
|
||||
|
||||
@:provide var switcher:FrameSwitcher;
|
||||
@:provide var network:NetworkManager;
|
||||
|
||||
private function refresh(game:GameProto):Void {
|
||||
info.text = '${game.id}. ${game.type}';
|
||||
}
|
||||
|
||||
public function onShow():Void {
|
||||
refresh(network.game);
|
||||
network.gameSignal.connect(onGame);
|
||||
network.stateSignal.connect(onState);
|
||||
}
|
||||
|
||||
public function onHide():Void {
|
||||
network.gameSignal.disconnect(onGame);
|
||||
network.stateSignal.disconnect(onState);
|
||||
network.leaveGame();
|
||||
}
|
||||
|
||||
private function onGame(game:GameProto):Void {
|
||||
refresh(game);
|
||||
}
|
||||
|
||||
private function onState(state:ConnectionState):Void {
|
||||
switch state {
|
||||
case ONLINE(_):
|
||||
case _: switcher.change(StartFrame.ID);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
20
src/client/haxe/ru/m/tankz/view/network/GameRoomFrame.yaml
Normal file
20
src/client/haxe/ru/m/tankz/view/network/GameRoomFrame.yaml
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
views:
|
||||
- $type: haxework.view.VGroupView
|
||||
skinId: container
|
||||
geometry.padding: 20
|
||||
views:
|
||||
- id: header
|
||||
$type: haxework.view.LabelView
|
||||
geometry.margin.bottom: 20
|
||||
skinId: text.header
|
||||
text: Game Room
|
||||
- id: info
|
||||
$type: haxework.view.TextView
|
||||
geometry.size.stretch: true
|
||||
- $type: haxework.view.HGroupView
|
||||
skinId: panel
|
||||
views:
|
||||
- $type: haxework.view.ButtonView
|
||||
skinId: button.close
|
||||
+onPress: $code:switcher.change('start')
|
||||
60
src/common/haxe/ru/m/Timer.hx
Normal file
60
src/common/haxe/ru/m/Timer.hx
Normal file
@@ -0,0 +1,60 @@
|
||||
package ru.m;
|
||||
|
||||
#if neko
|
||||
|
||||
import haxe.Log;
|
||||
import haxe.PosInfos;
|
||||
import neko.vm.Thread;
|
||||
|
||||
class Timer {
|
||||
|
||||
private var sleep:Float;
|
||||
private var stopped:Bool;
|
||||
|
||||
public function new(time_ms:Int) {
|
||||
this.sleep = time_ms / 1000.0;
|
||||
this.stopped = false;
|
||||
Thread.create(function() {
|
||||
while (!stopped) {
|
||||
Sys.sleep(sleep);
|
||||
try {
|
||||
run();
|
||||
} catch (error:Dynamic) {
|
||||
trace(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function stop() {
|
||||
stopped = true;
|
||||
}
|
||||
|
||||
public dynamic function run() {}
|
||||
|
||||
public static function delay(f:Void -> Void, time_ms:Int) {
|
||||
var t = new Timer(time_ms);
|
||||
t.run = function() {
|
||||
t.stop();
|
||||
f();
|
||||
};
|
||||
return t;
|
||||
}
|
||||
|
||||
public static function measure<T>(f:Void -> T, ?pos:PosInfos):T {
|
||||
var t0 = stamp();
|
||||
var r = f();
|
||||
Log.trace((stamp() - t0) + "s", pos);
|
||||
return r;
|
||||
}
|
||||
|
||||
public static inline function stamp():Float {
|
||||
return Sys.time();
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
typedef Timer = haxe.Timer;
|
||||
|
||||
#end
|
||||
@@ -1,7 +1,7 @@
|
||||
package ru.m.tankz.core;
|
||||
|
||||
import haxe.Timer;
|
||||
import haxework.signal.Signal;
|
||||
import ru.m.Timer;
|
||||
|
||||
class Modificator extends Signal2<Int, Bool> {
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package ru.m.tankz.game;
|
||||
|
||||
import haxe.ds.Option;
|
||||
import haxe.Timer;
|
||||
import haxework.signal.Signal;
|
||||
import ru.m.geom.Line;
|
||||
import ru.m.geom.Point;
|
||||
@@ -18,6 +17,7 @@ import ru.m.tankz.game.GameEvent;
|
||||
import ru.m.tankz.game.IGame;
|
||||
import ru.m.tankz.game.Spawner;
|
||||
import ru.m.tankz.Type;
|
||||
import ru.m.Timer;
|
||||
|
||||
class GameRunner implements EngineListener implements GameListener {
|
||||
@:provide var controlFactory:IControlFactory;
|
||||
|
||||
@@ -13,7 +13,10 @@ class State {
|
||||
public var hits:Int;
|
||||
|
||||
public function new() {
|
||||
|
||||
score = 0;
|
||||
frags = 0;
|
||||
shots = 0;
|
||||
hits = 0;
|
||||
}
|
||||
|
||||
public function add(state:State) {
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
package ru.m.tankz.game;
|
||||
|
||||
import haxe.Timer;
|
||||
import ru.m.tankz.config.Config;
|
||||
import ru.m.tankz.Type;
|
||||
|
||||
import ru.m.Timer;
|
||||
|
||||
typedef SpawnTask = {
|
||||
var point:SpawnPoint;
|
||||
|
||||
@@ -50,6 +50,7 @@ message JoinGameRequest {
|
||||
|
||||
message JoinGameResponse {
|
||||
ru.m.tankz.proto.core.GameProto game = 1;
|
||||
ru.m.tankz.proto.core.UserProto user = 2;
|
||||
}
|
||||
|
||||
// Leave Game
|
||||
@@ -57,6 +58,7 @@ message LeaveGameRequest {}
|
||||
|
||||
message LeaveGameResponse {
|
||||
ru.m.tankz.proto.core.GameProto game = 1;
|
||||
ru.m.tankz.proto.core.UserProto user = 2;
|
||||
}
|
||||
|
||||
// Start Game
|
||||
@@ -89,5 +91,7 @@ message Response {
|
||||
JoinGameResponse joinGame = 5;
|
||||
LeaveGameResponse leaveGame = 6;
|
||||
StartGameResponse startGame = 7;
|
||||
|
||||
ErrorResponse error = 999;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,12 @@ import haxework.provider.Provider;
|
||||
import neko.net.ThreadServer;
|
||||
import ru.m.tankz.bundle.IConfigBundle;
|
||||
import ru.m.tankz.bundle.ILevelBundle;
|
||||
import ru.m.tankz.control.IControlFactory;
|
||||
import ru.m.tankz.control.NoneControlFactory;
|
||||
import ru.m.tankz.server.bundle.ServerConfigBundle;
|
||||
import ru.m.tankz.server.bundle.ServerLevelBundle;
|
||||
import ru.m.tankz.server.game.GameManager;
|
||||
import ru.m.tankz.server.game.IGameManager;
|
||||
import ru.m.tankz.server.session.GameSession;
|
||||
import sys.net.Socket;
|
||||
|
||||
@@ -35,6 +39,8 @@ class Server extends ThreadServer<GameSession, Bytes> {
|
||||
session.pushData(bytes);
|
||||
}
|
||||
|
||||
@:provide static var gameManager:IGameManager;
|
||||
|
||||
public static function main() {
|
||||
L.push(new TraceLogger());
|
||||
#if debug
|
||||
@@ -44,7 +50,8 @@ class Server extends ThreadServer<GameSession, Bytes> {
|
||||
L.i(TAG, 'Build: ${CompilationOption.get("build")}');
|
||||
Provider.setFactory(IConfigBundle, ServerConfigBundle);
|
||||
Provider.setFactory(ILevelBundle, ServerLevelBundle);
|
||||
//Provider.setFactory(IControlFactory, NoneControlFactory);
|
||||
Provider.setFactory(IControlFactory, NoneControlFactory);
|
||||
gameManager = new GameManager();
|
||||
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 Server();
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
package ru.m.tankz.server.db;
|
||||
|
||||
import ru.m.tankz.db.Orm;
|
||||
import ru.m.tankz.proto.Person;
|
||||
import ru.m.tankz.proto.Account;
|
||||
|
||||
class DbProvider {
|
||||
|
||||
private var orm:Orm;
|
||||
|
||||
public function new() {
|
||||
var db = new orm.Db("mysql://shmyga:password@localhost:3306/armageddon");
|
||||
orm = new Orm(db);
|
||||
}
|
||||
|
||||
public function getAccount(login:String, password:String):Null<Account> {
|
||||
var account = null;
|
||||
var accountModel = orm.account.getByLogin(login);
|
||||
if (accountModel != null && accountModel.password == password) {
|
||||
account = new Account().setLogin(accountModel.login);
|
||||
var personsModels = orm.person.getByAccount_id(accountModel.id);
|
||||
for (personModel in personsModels) {
|
||||
account.addPersons(new Person().setId(personModel.id).setName(personModel.name));
|
||||
}
|
||||
}
|
||||
return account;
|
||||
}
|
||||
|
||||
public function getPerson(person_id:Int):Null<Person> {
|
||||
var person = null;
|
||||
var personModel = orm.person.get(person_id);
|
||||
if (personModel != null) {
|
||||
person = new Person().setId(personModel.id).setName(personModel.name);
|
||||
}
|
||||
return person;
|
||||
}
|
||||
}
|
||||
@@ -1,149 +1,79 @@
|
||||
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.preset.ClassicGame;
|
||||
import ru.m.tankz.proto.pack.StartGameResponse;
|
||||
import ru.m.tankz.proto.pack.LeaveGameResponse;
|
||||
import ru.m.tankz.proto.pack.JoinGameResponse;
|
||||
import ru.m.tankz.proto.pack.ListGameResponse;
|
||||
import ru.m.tankz.proto.pack.CreateGameResponse;
|
||||
import ru.m.tankz.proto.pack.Response;
|
||||
import ru.m.tankz.proto.core.GameStateProto;
|
||||
import ru.m.tankz.proto.core.GameInfoProto;
|
||||
import ru.m.tankz.game.GameState;
|
||||
import ru.m.tankz.proto.core.GameProto;
|
||||
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.game.IGameManager;
|
||||
|
||||
@:dispatcher(GameManagerListener) class GameManager implements IGameManager {
|
||||
public var games(default, null):Array<ServerGame>;
|
||||
public var gamesById(default, null):Map<Int, ServerGame>;
|
||||
public var gamesByCreator(default, null):Map<String, ServerGame>;
|
||||
public var gamesByUser(default, null):Map<String, ServerGame>;
|
||||
|
||||
/**
|
||||
*
|
||||
**/
|
||||
class NekoTimer {
|
||||
private var counter:Int;
|
||||
|
||||
private var sleep:Float;
|
||||
private var stopped:Bool;
|
||||
|
||||
public function new(time_ms:Int) {
|
||||
this.sleep = time_ms / 1000.0;
|
||||
this.stopped = false;
|
||||
Thread.create(function() {
|
||||
while (!stopped) {
|
||||
Sys.sleep(sleep);
|
||||
try {
|
||||
run();
|
||||
} catch (error:Dynamic) {
|
||||
trace(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
public function new() {
|
||||
counter = 0;
|
||||
games = [];
|
||||
gamesById = new Map();
|
||||
gamesByCreator = new Map();
|
||||
gamesByUser = new Map();
|
||||
}
|
||||
|
||||
public dynamic function run() {}
|
||||
public function create(user:UserProto, type:String, level:Int):ServerGame {
|
||||
if (gamesByCreator.exists(user.uuid)) {
|
||||
delete(gamesByCreator[user.uuid].proto.id);
|
||||
}
|
||||
var proto = new GameProto()
|
||||
.setId(++counter)
|
||||
.setCreator(user)
|
||||
.setType(type)
|
||||
.setLevel(level)
|
||||
.setPlayers([user]);
|
||||
var game = new ServerGame(proto);
|
||||
games.push(game);
|
||||
gamesById[game.proto.id] = game;
|
||||
gamesByCreator[game.proto.creator.uuid] = game;
|
||||
createSignal.emit(game);
|
||||
return game;
|
||||
}
|
||||
|
||||
public function stop() {
|
||||
stopped = true;
|
||||
public function join(gameId:Int, user:UserProto):Void {
|
||||
if (gamesById.exists(gameId)) {
|
||||
var game = gamesById[gameId];
|
||||
game.proto.players.push(user);
|
||||
gamesByUser[user.uuid] = game;
|
||||
changeSignal.emit(game, JOIN(user));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
**/
|
||||
class GameManager {
|
||||
|
||||
public static var byGameId:Map<Int, GameManager> = new Map<Int, GameManager>();
|
||||
public static var byPersonId:Map<String, GameManager> = new Map<String, GameManager>();
|
||||
public static var subscribers:Map<String, Bool> = new Map<String, Bool>();
|
||||
|
||||
private static var idCounter:Int = 0;
|
||||
|
||||
public var gameInfo(default, null):GameInfoProto;
|
||||
public var game(default, null):IGame;
|
||||
|
||||
private var timer:NekoTimer;
|
||||
|
||||
//private var changes:Array<GameChange> = new Array<GameChange>();
|
||||
|
||||
public function new(creator:UserProto) {
|
||||
gameInfo = new GameInfoProto()
|
||||
.setId(idCounter++)
|
||||
.setState(GameStateProto.READY)
|
||||
.setCreator(creator);
|
||||
gameInfo.addPlayers(creator);
|
||||
byGameId.set(gameInfo.id, this);
|
||||
byPersonId.set(creator.uuid, this);
|
||||
broadcast(new Response().setCreateGame(new CreateGameResponse().setGame(gameInfo)));
|
||||
broadcastGames();
|
||||
}
|
||||
|
||||
public static function getReadyGames():Array<GameInfoProto> {
|
||||
return Lambda.array(Lambda.filter(Lambda.map(GameManager.byGameId, function(gm) return gm.gameInfo), function(game) return game.state == GameStateProto.READY));
|
||||
}
|
||||
|
||||
public function broadcastGames() {
|
||||
var packet = new Response().setListGame(new ListGameResponse().setGames(getReadyGames()));
|
||||
for (personId in subscribers.keys()) {
|
||||
var session:_Session = _Session.sessions.get(personId);
|
||||
session.send(packet);
|
||||
public function delete(gameId:Int):Void {
|
||||
if (gamesById.exists(gameId)) {
|
||||
var game = gamesById[gameId];
|
||||
games.remove(game);
|
||||
gamesById.remove(game.proto.id);
|
||||
gamesByCreator.remove(game.proto.creator.uuid);
|
||||
deleteSignal.emit(game);
|
||||
}
|
||||
}
|
||||
|
||||
public function broadcast(packet:Response) {
|
||||
for (player in gameInfo.players) {
|
||||
var session:_Session = _Session.sessions.get(player.uuid);
|
||||
session.send(packet);
|
||||
public function leave(user:UserProto):Void {
|
||||
if (gamesByCreator.exists(user.uuid)) {
|
||||
delete(gamesByCreator[user.uuid].proto.id);
|
||||
} else if (gamesByUser.exists(user.uuid)) {
|
||||
var game = gamesByUser[user.uuid];
|
||||
game.proto.setPlayers(game.proto.players.filter(function(player) return player.uuid != user.uuid));
|
||||
changeSignal.emit(game, LEAVE(user));
|
||||
}
|
||||
}
|
||||
|
||||
public function join(user:UserProto) {
|
||||
gameInfo.addPlayers(user);
|
||||
byPersonId.set(user.uuid, this);
|
||||
broadcast(new Response().setJoinGame(new JoinGameResponse().setGame(gameInfo)));
|
||||
}
|
||||
|
||||
public function leave(user:UserProto) {
|
||||
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);
|
||||
if (gameInfo.players.length == 0/* || person.id == game.creator.id*/) {
|
||||
stop();
|
||||
} else {
|
||||
broadcast(packet);
|
||||
public function start(gameId:Int):Void {
|
||||
if (gamesById.exists(gameId)) {
|
||||
var game = gamesById[gameId];
|
||||
game.proto.setState(GameStateProto.STARTED);
|
||||
changeSignal.emit(game, START);
|
||||
}
|
||||
}
|
||||
|
||||
public function start() {
|
||||
gameInfo.setState(GameStateProto.STARTED);
|
||||
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)));
|
||||
}
|
||||
|
||||
public function stop() {
|
||||
gameInfo.setState(GameStateProto.ENDED);
|
||||
gameInfo.setPlayers([]);
|
||||
byGameId.remove(gameInfo.id);
|
||||
for (p in gameInfo.players) byPersonId.remove(p.uuid);
|
||||
if (timer != null) {
|
||||
timer.stop();
|
||||
timer = null;
|
||||
}
|
||||
broadcast(new Response().setLeaveGame(new LeaveGameResponse().setGame(gameInfo)));
|
||||
broadcastGames();
|
||||
}
|
||||
|
||||
private function update() {
|
||||
//game.engine.update();
|
||||
/*var changes = engine.update();
|
||||
changes = this.changes.concat(changes);
|
||||
this.changes = [];
|
||||
if (changes.length > 0) {
|
||||
broadcast(new GameUpdateResponse().setChanges(changes));
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
35
src/server/haxe/ru/m/tankz/server/game/IGameManager.hx
Normal file
35
src/server/haxe/ru/m/tankz/server/game/IGameManager.hx
Normal file
@@ -0,0 +1,35 @@
|
||||
package ru.m.tankz.server.game;
|
||||
|
||||
import haxework.signal.Signal;
|
||||
import ru.m.tankz.proto.core.UserProto;
|
||||
|
||||
enum GameChange {
|
||||
JOIN(user:UserProto);
|
||||
LEAVE(user:UserProto);
|
||||
START();
|
||||
}
|
||||
|
||||
interface GameManagerListener {
|
||||
public function onCreate(game:ServerGame):Void;
|
||||
public function onChange(game:ServerGame, change:GameChange):Void;
|
||||
public function onDelete(game:ServerGame):Void;
|
||||
}
|
||||
|
||||
interface IGameManager {
|
||||
public var games(default, null):Array<ServerGame>;
|
||||
public var gamesById(default, null):Map<Int, ServerGame>;
|
||||
public var gamesByCreator(default, null):Map<String, ServerGame>;
|
||||
|
||||
private var createSignal(default, null):Signal<ServerGame>;
|
||||
private var changeSignal(default, null):Signal2<ServerGame, GameChange>;
|
||||
private var deleteSignal(default, null):Signal<ServerGame>;
|
||||
|
||||
public function connect(listener:GameManagerListener):Void;
|
||||
public function disconnect(listener:GameManagerListener):Void;
|
||||
|
||||
public function create(user:UserProto, type:String, level:Int):ServerGame;
|
||||
public function delete(gameId:Int):Void;
|
||||
public function join(gameId:Int, user:UserProto):Void;
|
||||
public function leave(user:UserProto):Void;
|
||||
public function start(gameId:Int):Void;
|
||||
}
|
||||
18
src/server/haxe/ru/m/tankz/server/game/ServerGame.hx
Normal file
18
src/server/haxe/ru/m/tankz/server/game/ServerGame.hx
Normal file
@@ -0,0 +1,18 @@
|
||||
package ru.m.tankz.server.game;
|
||||
|
||||
import ru.m.tankz.game.Game;
|
||||
//import ru.m.tankz.game.GameRunner;
|
||||
import ru.m.tankz.game.GameState;
|
||||
import ru.m.tankz.proto.core.GameProto;
|
||||
|
||||
class ServerGame extends Game {
|
||||
|
||||
//public var runner(default, null):GameRunner;
|
||||
public var proto(default, null):GameProto;
|
||||
|
||||
public function new(proto:GameProto) {
|
||||
super(new GameState(proto.type, 0, proto.level));
|
||||
this.proto = proto;
|
||||
//runner = new GameRunner(this);
|
||||
}
|
||||
}
|
||||
@@ -2,69 +2,128 @@ package ru.m.tankz.server.session;
|
||||
|
||||
import com.hurlant.crypto.extra.UUID;
|
||||
import com.hurlant.crypto.prng.Random;
|
||||
import ru.m.tankz.proto.core.GameProto;
|
||||
import haxework.log.BaseLogger.LoggerUtil;
|
||||
import ru.m.tankz.proto.core.UserProto;
|
||||
import ru.m.tankz.proto.pack.CreateGameRequest;
|
||||
import ru.m.tankz.proto.pack.CreateGameResponse;
|
||||
import ru.m.tankz.proto.pack.ListGameRequest;
|
||||
import ru.m.tankz.proto.pack.ErrorResponse;
|
||||
import ru.m.tankz.proto.pack.JoinGameResponse;
|
||||
import ru.m.tankz.proto.pack.LeaveGameResponse;
|
||||
import ru.m.tankz.proto.pack.ListGameResponse;
|
||||
import ru.m.tankz.proto.pack.LoginRequest;
|
||||
import ru.m.tankz.proto.pack.LoginResponse;
|
||||
import ru.m.tankz.proto.pack.LogoutRequest;
|
||||
import ru.m.tankz.proto.pack.LogoutResponse;
|
||||
import ru.m.tankz.proto.pack.Request;
|
||||
import ru.m.tankz.proto.pack.Response;
|
||||
import ru.m.tankz.proto.pack.StartGameResponse;
|
||||
import ru.m.tankz.server.game.IGameManager;
|
||||
import ru.m.tankz.server.game.ServerGame;
|
||||
import sys.net.Socket;
|
||||
|
||||
class GameSession extends ProtoSession<Response, Request> {
|
||||
class GameSession extends ProtoSession<Response, Request> implements GameManagerListener {
|
||||
private static inline var TAG = "Session";
|
||||
|
||||
@:provide static var gameManager:IGameManager;
|
||||
|
||||
public var user(default, null):UserProto;
|
||||
public var gameId(default, null):Int;
|
||||
|
||||
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 sendError(code:Int, message:String):Void {
|
||||
send(new Response().setError(new ErrorResponse().setCode(code).setMessage(message)));
|
||||
}
|
||||
|
||||
private function onLogout(request:LogoutRequest):LogoutResponse {
|
||||
return new LogoutResponse();
|
||||
private function listGame():ListGameResponse {
|
||||
var games = gameManager.games;
|
||||
return new ListGameResponse().setGames([for (game in games) game.proto]);
|
||||
}
|
||||
|
||||
private function onCreateGame(request:CreateGameRequest):CreateGameResponse {
|
||||
var game = new GameProto()
|
||||
.setId(1)
|
||||
.setCreator(user)
|
||||
.setType(request.type)
|
||||
.setLevel(request.level);
|
||||
return new CreateGameResponse().setGame(game);
|
||||
}
|
||||
|
||||
private function onListGame(request:ListGameRequest):ListGameResponse {
|
||||
// ToDo: test games
|
||||
var games = [for (i in 0...5) new GameProto()
|
||||
.setId(i)
|
||||
.setCreator(user)
|
||||
.setType("classic")
|
||||
.setLevel(i)];
|
||||
return new ListGameResponse().setGames(games);
|
||||
override public function send(packet:Response):Void {
|
||||
L.d(TAG, 'send: ${user == null ? '' : user.name} - ${packet}');
|
||||
super.send(packet);
|
||||
}
|
||||
|
||||
override private function onRequest(request:Request):Void {
|
||||
L.d(TAG, 'onRequest: ${request}');
|
||||
L.d(TAG, 'onRequest: ${user == null ? '' : user.name} - ${request}');
|
||||
try {
|
||||
if (!request.hasLogin() && user == null) {
|
||||
throw "Not Authorized";
|
||||
}
|
||||
// login
|
||||
if (request.hasLogin()) {
|
||||
send(new Response().setLogin(onLogin(request.login)));
|
||||
user = new UserProto()
|
||||
.setUuid(request.login.uuid != null ? request.login.uuid : UUID.generateRandom(new Random()).toString())
|
||||
.setName(request.login.name);
|
||||
gameManager.connect(this);
|
||||
send(new Response().setLogin(new LoginResponse().setUser(user)));
|
||||
// logout
|
||||
} else if (request.hasLogout()) {
|
||||
send(new Response().setLogout(onLogout(request.logout)));
|
||||
gameManager.disconnect(this);
|
||||
if (user != null) {
|
||||
gameManager.leave(user);
|
||||
user = null;
|
||||
}
|
||||
send(new Response().setLogout(new LogoutResponse()));
|
||||
// create
|
||||
} else if (request.hasCreateGame()) {
|
||||
send(new Response().setCreateGame(onCreateGame(request.createGame)));
|
||||
var game = gameManager.create(user, request.createGame.type, request.createGame.level);
|
||||
gameId = game.proto.id;
|
||||
send(new Response().setCreateGame(new CreateGameResponse().setGame(game.proto)));
|
||||
// list
|
||||
} else if (request.hasListGame()) {
|
||||
send(new Response().setListGame(onListGame(request.listGame)));
|
||||
send(new Response().setListGame(listGame()));
|
||||
// join
|
||||
} else if (request.hasJoinGame()) {
|
||||
gameId = request.joinGame.gameId;
|
||||
gameManager.join(request.joinGame.gameId, user);
|
||||
// leave
|
||||
} else if (request.hasLeaveGame()) {
|
||||
gameManager.leave(user);
|
||||
// start
|
||||
} else if (request.hasStartGame()) {
|
||||
gameManager.start(gameId);
|
||||
}
|
||||
} catch (error:Dynamic) {
|
||||
L.e(TAG, "onRequest ", error);
|
||||
sendError(500, LoggerUtil.printError(error));
|
||||
}
|
||||
}
|
||||
|
||||
override public function disconnect():Void {
|
||||
gameId = null;
|
||||
gameManager.disconnect(this);
|
||||
if (user != null) {
|
||||
gameManager.leave(user);
|
||||
user = null;
|
||||
}
|
||||
super.disconnect();
|
||||
}
|
||||
|
||||
public function onCreate(game:ServerGame):Void {
|
||||
send(new Response().setListGame(listGame()));
|
||||
}
|
||||
|
||||
public function onChange(game:ServerGame, change:GameChange):Void {
|
||||
if (gameId == game.proto.id) {
|
||||
switch change {
|
||||
case JOIN(user):
|
||||
if (user.uuid == this.user.uuid) {
|
||||
gameId = gameId = game.proto.id;
|
||||
}
|
||||
send(new Response().setJoinGame(new JoinGameResponse().setGame(game.proto).setUser(user)));
|
||||
case LEAVE(user):
|
||||
if (user.uuid == this.user.uuid) {
|
||||
gameId = null;
|
||||
}
|
||||
send(new Response().setLeaveGame(new LeaveGameResponse().setGame(game.proto).setUser(user)));
|
||||
case START:
|
||||
send(new Response().setStartGame(new StartGameResponse().setGame(game.proto)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function onDelete(game:ServerGame):Void {
|
||||
send(new Response().setListGame(listGame()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
package ru.m.tankz.server.session;
|
||||
|
||||
enum ThreadHandle {
|
||||
}
|
||||
|
||||
class Thread {
|
||||
|
||||
var handle:ThreadHandle;
|
||||
|
||||
function new(h) {
|
||||
handle = h;
|
||||
}
|
||||
|
||||
/**
|
||||
Send a message to the thread queue. This message can be readed by using [readMessage].
|
||||
**/
|
||||
public function sendMessage(msg:Dynamic) {
|
||||
thread_send(handle, msg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Returns the current thread.
|
||||
**/
|
||||
public static function current() {
|
||||
return new Thread(thread_current());
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a new thread that will execute the [callb] function, then exit.
|
||||
**/
|
||||
public static function create(callb:Void -> Void) {
|
||||
return new Thread(thread_create(function(_) { return callb(); }, null));
|
||||
}
|
||||
|
||||
/**
|
||||
Reads a message from the thread queue. If [block] is true, the function
|
||||
blocks until a message is available. If [block] is false, the function
|
||||
returns [null] if no message is available.
|
||||
**/
|
||||
public static function readMessage(block:Bool):Dynamic {
|
||||
return thread_read_message(block);
|
||||
}
|
||||
|
||||
@:keep function __compare(t) {
|
||||
return untyped __dollar__compare(handle, t.handle);
|
||||
}
|
||||
|
||||
/**
|
||||
Starts an OS message loop after [osInitialize] has been done.
|
||||
In that state, the UI handled by this thread will be updated and
|
||||
[sync] calls can be performed. The loop returns when [exitLoop] is
|
||||
called for this thread.
|
||||
**
|
||||
public static function osLoop() {
|
||||
if( os_loop == null ) throw "Please call osInitialize() first";
|
||||
os_loop();
|
||||
}
|
||||
|
||||
/**
|
||||
The function [f] will be called by this thread if it's in [osLoop].
|
||||
[sync] returns immediatly. See [osInitialize] remarks.
|
||||
**
|
||||
public function sync( f : Void -> Void ) {
|
||||
os_sync(handle,f);
|
||||
}
|
||||
|
||||
/**
|
||||
The function [f] will be called by this thread and the calling thread
|
||||
will wait until the result is available then return its value.
|
||||
**
|
||||
public function syncResult<T>( f : Void -> T ) : T {
|
||||
if( this == current() )
|
||||
return f();
|
||||
var v = new neko.vm.Lock();
|
||||
var r = null;
|
||||
sync(function() {
|
||||
r = f();
|
||||
v.release();
|
||||
});
|
||||
v.wait();
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
Exit from [osLoop].
|
||||
**
|
||||
public function exitLoop() {
|
||||
os_loop_stop(handle);
|
||||
}
|
||||
|
||||
/**
|
||||
If you want to use the [osLoop], [sync] and [syncResult] methods, you
|
||||
need to call [osInitialize] before creating any thread or calling [current].
|
||||
This will load [os.ndll] library and initialize UI methods for each thread.
|
||||
**
|
||||
public static function osInitialize() {
|
||||
os_loop = neko.Lib.load("os","os_loop",0);
|
||||
os_loop_stop = neko.Lib.load("os","os_loop_stop",1);
|
||||
os_sync = neko.Lib.load("os","os_sync",2);
|
||||
}
|
||||
|
||||
static var os_loop = null;
|
||||
static var os_loop_stop = null;
|
||||
static var os_sync = null;
|
||||
*/
|
||||
|
||||
static var thread_create = neko.Lib.load("std", "thread_create", 2);
|
||||
static var thread_current = neko.Lib.load("std", "thread_current", 0);
|
||||
static var thread_send = neko.Lib.load("std", "thread_send", 2);
|
||||
static var thread_read_message = neko.Lib.load("std", "thread_read_message", 1);
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
package ru.m.tankz.server.session;
|
||||
|
||||
import ru.m.tankz.proto.pack.GameResponse;
|
||||
import ru.m.tankz.proto.pack.GameRequest;
|
||||
import com.hurlant.crypto.extra.UUID;
|
||||
import com.hurlant.crypto.prng.Random;
|
||||
import haxe.io.Bytes;
|
||||
import ru.m.connect.IConnection;
|
||||
import ru.m.connect.neko.NekoConnection;
|
||||
import ru.m.connect.neko.NekoWSConnection;
|
||||
import ru.m.tankz.proto.core.UserProto;
|
||||
import ru.m.tankz.proto.pack.CreateGameRequest;
|
||||
import ru.m.tankz.proto.pack.CreateGameResponse;
|
||||
import ru.m.tankz.proto.pack.JoinGameRequest;
|
||||
import ru.m.tankz.proto.pack.JoinGameResponse;
|
||||
import ru.m.tankz.proto.pack.LeaveGameRequest;
|
||||
import ru.m.tankz.proto.pack.LeaveGameResponse;
|
||||
import ru.m.tankz.proto.pack.ListGameRequest;
|
||||
import ru.m.tankz.proto.pack.ListGameResponse;
|
||||
import ru.m.tankz.proto.pack.LoginRequest;
|
||||
import ru.m.tankz.proto.pack.LoginResponse;
|
||||
import ru.m.tankz.proto.pack.LogoutRequest;
|
||||
import ru.m.tankz.proto.pack.LogoutResponse;
|
||||
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.proto.pack.StartGameResponse;
|
||||
import ru.m.tankz.server.game.GameManager;
|
||||
import sys.net.Socket;
|
||||
|
||||
|
||||
typedef ServerConnection = IConnection<Response, Request>;
|
||||
|
||||
class _Session {
|
||||
private static inline var TAG = 'Session';
|
||||
|
||||
private static var POLICY_FILE:String = [
|
||||
'<?xml version="1.0"?>',
|
||||
'<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">',
|
||||
'<cross-domain-policy>',
|
||||
'<site-control permitted-cross-domain-policies="master-only"/>',
|
||||
'<allow-access-from domain="*" to-ports="*"/>',
|
||||
'</cross-domain-policy>'
|
||||
].join('\n');
|
||||
|
||||
public static var sessions:Map<String, _Session> = new Map<String, _Session>();
|
||||
|
||||
public var user(default, null):UserProto;
|
||||
public var gameId(default, null):Int = -1;
|
||||
|
||||
public var connection(default, null):ServerConnection;
|
||||
|
||||
private var socket:Socket;
|
||||
|
||||
public function new(socket:Socket) {
|
||||
this.socket = socket;
|
||||
}
|
||||
|
||||
public function send(packet:Response):Void {
|
||||
connection.send(packet);
|
||||
}
|
||||
|
||||
public function pushData(bytes:Bytes):Void {
|
||||
if (connection != null) {
|
||||
connection.pushData(bytes);
|
||||
} else {
|
||||
var str:String = bytes.getString(0, bytes.length);
|
||||
if (str == '<policy-file-request/>' + String.fromCharCode(0)) {
|
||||
L.d(TAG, 'policy-file-request');
|
||||
socket.output.writeString(POLICY_FILE + String.fromCharCode(0));
|
||||
socket.output.flush();
|
||||
return;
|
||||
}
|
||||
if (StringTools.startsWith(str, "GET")) {
|
||||
connection = new NekoWSConnection<Response, Request>(socket, Request);
|
||||
} else {
|
||||
connection = new NekoConnection<Response, Request>(socket, Request);
|
||||
}
|
||||
connection.handler.connect(onConnectionEvent);
|
||||
connection.receiveHandler.connect(onRequest);
|
||||
connection.pushData(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
private function onConnectionEvent(event:ConnectionEvent):Void {
|
||||
L.d(TAG, '${event}');
|
||||
}
|
||||
|
||||
public function onRequest(request:Request):Void {
|
||||
if (request.hasLogin()) {
|
||||
send(new Response().setLogin(login(request.login)));
|
||||
} else if (request.hasLogout()) {
|
||||
send(new Response().setLogout(logout(request.logout)));
|
||||
} else if (request.hasListGame()) {
|
||||
send(new Response().setListGame(listGame(request.listGame)));
|
||||
} else if (request.hasCreateGame()) {
|
||||
send(new Response().setCreateGame(createGame(request.createGame)));
|
||||
} else if (request.hasJoinGame()) {
|
||||
send(new Response().setJoinGame(joinGame(request.joinGame)));
|
||||
} else if (request.hasLeaveGame()) {
|
||||
send(new Response().setLeaveGame(leaveGame(request.leaveGame)));
|
||||
} else if (request.hasStartGame()) {
|
||||
send(new Response().setStartGame(startGame(request.startGame)));
|
||||
}else if (request.hasGame()) {
|
||||
send(new Response().setGame(game(request.game)));
|
||||
}
|
||||
}
|
||||
|
||||
private function login(request:LoginRequest):LoginResponse {
|
||||
user = new UserProto()
|
||||
.setUuid(request.uuid != null ? request.uuid : UUID.generateRandom(new Random()).toString())
|
||||
.setName(request.name);
|
||||
sessions.set(user.uuid, this);
|
||||
GameManager.subscribers.set(user.uuid, true);
|
||||
return new LoginResponse().setUser(user);
|
||||
}
|
||||
|
||||
private function logout(request:LogoutRequest):LogoutResponse {
|
||||
GameManager.subscribers.remove(user.uuid);
|
||||
user = null;
|
||||
return new LogoutResponse();
|
||||
}
|
||||
|
||||
private function listGame(request:ListGameRequest):ListGameResponse {
|
||||
return new ListGameResponse().setGames(GameManager.getReadyGames());
|
||||
}
|
||||
|
||||
private function createGame(request:CreateGameRequest):CreateGameResponse {
|
||||
var gameManager:GameManager = new GameManager(user);
|
||||
return new CreateGameResponse().setGame(gameManager.gameInfo);
|
||||
}
|
||||
|
||||
private function joinGame(request:JoinGameRequest):JoinGameResponse {
|
||||
var gameManager:GameManager = GameManager.byGameId.get(request.gameId);
|
||||
gameManager.join(user);
|
||||
return new JoinGameResponse().setGame(gameManager.gameInfo);
|
||||
}
|
||||
|
||||
private function leaveGame(request:LeaveGameRequest):LeaveGameResponse {
|
||||
var gameManager:GameManager = GameManager.byPersonId.get(user.uuid);
|
||||
gameManager.leave(user);
|
||||
return new LeaveGameResponse().setGame(gameManager.gameInfo);
|
||||
}
|
||||
|
||||
private function startGame(request:StartGameRequest):StartGameResponse {
|
||||
var gameManager:GameManager = GameManager.byPersonId.get(user.uuid);
|
||||
gameManager.start();
|
||||
return new StartGameResponse().setGame(gameManager.gameInfo);
|
||||
}
|
||||
|
||||
private function game(request:GameRequest):GameResponse {
|
||||
var gameManager:GameManager = GameManager.byPersonId.get(user.uuid);
|
||||
return new GameResponse();
|
||||
//return new GameResponse().setGame(gameManager.game.export());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user