[server] update
This commit is contained in:
@@ -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<Int, GameManager> = new Map<Int, GameManager>();
|
||||
public static var byPersonId:Map<Int, GameManager> = new Map<Int, GameManager>();
|
||||
public static var subscribers:Map<Int, Bool> = new Map<Int, Bool>();
|
||||
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<Int, Bool> = new Map<Int, Bool>();
|
||||
|
||||
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<GameChange> = new Array<GameChange>();
|
||||
//private var changes:Array<GameChange> = new Array<GameChange>();
|
||||
|
||||
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<Game> {
|
||||
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<Game> {
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ typedef ServerConnection = IConnection<Response, Request>;
|
||||
|
||||
class Session {
|
||||
|
||||
public static var sessions:Map<Int, Session> = new Map<Int, Session>();
|
||||
public static var sessions:Map<String, Session> = new Map<String, Session>();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<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);
|
||||
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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user