[server] add session
This commit is contained in:
11
gulpfile.js
11
gulpfile.js
@@ -23,6 +23,14 @@ exports.clean = function clean() {
|
|||||||
return gulp.src('target/*', {read: false}).pipe(gulpClean());
|
return gulp.src('target/*', {read: false}).pipe(gulpClean());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.generate = function generate() {
|
||||||
|
return new Haxe().haxelib(['run', 'protohx', 'generate', 'protohx.json']).then(({stdout}) => {
|
||||||
|
if (stdout.indexOf('FAIL') > -1) {
|
||||||
|
throw stdout;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const config = new Project.Config({
|
const config = new Project.Config({
|
||||||
meta: {
|
meta: {
|
||||||
title: 'Puzzle\'z',
|
title: 'Puzzle\'z',
|
||||||
@@ -52,6 +60,7 @@ const app = new Project(
|
|||||||
config.branch({
|
config.branch({
|
||||||
name: 'app',
|
name: 'app',
|
||||||
sources: [
|
sources: [
|
||||||
|
'src-gen/haxe',
|
||||||
'src/common/haxe',
|
'src/common/haxe',
|
||||||
'src/app/haxe',
|
'src/app/haxe',
|
||||||
],
|
],
|
||||||
@@ -79,6 +88,7 @@ const server = new Project(
|
|||||||
config.branch({
|
config.branch({
|
||||||
name: 'server',
|
name: 'server',
|
||||||
sources: [
|
sources: [
|
||||||
|
'src-gen/haxe',
|
||||||
'src/common/haxe',
|
'src/common/haxe',
|
||||||
'src/server/haxe',
|
'src/server/haxe',
|
||||||
],
|
],
|
||||||
@@ -90,6 +100,7 @@ module.exports.publish = publish(packageInfo.name, packageInfo.version, Config.P
|
|||||||
|
|
||||||
const defaultSeries = [
|
const defaultSeries = [
|
||||||
exports.clean,
|
exports.clean,
|
||||||
|
exports.generate,
|
||||||
module.exports['app:flash:build'],
|
module.exports['app:flash:build'],
|
||||||
module.exports['app:flash:html'],
|
module.exports['app:flash:html'],
|
||||||
module.exports['app:html5:build'],
|
module.exports['app:html5:build'],
|
||||||
|
|||||||
@@ -16,7 +16,8 @@
|
|||||||
"lime": "7.7.0",
|
"lime": "7.7.0",
|
||||||
"openfl": "8.9.6",
|
"openfl": "8.9.6",
|
||||||
"hxcpp": "4.0.52",
|
"hxcpp": "4.0.52",
|
||||||
"svg": "1.1.3"
|
"svg": "1.1.3",
|
||||||
|
"haxe-crypto": "0.0.7"
|
||||||
},
|
},
|
||||||
"haxe": "4.0.5",
|
"haxe": "4.0.5",
|
||||||
"dependencies": {}
|
"dependencies": {}
|
||||||
|
|||||||
12
protohx.json
Executable file
12
protohx.json
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"protoPath": "src/common/proto",
|
||||||
|
"protoFiles": [
|
||||||
|
"src/common/proto/core.proto",
|
||||||
|
"src/common/proto/game.proto",
|
||||||
|
"src/common/proto/room.proto",
|
||||||
|
"src/common/proto/pack.proto"
|
||||||
|
],
|
||||||
|
"cleanOut": true,
|
||||||
|
"haxeOut": "src-gen/haxe",
|
||||||
|
"javaOut": null
|
||||||
|
}
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
package ru.m.puzzlez;
|
package ru.m.puzzlez;
|
||||||
|
|
||||||
|
import hw.connect.ConnectionFactory;
|
||||||
|
import ru.m.puzzlez.proto.pack.Request;
|
||||||
|
import ru.m.puzzlez.proto.pack.Response;
|
||||||
|
import hw.connect.IConnection;
|
||||||
import hw.log.TraceLogger;
|
import hw.log.TraceLogger;
|
||||||
import hw.app.App;
|
import hw.app.App;
|
||||||
import hw.app.Const;
|
import hw.app.Const;
|
||||||
@@ -12,6 +16,7 @@ import ru.m.update.Updater;
|
|||||||
class PuzzlezApp {
|
class PuzzlezApp {
|
||||||
|
|
||||||
@:provide static var updater:Updater;
|
@:provide static var updater:Updater;
|
||||||
|
@:provide static var connection:IConnection<Response, Request>;
|
||||||
|
|
||||||
public static function main() {
|
public static function main() {
|
||||||
// ToDo: fix @:provide macro
|
// ToDo: fix @:provide macro
|
||||||
@@ -19,6 +24,8 @@ class PuzzlezApp {
|
|||||||
ImageStorage;
|
ImageStorage;
|
||||||
SettingsStorage;
|
SettingsStorage;
|
||||||
L.push(new TraceLogger());
|
L.push(new TraceLogger());
|
||||||
|
connection = ConnectionFactory.buildClientConnection("127.0.0.1", 6000, Request);
|
||||||
|
connection.connect().then(_ -> L.i("connect", "success")).catchError(error -> L.e("connect", "", error));
|
||||||
updater = new Updater(Const.instance.VERSION, "https://shmyga.ru/repo/puzzlez/packages.json");
|
updater = new Updater(Const.instance.VERSION, "https://shmyga.ru/repo/puzzlez/packages.json");
|
||||||
var app = new App();
|
var app = new App();
|
||||||
app.theme = new PuzzlezTheme();
|
app.theme = new PuzzlezTheme();
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ class Render extends SpriteView implements IRender {
|
|||||||
}
|
}
|
||||||
activePart = pointPart;
|
activePart = pointPart;
|
||||||
tableView.setChildIndex(activePart, tableView.numChildren - 1);
|
tableView.setChildIndex(activePart, tableView.numChildren - 1);
|
||||||
activePoint = tableView.globalToLocal(point);
|
activePoint = RenderUtil.convertPoint(tableView.globalToLocal(point));
|
||||||
tableView.stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
|
tableView.stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
|
||||||
tableView.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
|
tableView.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
|
||||||
signal.emit(ACTION(PART_TAKE(activePart.id)));
|
signal.emit(ACTION(PART_TAKE(activePart.id)));
|
||||||
@@ -194,14 +194,14 @@ class Render extends SpriteView implements IRender {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private function onMouseMove(event:MouseEvent):Void {
|
private function onMouseMove(event:MouseEvent):Void {
|
||||||
var newPoint:Point = tableView.globalToLocal(new FlashPoint(event.stageX, event.stageY));
|
var newPoint:Point = RenderUtil.convertPoint(tableView.globalToLocal(new FlashPoint(event.stageX, event.stageY)));
|
||||||
var partPosition = activePart.position.add(newPoint).subtract(activePoint);
|
var partPosition = activePart.position.add(newPoint).subtract(activePoint);
|
||||||
signal.emit(ACTION(PART_MOVE(activePart.id, partPosition.clone())));
|
signal.emit(ACTION(PART_MOVE(activePart.id, partPosition.clone())));
|
||||||
activePoint = newPoint;
|
activePoint = newPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function onMouseUp(event:MouseEvent):Void {
|
private function onMouseUp(event:MouseEvent):Void {
|
||||||
var newPoint:Point = tableView.globalToLocal(new FlashPoint(event.stageX, event.stageY));
|
var newPoint:Point = RenderUtil.convertPoint(tableView.globalToLocal(new FlashPoint(event.stageX, event.stageY)));
|
||||||
var partPosition = activePart.position.add(newPoint).subtract(activePoint);
|
var partPosition = activePart.position.add(newPoint).subtract(activePoint);
|
||||||
signal.emit(ACTION(PART_PUT(activePart.id, partPosition.clone())));
|
signal.emit(ACTION(PART_PUT(activePart.id, partPosition.clone())));
|
||||||
tableView.stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
|
tableView.stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
|
||||||
|
|||||||
@@ -116,4 +116,8 @@ class RenderUtil {
|
|||||||
var height = source.height * s;
|
var height = source.height * s;
|
||||||
return new Rectangle((target.width - width) / 2, (target.height - height) / 2, width, height);
|
return new Rectangle((target.width - width) / 2, (target.height - height) / 2, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function convertPoint(point:flash.geom.Point):Point {
|
||||||
|
return new Point(point.x, point.y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/common/proto/core.proto
Normal file
12
src/common/proto/core.proto
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package ru.m.puzzlez.proto.core;
|
||||||
|
|
||||||
|
message UserProto {
|
||||||
|
string uuid = 1;
|
||||||
|
string name = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GameProto {
|
||||||
|
int32 id = 1;
|
||||||
|
}
|
||||||
8
src/common/proto/game.proto
Normal file
8
src/common/proto/game.proto
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package ru.m.puzzlez.proto.game;
|
||||||
|
|
||||||
|
message GameEventProto {
|
||||||
|
int32 time = 1;
|
||||||
|
string event = 2;
|
||||||
|
}
|
||||||
55
src/common/proto/pack.proto
Normal file
55
src/common/proto/pack.proto
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
import "core.proto";
|
||||||
|
import "game.proto";
|
||||||
|
import "room.proto";
|
||||||
|
|
||||||
|
package ru.m.puzzlez.proto.pack;
|
||||||
|
|
||||||
|
message ErrorResponse {
|
||||||
|
int32 code = 1;
|
||||||
|
string message = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message LoginRequest {
|
||||||
|
string uuid = 1;
|
||||||
|
string name = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message LoginResponse {
|
||||||
|
ru.m.puzzlez.proto.core.UserProto user = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message LogoutRequest {}
|
||||||
|
|
||||||
|
message LogoutResponse {}
|
||||||
|
|
||||||
|
message GameEventRequest {
|
||||||
|
ru.m.puzzlez.proto.game.GameEventProto event = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GameEventResponse {
|
||||||
|
ru.m.puzzlez.proto.game.GameEventProto event = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Request {
|
||||||
|
oneof content {
|
||||||
|
LoginRequest login = 1;
|
||||||
|
LogoutRequest logout = 2;
|
||||||
|
ru.m.puzzlez.proto.room.RoomRequest room = 3;
|
||||||
|
ru.m.puzzlez.proto.room.RoomListRequest roomList = 4;
|
||||||
|
GameEventRequest gameEvent = 6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message Response {
|
||||||
|
oneof content {
|
||||||
|
LoginResponse login = 1;
|
||||||
|
LogoutResponse logout = 2;
|
||||||
|
ru.m.puzzlez.proto.room.RoomResponse room = 3;
|
||||||
|
ru.m.puzzlez.proto.room.RoomListResponse roomList = 4;
|
||||||
|
GameEventResponse gameEvent = 6;
|
||||||
|
|
||||||
|
ErrorResponse error = 999;
|
||||||
|
}
|
||||||
|
}
|
||||||
64
src/common/proto/room.proto
Normal file
64
src/common/proto/room.proto
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
import "core.proto";
|
||||||
|
|
||||||
|
package ru.m.puzzlez.proto.room;
|
||||||
|
|
||||||
|
message SlotProto {
|
||||||
|
string team = 3;
|
||||||
|
int32 index = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RoomSlotProto {
|
||||||
|
SlotProto slot = 1;
|
||||||
|
ru.m.puzzlez.proto.core.UserProto user = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RoomProto {
|
||||||
|
ru.m.puzzlez.proto.core.GameProto game = 1;
|
||||||
|
ru.m.puzzlez.proto.core.UserProto creator = 2;
|
||||||
|
repeated ru.m.puzzlez.proto.core.UserProto users = 3;
|
||||||
|
repeated RoomSlotProto slots = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CreateRequest {
|
||||||
|
string type = 2;
|
||||||
|
int32 level = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message JoinRequest {
|
||||||
|
int32 gameId = 1;
|
||||||
|
bool restore = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message LeaveRequest {
|
||||||
|
}
|
||||||
|
|
||||||
|
message SlotRequest {
|
||||||
|
SlotProto slot = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message StartRequest {
|
||||||
|
}
|
||||||
|
|
||||||
|
message RoomRequest {
|
||||||
|
oneof content {
|
||||||
|
CreateRequest create = 1;
|
||||||
|
JoinRequest join = 2;
|
||||||
|
LeaveRequest leave = 3;
|
||||||
|
SlotRequest slot = 4;
|
||||||
|
StartRequest start = 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message RoomResponse {
|
||||||
|
RoomProto room = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RoomListRequest {
|
||||||
|
bool subscribe = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RoomListResponse {
|
||||||
|
repeated RoomProto rooms = 1;
|
||||||
|
}
|
||||||
179
src/server/haxe/ru/m/puzzlez/GameSession.hx
Normal file
179
src/server/haxe/ru/m/puzzlez/GameSession.hx
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
package ru.m.puzzlez;
|
||||||
|
|
||||||
|
import com.hurlant.crypto.extra.UUID;
|
||||||
|
import com.hurlant.crypto.prng.Random;
|
||||||
|
import haxe.Serializer;
|
||||||
|
import haxe.Unserializer;
|
||||||
|
import hw.connect.session.ProtoSession;
|
||||||
|
import hw.log.BaseLogger.LoggerUtil;
|
||||||
|
import ru.m.puzzlez.core.GameEvent;
|
||||||
|
import ru.m.puzzlez.game.IGameManager;
|
||||||
|
import ru.m.puzzlez.game.ServerGame;
|
||||||
|
import ru.m.puzzlez.proto.core.UserProto;
|
||||||
|
import ru.m.puzzlez.proto.game.GameEventProto;
|
||||||
|
import ru.m.puzzlez.proto.pack.ErrorResponse;
|
||||||
|
import ru.m.puzzlez.proto.pack.GameEventResponse;
|
||||||
|
import ru.m.puzzlez.proto.pack.LoginResponse;
|
||||||
|
import ru.m.puzzlez.proto.pack.LogoutResponse;
|
||||||
|
import ru.m.puzzlez.proto.pack.Request;
|
||||||
|
import ru.m.puzzlez.proto.pack.Response;
|
||||||
|
import ru.m.puzzlez.proto.room.RoomListResponse;
|
||||||
|
import ru.m.puzzlez.proto.room.RoomResponse;
|
||||||
|
import sys.net.Socket;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
private var subscribed:Bool;
|
||||||
|
private var tag(get, never):String;
|
||||||
|
|
||||||
|
private function get_tag():String {
|
||||||
|
return '[${id}|${user == null ? '-' : user.name}|${gameId == -1 ? '-' : Std.string(gameId)}]';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function new(socket:Socket) {
|
||||||
|
super(socket, Request);
|
||||||
|
gameId = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function sendError(code:Int, message:String):Void {
|
||||||
|
send(new Response().setError(new ErrorResponse().setCode(code).setMessage(message)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function listGame():RoomListResponse {
|
||||||
|
var games = gameManager.games;
|
||||||
|
return new RoomListResponse().setRooms([for (game in games) game.room]);
|
||||||
|
}
|
||||||
|
|
||||||
|
override public function send(packet:Response):Void {
|
||||||
|
#if proto_debug L.d(TAG, '$tag send: ${packet}'); #end
|
||||||
|
try {
|
||||||
|
super.send(packet);
|
||||||
|
} catch (error:Dynamic) {
|
||||||
|
L.e(TAG, '$tag send ', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function logout(leave:Bool = true):Void {
|
||||||
|
gameId = -1;
|
||||||
|
gameManager.disconnect(this);
|
||||||
|
if (user != null && leave) {
|
||||||
|
gameManager.leave(user);
|
||||||
|
user = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function join(gameId:Int, restore:Bool):Void {
|
||||||
|
this.gameId = gameId;
|
||||||
|
gameManager.join(gameId, user);
|
||||||
|
var game = gameManager.gamesById[gameId];
|
||||||
|
if (restore) {
|
||||||
|
// ToDo: restore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override private function onRequest(request:Request):Void {
|
||||||
|
#if proto_debug L.d(TAG, '$tag onRequest: ${request}'); #end
|
||||||
|
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)));
|
||||||
|
if (gameManager.gamesByUser.exists(user.uuid)) {
|
||||||
|
join(gameManager.gamesByUser[user.uuid].id, false);
|
||||||
|
}
|
||||||
|
// logout
|
||||||
|
} else if (request.hasLogout()) {
|
||||||
|
logout();
|
||||||
|
send(new Response().setLogout(new LogoutResponse()));
|
||||||
|
// room
|
||||||
|
} else if (request.hasRoom()) {
|
||||||
|
if (request.room.hasCreate()) {
|
||||||
|
var game = gameManager.create(user);
|
||||||
|
gameId = game.id;
|
||||||
|
send(new Response().setRoom(new RoomResponse().setRoom(game.room)));
|
||||||
|
} else if (request.room.hasJoin()) {
|
||||||
|
join(request.room.join.gameId, request.room.join.restore);
|
||||||
|
} else if (request.room.hasLeave()) {
|
||||||
|
gameManager.leave(user);
|
||||||
|
} else if (request.room.hasSlot()) {
|
||||||
|
gameManager.slot(user, request.room.slot.slot);
|
||||||
|
} else if (request.room.hasStart()) {
|
||||||
|
gameManager.start(gameId);
|
||||||
|
}
|
||||||
|
// room list
|
||||||
|
} else if (request.hasRoomList()) {
|
||||||
|
subscribed = request.roomList.subscribe;
|
||||||
|
if (subscribed) {
|
||||||
|
send(new Response().setRoomList(listGame()));
|
||||||
|
}
|
||||||
|
} else if (request.hasGameEvent()) {
|
||||||
|
if (gameManager.gamesById.exists(gameId)) {
|
||||||
|
var event:GameEvent = Unserializer.run(request.gameEvent.event.event);
|
||||||
|
// ToDo: emit event
|
||||||
|
///gameManager.gamesById[gameId].gameEventSignal.emit(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error:Dynamic) {
|
||||||
|
L.e(TAG, '$tag onRequest ', error);
|
||||||
|
sendError(500, LoggerUtil.printError(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override public function disconnect():Void {
|
||||||
|
L.d(TAG, '$tag disconnect');
|
||||||
|
logout(false);
|
||||||
|
super.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onCreate(game:ServerGame):Void {
|
||||||
|
if (subscribed) {
|
||||||
|
send(new Response().setRoomList(listGame()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onChange(game:ServerGame, change:GameChange):Void {
|
||||||
|
if (gameId == game.id) {
|
||||||
|
switch change {
|
||||||
|
case LEAVE(user):
|
||||||
|
if (user.uuid == this.user.uuid) {
|
||||||
|
gameId = -1;
|
||||||
|
send(new Response().setRoom(new RoomResponse()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case _:
|
||||||
|
}
|
||||||
|
send(new Response().setRoom(new RoomResponse().setRoom(game.room)));
|
||||||
|
}
|
||||||
|
if (subscribed) {
|
||||||
|
send(new Response().setRoomList(listGame()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onDelete(game:ServerGame):Void {
|
||||||
|
if (gameId == game.id) {
|
||||||
|
gameId = -1;
|
||||||
|
send(new Response().setRoom(new RoomResponse()));
|
||||||
|
}
|
||||||
|
if (subscribed) {
|
||||||
|
send(new Response().setRoomList(listGame()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onEvent(game:ServerGame, event:GameEvent):Void {
|
||||||
|
if (gameId == game.id) {
|
||||||
|
send(new Response().setGameEvent(new GameEventResponse().setEvent(new GameEventProto().setTime(0).setEvent(Serializer.run(event)))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,34 +5,34 @@ import cpp.net.ThreadServer;
|
|||||||
import sys.net.Socket;
|
import sys.net.Socket;
|
||||||
import haxe.io.Bytes;
|
import haxe.io.Bytes;
|
||||||
|
|
||||||
typedef Session = Dynamic;
|
|
||||||
typedef Message = Bytes;
|
typedef Message = Bytes;
|
||||||
|
|
||||||
typedef ClientMessage<M> = {
|
typedef ClientMessage<M> = {
|
||||||
var msg:M;
|
var msg:M;
|
||||||
var bytes:Int;
|
var bytes:Int;
|
||||||
}
|
}
|
||||||
|
|
||||||
class PuzzlezServer extends ThreadServer<Session, Message> {
|
class PuzzlezServer extends ThreadServer<GameSession, Message> {
|
||||||
|
|
||||||
private static inline var TAG = 'Server';
|
private static inline var TAG = 'Server';
|
||||||
|
|
||||||
override public function clientConnected(socket:Socket):Session {
|
override public function clientConnected(socket:Socket):GameSession {
|
||||||
var session = null; // new Session(socket);
|
var session = new GameSession(socket);
|
||||||
L.d(TAG, 'Client connected');
|
L.d(TAG, 'Client connected');
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
override public function clientDisconnected(session:Session) {
|
override public function clientDisconnected(session:GameSession) {
|
||||||
L.d(TAG, 'Client disconnected');
|
L.d(TAG, 'Client disconnected');
|
||||||
//session.disconnect();
|
//session.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
override public function readClientMessage(session:Session, buf:Bytes, pos:Int, len:Int): ClientMessage<Message> {
|
override public function readClientMessage(session:GameSession, buf:Bytes, pos:Int, len:Int): ClientMessage<Message> {
|
||||||
//L.d(TAG, 'Client message: ${buf}');
|
//L.d(TAG, 'Client message: ${buf}');
|
||||||
return {msg: buf.sub(pos, len), bytes: len};
|
return {msg: buf.sub(pos, len), bytes: len};
|
||||||
}
|
}
|
||||||
|
|
||||||
override public function clientMessage(session:Session, message:Message) {
|
override public function clientMessage(session:GameSession, message:Message) {
|
||||||
//session.pushData(bytes);
|
//session.pushData(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ class PuzzlezServer extends ThreadServer<Session, Message> {
|
|||||||
L.d(TAG, 'Running');
|
L.d(TAG, 'Running');
|
||||||
L.i(TAG, 'Build: ${CompilationOption.get("build")}');
|
L.i(TAG, 'Build: ${CompilationOption.get("build")}');
|
||||||
var host:String = Sys.args().length > 0 ? Sys.args()[0] : "0.0.0.0";
|
var host:String = Sys.args().length > 0 ? Sys.args()[0] : "0.0.0.0";
|
||||||
var port:Int = Sys.args().length > 1 ? Std.parseInt(Sys.args()[1]) : 5000;
|
var port:Int = Sys.args().length > 1 ? Std.parseInt(Sys.args()[1]) : 6000;
|
||||||
var wserver = new PuzzlezServer();
|
var wserver = new PuzzlezServer();
|
||||||
L.i(TAG, 'Start on ${host}:${port}');
|
L.i(TAG, 'Start on ${host}:${port}');
|
||||||
wserver.run(host, port);
|
wserver.run(host, port);
|
||||||
|
|||||||
4
src/server/haxe/ru/m/puzzlez/game/GameListener.hx
Normal file
4
src/server/haxe/ru/m/puzzlez/game/GameListener.hx
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
package ru.m.puzzlez.game;
|
||||||
|
|
||||||
|
interface GameListener {
|
||||||
|
}
|
||||||
124
src/server/haxe/ru/m/puzzlez/game/GameManager.hx
Normal file
124
src/server/haxe/ru/m/puzzlez/game/GameManager.hx
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
package ru.m.puzzlez.game;
|
||||||
|
|
||||||
|
import ru.m.puzzlez.core.GameEvent;
|
||||||
|
import ru.m.puzzlez.game.IGameManager;
|
||||||
|
import ru.m.puzzlez.proto.room.SlotProto;
|
||||||
|
import ru.m.puzzlez.proto.room.RoomSlotProto;
|
||||||
|
import ru.m.puzzlez.proto.core.GameProto;
|
||||||
|
import ru.m.puzzlez.proto.room.RoomProto;
|
||||||
|
import ru.m.puzzlez.proto.core.UserProto;
|
||||||
|
|
||||||
|
class _GameListener implements GameListener {
|
||||||
|
private var game:ServerGame;
|
||||||
|
private var dispatcher:IGameManager;
|
||||||
|
|
||||||
|
public function new(game:ServerGame, dispatcher:IGameManager) {
|
||||||
|
this.game = game;
|
||||||
|
this.dispatcher = dispatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onGameEvent(event:GameEvent):Void {
|
||||||
|
dispatcher.dispatchEvent(game, event);
|
||||||
|
switch event {
|
||||||
|
case COMPLETE:
|
||||||
|
dispatcher.delete(game.id);
|
||||||
|
dispose();
|
||||||
|
case _:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dispose():Void {
|
||||||
|
game.disconnect(this);
|
||||||
|
game = null;
|
||||||
|
dispatcher = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@: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>;
|
||||||
|
|
||||||
|
private var counter:Int;
|
||||||
|
|
||||||
|
public function new() {
|
||||||
|
counter = 0;
|
||||||
|
games = [];
|
||||||
|
gamesById = new Map();
|
||||||
|
gamesByCreator = new Map();
|
||||||
|
gamesByUser = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(user:UserProto):ServerGame {
|
||||||
|
if (gamesByCreator.exists(user.uuid)) {
|
||||||
|
delete(gamesByCreator[user.uuid].id);
|
||||||
|
}
|
||||||
|
var room = new RoomProto()
|
||||||
|
.setGame(
|
||||||
|
new GameProto()
|
||||||
|
.setId(++counter)
|
||||||
|
)
|
||||||
|
.setCreator(user);
|
||||||
|
var game = new ServerGame(room);
|
||||||
|
var slots:Array<RoomSlotProto> = [];
|
||||||
|
game.room.setSlots(slots);
|
||||||
|
games.push(game);
|
||||||
|
gamesById[game.id] = game;
|
||||||
|
gamesByCreator[game.room.creator.uuid] = game;
|
||||||
|
createSignal.emit(game);
|
||||||
|
join(game.id, user);
|
||||||
|
return game;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function join(gameId:Int, user:UserProto):Void {
|
||||||
|
if (gamesById.exists(gameId)) {
|
||||||
|
var game = gamesById[gameId];
|
||||||
|
game.join(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.id);
|
||||||
|
gamesByCreator.remove(game.room.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];
|
||||||
|
gamesByUser.remove(user.uuid);
|
||||||
|
game.leave(user);
|
||||||
|
changeSignal.emit(game, LEAVE(user));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function slot(user:UserProto, slot:SlotProto):Void {
|
||||||
|
if (gamesByUser.exists(user.uuid)) {
|
||||||
|
var game = gamesByUser[user.uuid];
|
||||||
|
game.slot(user, slot);
|
||||||
|
changeSignal.emit(game, SLOT(user, slot));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function start(gameId:Int):Void {
|
||||||
|
if (gamesById.exists(gameId)) {
|
||||||
|
var game:ServerGame = gamesById[gameId];
|
||||||
|
changeSignal.emit(game, START);
|
||||||
|
game.connect(new _GameListener(game, this));
|
||||||
|
game.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dispatchEvent(game:ServerGame, event:GameEvent):Void {
|
||||||
|
eventSignal.emit(game, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
44
src/server/haxe/ru/m/puzzlez/game/IGameManager.hx
Normal file
44
src/server/haxe/ru/m/puzzlez/game/IGameManager.hx
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package ru.m.puzzlez.game;
|
||||||
|
|
||||||
|
import hw.signal.Signal;
|
||||||
|
import ru.m.puzzlez.core.GameEvent;
|
||||||
|
import ru.m.puzzlez.proto.core.UserProto;
|
||||||
|
import ru.m.puzzlez.proto.room.SlotProto;
|
||||||
|
|
||||||
|
enum GameChange {
|
||||||
|
JOIN(user:UserProto);
|
||||||
|
LEAVE(user:UserProto);
|
||||||
|
SLOT(user:UserProto, slot:SlotProto);
|
||||||
|
START();
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GameManagerListener {
|
||||||
|
public function onCreate(game:ServerGame):Void;
|
||||||
|
public function onChange(game:ServerGame, change:GameChange):Void;
|
||||||
|
public function onDelete(game:ServerGame):Void;
|
||||||
|
public function onEvent(game:ServerGame, event:GameEvent):Void;
|
||||||
|
}
|
||||||
|
|
||||||
|
@:provide(GameManager) 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>;
|
||||||
|
public var gamesByUser(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>;
|
||||||
|
private var eventSignal(default, null):Signal2<ServerGame, GameEvent>;
|
||||||
|
|
||||||
|
public function dispatchEvent(game:ServerGame, event:GameEvent):Void;
|
||||||
|
|
||||||
|
public function connect(listener:GameManagerListener):Void;
|
||||||
|
public function disconnect(listener:GameManagerListener):Void;
|
||||||
|
|
||||||
|
public function create(user:UserProto):ServerGame;
|
||||||
|
public function delete(gameId:Int):Void;
|
||||||
|
public function join(gameId:Int, user:UserProto):Void;
|
||||||
|
public function slot(user:UserProto, slot:SlotProto):Void;
|
||||||
|
public function leave(user:UserProto):Void;
|
||||||
|
public function start(gameId:Int):Void;
|
||||||
|
}
|
||||||
72
src/server/haxe/ru/m/puzzlez/game/ServerGame.hx
Normal file
72
src/server/haxe/ru/m/puzzlez/game/ServerGame.hx
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package ru.m.puzzlez.game;
|
||||||
|
|
||||||
|
import haxe.Timer;
|
||||||
|
import ru.m.puzzlez.core.GameEvent;
|
||||||
|
import ru.m.puzzlez.proto.core.UserProto;
|
||||||
|
import ru.m.puzzlez.proto.room.RoomProto;
|
||||||
|
import ru.m.puzzlez.proto.room.SlotProto;
|
||||||
|
|
||||||
|
@:dispatcher(GameListener) class ServerGame {
|
||||||
|
|
||||||
|
public var room(default, null):RoomProto;
|
||||||
|
public var id(get, null):Int;
|
||||||
|
|
||||||
|
private var timer:Timer;
|
||||||
|
|
||||||
|
public function new(room:RoomProto) {
|
||||||
|
this.room = room;
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline function get_id():Int {
|
||||||
|
return room.game.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function contains(user:UserProto):Bool {
|
||||||
|
for (slot in room.slots) {
|
||||||
|
if (slot.hasUser() && slot.user.uuid == user.uuid) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function join(user:UserProto):Void {
|
||||||
|
if (!contains(user)) {
|
||||||
|
room.users.push(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function slot(user:UserProto, slot:SlotProto):Void {
|
||||||
|
join(user);
|
||||||
|
for (s in room.slots) {
|
||||||
|
if (s.hasUser() && s.user.uuid == user.uuid) {
|
||||||
|
s.clearUser();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (s in room.slots) {
|
||||||
|
if (s.slot.team == slot.team && s.slot.index == slot.index) {
|
||||||
|
s.setUser(user);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function leave(user:UserProto):Void {
|
||||||
|
for (slot in room.slots) {
|
||||||
|
if (slot.user != null && slot.user.uuid == user.uuid) {
|
||||||
|
slot.clearUser();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
room.setUsers(room.users.filter(function(u:UserProto) return u.uuid != user.uuid));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function start():Void {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function restore():Array<GameEvent> {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user