[client] add GameRoomFrame

This commit is contained in:
2019-05-23 17:57:54 +03:00
parent 864d5f3335
commit 26ad5f47cd
21 changed files with 415 additions and 498 deletions

View File

@@ -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);
/**

View File

@@ -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()) {
gameSignal.emit(null);
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);
}

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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:

View 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);
}
}
}

View 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')

View 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

View File

@@ -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> {

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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();

View File

@@ -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;
}
}

View File

@@ -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;
}
}
/**
*
**/
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 broadcast(packet:Response) {
for (player in gameInfo.players) {
var session:_Session = _Session.sessions.get(player.uuid);
session.send(packet);
}
}
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() {
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));
}*/
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));
}
}
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 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 start(gameId:Int):Void {
if (gamesById.exists(gameId)) {
var game = gamesById[gameId];
game.proto.setState(GameStateProto.STARTED);
changeSignal.emit(game, START);
}
}
}

View 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;
}

View 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);
}
}

View File

@@ -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}');
if (request.hasLogin()) {
send(new Response().setLogin(onLogin(request.login)));
} else if (request.hasLogout()) {
send(new Response().setLogout(onLogout(request.logout)));
} else if (request.hasCreateGame()) {
send(new Response().setCreateGame(onCreateGame(request.createGame)));
} else if (request.hasListGame()) {
send(new Response().setListGame(onListGame(request.listGame)));
L.d(TAG, 'onRequest: ${user == null ? '' : user.name} - ${request}');
try {
if (!request.hasLogin() && user == null) {
throw "Not Authorized";
}
// login
if (request.hasLogin()) {
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()) {
gameManager.disconnect(this);
if (user != null) {
gameManager.leave(user);
user = null;
}
send(new Response().setLogout(new LogoutResponse()));
// create
} else if (request.hasCreateGame()) {
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(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()));
}
}

View File

@@ -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);
}

View File

@@ -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());
}
}