This commit is contained in:
2015-08-12 15:19:34 +03:00
parent 64e1cfdbfc
commit 64d36c6924
14 changed files with 314 additions and 30 deletions

View File

@@ -49,7 +49,11 @@ class PlayerTank extends Tank {
private function updateMove():Void {
if (moveQueue.length == 0) {
stop();
//stop();
Provider.get(IConnection).send(
new GameActionRequest()
.setType(GameActionType.STOP)
);
} else {
switch (keyBinding.get(moveQueue[0])) {
case TankAction.MOVE(direction):

View File

@@ -1,5 +1,9 @@
package ru.m.tankz.view.frames;
import ru.m.tankz.core.MobileEntity;
import ru.m.tankz.core.Direction;
import ru.m.tankz.proto.GameObjectType;
import ru.m.tankz.proto.GameChangeType;
import ru.m.tankz.game.ClientTankz;
import protohx.Message;
import ru.m.tankz.proto.GameUpdateResponse;
@@ -32,6 +36,7 @@ class GameFrame extends VGroupView implements ViewBuilder implements IPacketHand
game.init(persons, DEFAULT.CONFIG);
content.addEventListener(Event.ENTER_FRAME, updateGame);
Provider.get(IConnection).packetHandler.addListener(this);
render.draw(game);
}
public function onHide():Void {
@@ -41,12 +46,47 @@ class GameFrame extends VGroupView implements ViewBuilder implements IPacketHand
}
private function updateGame(_):Void {
game.update();
render.draw(game);
//game.update();
//render.draw(game);
}
public function onGameUpdateResponse(packet:GameUpdateResponse):Void {
for (change in packet.changes) {
switch (change.type) {
case GameChangeType.DIRECTION:
switch (change.objectType) {
case GameObjectType.TANK:
for (tank in game.tanks) {
if (tank.id == change.objectId) {
tank.direction = new Direction(change.directionX, change.directionY);
break;
}
}
}
case GameChangeType.MOVED:
switch (change.objectType) {
case GameObjectType.TANK:
for (tank in game.tanks) {
if (tank.id == change.objectId) {
tank.x = change.x;
tank.y = change.y;
break;
}
}
}
case GameChangeType.APPEND:
switch (change.objectType) {
case GameObjectType.BULLET:
for (tank in game.tanks) {
if (tank.id == change.parentObjectId) {
tank.bullets.push(new MobileEntity(0, change.x, change.y, 0, new Direction(change.directionX, change.directionY)));
break;
}
}
}
}
}
render.draw(game);
}
public function onPacket(packet:Message):Void {}

View File

@@ -42,11 +42,11 @@ class BaseConnection implements IConnection {
}
}
public function send(packet:Message):Void {
L.d("Send", Type.getClassName(Type.getClass(packet)).split(".").pop());
//L.d("Send", Type.getClassName(Type.getClass(packet)).split(".").pop());
}
public function receive(packet:Message):Void {
L.d("Receive", Type.getClassName(Type.getClass(packet)).split(".").pop());
//L.d("Receive", Type.getClassName(Type.getClass(packet)).split(".").pop());
var name = "on" + Type.getClassName(Type.getClass(packet)).split(".").pop();
packetHandler.dispatch(function(h) {
var method = Reflect.field(h, name);

View File

@@ -17,7 +17,7 @@ class NekoWebConnection extends NekoConnection {
}
override public function send(packet:Message):Void {
L.d("Send", Type.getClassName(Type.getClass(packet)).split(".").pop());
//L.d("Send", Type.getClassName(Type.getClass(packet)).split(".").pop());
try {
var data = WebSocketTools.packet2string(packet, builder);
writeData(data, socket);

View File

@@ -2,8 +2,8 @@ package ru.m.tankz.core;
class Entity implements IEntity {
public var x(default, default):Float;
public var y(default, default):Float;
public var x(default, default):Float = 0;
public var y(default, default):Float = 0;
public var width(default, default):Float;
public var height(default, default):Float;

View File

@@ -1,6 +1,8 @@
package ru.m.tankz.core;
interface IMobileEntity extends IEntity {
public var id(default, null):Int;
public var mx(default, default):Float;
public var my(default, default):Float;

View File

@@ -1,7 +1,6 @@
package ru.m.tankz.core;
interface ITank extends IMobileEntity {
public var id(default, null):Int;
public var bullets:Array<IMobileEntity>;
public function shot():Void;

View File

@@ -1,15 +1,17 @@
package ru.m.tankz.core;
class MobileEntity extends Entity implements IMobileEntity {
public var id(default, null):Int;
public var mx(default, default):Float;
public var my(default, default):Float;
public var mx(default, default):Float = 0;
public var my(default, default):Float = 0;
public var speed(default, null):Float;
public var speed(default, null):Float = 0;
public var direction(default, default):Direction;
public function new(x:Float, y:Float, speed:Float, direction:Direction = null) {
public function new(id:Int, x:Float, y:Float, speed:Float, direction:Direction = null) {
super(x, y);
this.id = id;
this.speed = speed;
this.direction = direction == null ? Direction.BOTTOM : direction;
}

View File

@@ -7,11 +7,10 @@ enum TankAction {
class Tank extends MobileEntity implements ITank {
public var id(default, null):Int;
public var bullets:Array<IMobileEntity>;
public function new(id:Int, x:Float, y:Float) {
super(x, y, 4);
super(id, x, y, 4);
this.id = id;
bullets = new Array<IMobileEntity>();
width = 34;
@@ -20,7 +19,7 @@ class Tank extends MobileEntity implements ITank {
public function shot():Void {
if (bullets.length >= 5) return;
var bullet = new MobileEntity(x + width / 2 - 5, y + height / 2 - 5, 6, direction);
var bullet = new MobileEntity(0, x + width / 2 - 5, y + height / 2 - 5, 6, direction);
bullet.width = 10;
bullet.height = 10;
bullet.move(direction);

View File

@@ -93,6 +93,7 @@ message ExitGameResponse {
enum GameActionType {
MOVE = 1;
SHOT = 2;
STOP = 3;
}
message GameActionRequest {
@@ -103,6 +104,7 @@ message GameActionRequest {
enum GameObjectType {
TANK = 1;
BULLET = 2;
}
enum GameChangeType {
@@ -110,14 +112,18 @@ enum GameChangeType {
DESTROED = 2;
MODIFIED = 3;
APPEND = 4;
DIRECTION = 5;
}
message GameChange {
required GameChangeType type = 1;
required GameObjectType objectType = 2;
required int32 objectId = 3;
optional int32 newX = 4;
optional int32 newY = 5;
optional int32 parentObjectId = 4;
optional float x = 5;
optional float y = 6;
optional int32 directionX = 7;
optional int32 directionY = 8;
}
message GameUpdateResponse {

View File

@@ -0,0 +1,12 @@
package ru.m.tankz.server.game;
import ru.m.tankz.game.ITankz;
class Game {
private var tankz:ITankz;
public function new() {
}
}

View File

@@ -1,5 +1,12 @@
package ru.m.tankz.server.session;
import ru.m.tankz.proto.GameObjectType;
import ru.m.tankz.proto.GameChangeType;
import ru.m.tankz.proto.GameChange;
import ru.m.tankz.config.TankzConfig.DEFAULT;
import ru.m.tankz.proto.GameUpdateResponse;
import haxe.Timer;
import ru.m.tankz.core.Direction;
import ru.m.tankz.game.Tankz;
import ru.m.tankz.game.ITankz;
import ru.m.tankz.proto.GameActionType;
@@ -31,6 +38,39 @@ import protohx.Message;
import ru.m.core.connect.IConnection;
import sys.net.Socket;
class NekoTimer {
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 dynamic function run() {}
public function stop() {
stopped = true;
}
}
typedef ObjectState = {
var x:Float;
var y:Float;
var d:Direction;
}
class GameCenter {
@@ -39,21 +79,27 @@ class GameCenter {
private var created:Map<Int, Int>;
private var persons:Map<Int, Int>;
private var running:Map<Int, ITankz>;
public var running:Map<Int, ITankz>;
private var timers:Map<Int, NekoTimer>;
public function new() {
games = new Map<Int, Game>();
created = new Map<Int, Int>();
persons = new Map<Int, Int>();
running = new Map<Int, ITankz>();
timers = new Map<Int, NekoTimer>();
}
public function getReadyGames():Array<Game> {
return Lambda.array(games).filter(function(g) return g.state == GameState.READY);
}
public function getCreatedGame(person_id:Int):Game {
return games.get(created.get(person_id));
public function getCreatedGame(peronsId:Int):Game {
return games.get(created.get(peronsId));
}
public function getPersonGameId(personId:Int):Int {
return persons.get(personId);
}
public function createGame(person:Person):Game {
@@ -82,6 +128,10 @@ class GameCenter {
game.persons.remove(person);
if (game.persons.length == 0) {
games.remove(game.id);
if (timers.exists(game.id)) {
timers.get(game.id).stop();
timers.remove(game.id);
}
}
break;
}
@@ -91,9 +141,57 @@ class GameCenter {
public function start(gameId:Int):Void {
if (games.exists(gameId)) {
games.get(gameId).setState(GameState.STARTED);
var game:Game = games.get(gameId);
game.setState(GameState.STARTED);
var tankz = new Tankz();
tankz.init(game.persons, DEFAULT.CONFIG);
running.set(gameId, tankz);
var timer = new NekoTimer(30);
timer.run = buildUpdater(gameId, tankz);
timers.set(gameId, timer);
broadcast(gameId, new StartGameResponse().setGame(game));
}
}
private function buildUpdater(gameId:Int, tankz:ITankz):Void->Void {
return function() {
var states = new Map<Int, ObjectState>();
for (tank in tankz.tanks) {
states.set(tank.id, {
x: tank.x,
y: tank.y,
d: tank.direction
});
}
tankz.update();
var changes = new Array<GameChange>();
for (tank in tankz.tanks) {
if (states.exists(tank.id)) {
var state = states.get(tank.id);
if (state.d != tank.direction) {
trace("DDD");
changes.push(new GameChange()
.setType(GameChangeType.DIRECTION)
.setObjectType(GameObjectType.TANK)
.setObjectId(tank.id)
.setDirectionX(tank.direction.x)
.setDirectionY(tank.direction.y)
);
}
if (state.x != tank.x || state.y != tank.y) {
changes.push(new GameChange()
.setType(GameChangeType.MOVED)
.setObjectType(GameObjectType.TANK)
.setObjectId(tank.id)
.setX(tank.x)
.setY(tank.y)
);
}
}
}
if (changes.length > 0) {
broadcast(gameId, new GameUpdateResponse().setChanges(changes));
}
}
}
@@ -209,8 +307,7 @@ class Session implements IConnectionHandler implements IPacketHandler {
public function onStartGameRequest(packet:StartGameRequest):Void {
var game:Game = games.getCreatedGame(person.id);
game.setState(GameState.STARTED);
games.broadcast(game.id, new StartGameResponse().setGame(game));
games.start(game.id);
}
public function onExitGameRequest(packet:ExitGameRequest):Void {
@@ -219,11 +316,18 @@ class Session implements IConnectionHandler implements IPacketHandler {
}
public function onGameActionRequest(packet:GameActionRequest):Void {
switch (packet.type) {
case GameActionType.SHOT:
case GameActionType.MOVE:
var game:ITankz = games.running.get(games.getPersonGameId(person.id));
for (tank in game.tanks) {
if (tank.id == person.id) {
switch (packet.type) {
case GameActionType.SHOT:
tank.shot();
case GameActionType.MOVE:
tank.move(new Direction(packet.directionX, packet.directionY));
case GameActionType.STOP:
tank.stop();
}
}
}
}

View File

@@ -0,0 +1,113 @@
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);
}