diff --git a/src/server/haxe/ru/m/tankz/server/game/GameManager.hx b/src/server/haxe/ru/m/tankz/server/game/GameManager.hx index ba56d65..7fbefaf 100644 --- a/src/server/haxe/ru/m/tankz/server/game/GameManager.hx +++ b/src/server/haxe/ru/m/tankz/server/game/GameManager.hx @@ -1,55 +1,40 @@ package ru.m.tankz.server.game; -import ru.m.tankz.proto.GamesResponse; -import ru.m.tankz.proto.GameActionType; -import ru.m.tankz.proto.GameActionRequest; -import ru.m.tankz.proto.GameObjectType; -import ru.m.tankz.proto.GameChangeType; -import ru.m.tankz.proto.GameUpdateResponse; -import ru.m.tankz.proto.GameChange; -import ru.m.tankz.proto.StartGameResponse; -import ru.m.tankz.core.Direction; +import ru.m.tankz.proto.core.GameState; +import ru.m.tankz.proto.core.Game; import ru.m.tankz.server.session.Thread; -import ru.m.tankz.config.TankzConfig.DEFAULT; import ru.m.tankz.engine.Engine; -import ru.m.tankz.proto.CreateGameResponse; -import ru.m.tankz.proto.LeaveGameResponse; -import ru.m.tankz.proto.JoinGameResponse; import ru.m.tankz.server.session.Session; import protohx.Message; -import ru.m.tankz.proto.Person; -import ru.m.tankz.proto.GameState; -import ru.m.tankz.proto.Game; -import ru.m.tankz.engine.IEngine; /** * **/ class NekoTimer { - private var sleep:Float; - private var stopped:Bool; + 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(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 dynamic function run() {} + public dynamic function run() {} - public function stop() { - stopped = true; - } + public function stop() { + stopped = true; + } } /** @@ -57,132 +42,132 @@ class NekoTimer { **/ class GameManager { - public static var byGameId:Map = new Map(); - public static var byPersonId:Map = new Map(); - public static var subscribers:Map = new Map(); + public static var byGameId:Map = new Map(); + public static var byPersonId:Map = new Map(); + public static var subscribers:Map = new Map(); - private static var idCounter:Int = 0; + private static var idCounter:Int = 0; - public var game(default, null):Game; + public var game(default, null):Game; - public var engine(default, null):IEngine; + public var engine(default, null):Engine; - private var timer:NekoTimer; + private var timer:NekoTimer; - private var changes:Array = new Array(); + //private var changes:Array = new Array(); - public function new(person:Person) { - game = new Game() - .setId(idCounter++) - .setState(GameState.READY) - .setCreator(person); - game.addPersons(person); - byGameId.set(game.id, this); - byPersonId.set(person.id, this); - broadcast(new CreateGameResponse().setGame(game)); - broadcastGames(); - } - - public static function getReadyGames():Array { - return Lambda.array(Lambda.filter(Lambda.map(GameManager.byGameId, function(gm) return gm.game), function(game) return game.state == GameState.READY)); - } - - public function broadcastGames() { - var packet = new GamesResponse().setGames(getReadyGames()); - for (personId in subscribers.keys()) { - var session = Session.sessions.get(personId); - session.send(packet); + public function new(creator:User) { + game = new Game() + .setId(idCounter++) + .setState(GameState.READY) + .setCreator(creator); + game.addPlayers(creator); + byGameId.set(game.id, this); + byPersonId.set(creator.uuid, this); + broadcast(new CreateGameResponse().setGame(game)); + broadcastGames(); } - } - public function broadcast(packet:Message) { - for (person in game.persons) { - var session = Session.sessions.get(person.id); - session.send(packet); + public static function getReadyGames():Array { + return Lambda.array(Lambda.filter(Lambda.map(GameManager.byGameId, function(gm) return gm.game), function(game) return game.state == GameState.READY)); } - } - public function join(person:Person) { - game.addPersons(person); - byPersonId.set(person.id, this); - broadcast(new JoinGameResponse().setGame(game)); - } - - public function leave(person:Person) { - game.setPersons(game.persons.filter(function(p) return p.id != person.id)); - byPersonId.remove(person.id); - var packet = new LeaveGameResponse().setGame(game); - Session.sessions.get(person.id).send(packet); - if (game.persons.length == 0/* || person.id == game.creator.id*/) { - stop(); - } else { - broadcast(packet); - } - } - - public function start() { - game.setState(GameState.STARTED); - engine = new Engine(); - engine.init(DEFAULT.CONFIG); - var changes = engine.initTanks(game.persons); - timer = new NekoTimer(30); - timer.run = update; - broadcast(new StartGameResponse().setGame(game)); - broadcast(new GameUpdateResponse().setChanges(changes)); - } - - public function stop() { - game.setState(GameState.ENDED); - game.setPersons([]); - byGameId.remove(game.id); - for (p in game.persons) byPersonId.remove(p.id); - if (timer != null) { - timer.stop(); - timer = null; - } - broadcast(new LeaveGameResponse().setGame(game)); - broadcastGames(); - } - - public function action(person:Person, action:GameActionRequest) { - var tank = engine.tanks.get(person.id); - switch (action.type) { - case GameActionType.SHOT: - var bullet = tank.shot(); - if (bullet != null) { - engine.mobileEntities.set(bullet.id, bullet); - changes.push(new GameChange() - .setType(GameChangeType.APPEND) - .setObjectType(GameObjectType.BULLET) - .setPersonId(bullet.personId) - .setObjectId(bullet.id) - .setX(bullet.x) - .setY(bullet.y) - .setDirectionX(bullet.direction.x) - .setDirectionY(bullet.direction.y) - ); + public function broadcastGames() { + var packet = new GamesResponse().setGames(getReadyGames()); + for (personId in subscribers.keys()) { + var session = Session.sessions.get(personId); + session.send(packet); } - case GameActionType.MOVE: - tank.move(Direction.from(action.directionX, action.directionY)); - changes.push(new GameChange() - .setType(GameChangeType.DIRECTION) - .setObjectType(GameObjectType.TANK) - .setPersonId(tank.personId) - .setObjectId(tank.id) - .setDirectionX(tank.direction.x) - .setDirectionY(tank.direction.y) - ); - case GameActionType.STOP: - tank.stop(); } - } - private function update() { - var changes = engine.update(); - changes = this.changes.concat(changes); - this.changes = []; - if (changes.length > 0) { - broadcast(new GameUpdateResponse().setChanges(changes)); + public function broadcast(packet:Message) { + for (player in game.players) { + var session = Session.sessions.get(player.uuid); + session.send(packet); + } + } + + public function join(person:Person) { + game.addPersons(person); + byPersonId.set(person.id, this); + broadcast(new JoinGameResponse().setGame(game)); + } + + public function leave(person:Person) { + game.setPersons(game.persons.filter(function(p) return p.id != person.id)); + byPersonId.remove(person.id); + var packet = new LeaveGameResponse().setGame(game); + Session.sessions.get(person.id).send(packet); + if (game.persons.length == 0/* || person.id == game.creator.id*/) { + stop(); + } else { + broadcast(packet); + } + } + + public function start() { + game.setState(GameState.STARTED); + engine = new Engine(); + engine.init(DEFAULT.CONFIG); + var changes = engine.initTanks(game.persons); + timer = new NekoTimer(30); + timer.run = update; + broadcast(new StartGameResponse().setGame(game)); + broadcast(new GameUpdateResponse().setChanges(changes)); + } + + public function stop() { + game.setState(GameState.ENDED); + game.setPersons([]); + byGameId.remove(game.id); + for (p in game.persons) byPersonId.remove(p.id); + if (timer != null) { + timer.stop(); + timer = null; + } + broadcast(new LeaveGameResponse().setGame(game)); + broadcastGames(); + } + + public function action(person:Person, action:GameActionRequest) { + var tank = engine.tanks.get(person.id); + switch (action.type) { + case GameActionType.SHOT: + var bullet = tank.shot(); + if (bullet != null) { + engine.mobileEntities.set(bullet.id, bullet); + changes.push(new GameChange() + .setType(GameChangeType.APPEND) + .setObjectType(GameObjectType.BULLET) + .setPersonId(bullet.personId) + .setObjectId(bullet.id) + .setX(bullet.x) + .setY(bullet.y) + .setDirectionX(bullet.direction.x) + .setDirectionY(bullet.direction.y) + ); + } + case GameActionType.MOVE: + tank.move(Direction.from(action.directionX, action.directionY)); + changes.push(new GameChange() + .setType(GameChangeType.DIRECTION) + .setObjectType(GameObjectType.TANK) + .setPersonId(tank.personId) + .setObjectId(tank.id) + .setDirectionX(tank.direction.x) + .setDirectionY(tank.direction.y) + ); + case GameActionType.STOP: + tank.stop(); + } + } + + private function update() { + var changes = engine.update(); + changes = this.changes.concat(changes); + this.changes = []; + if (changes.length > 0) { + broadcast(new GameUpdateResponse().setChanges(changes)); + } } - } } diff --git a/src/server/haxe/ru/m/tankz/server/session/Session.hx b/src/server/haxe/ru/m/tankz/server/session/Session.hx index 8218d86..daf06f7 100755 --- a/src/server/haxe/ru/m/tankz/server/session/Session.hx +++ b/src/server/haxe/ru/m/tankz/server/session/Session.hx @@ -27,7 +27,7 @@ typedef ServerConnection = IConnection; class Session { - public static var sessions:Map = new Map(); + public static var sessions:Map = new Map(); public var user(default, null):User; public var gameId(default, null):Int = -1; @@ -84,6 +84,7 @@ class Session { user = new User() .setUuid(request.uuid != null ? request.uuid : 'xxx') .setName(request.name); + sessions.set(user.uuid, this); return new LoginResponse().setUser(user); } diff --git a/src/server/haxe/ru/m/tankz/server/session/Thread.hx b/src/server/haxe/ru/m/tankz/server/session/Thread.hx index c7442f3..518ff4c 100644 --- a/src/server/haxe/ru/m/tankz/server/session/Thread.hx +++ b/src/server/haxe/ru/m/tankz/server/session/Thread.hx @@ -5,109 +5,108 @@ 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( 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); + 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( 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); }