[server] update

This commit is contained in:
2020-06-03 18:02:42 +03:00
parent 33d1d77cd3
commit edbc587e2e
18 changed files with 240 additions and 399 deletions

View File

@@ -47,7 +47,7 @@ const config = new Project.Config({
`CompilationOption.set('build','${dateformat(new Date(), 'yyyy-mm-dd HH:MM:ss')}')`,
],
flags: [
//'proto_debug',
'proto_debug',
]
});

View File

@@ -60,6 +60,9 @@ class PuzzlezTheme extends Theme {
register(new Style("icon.orange", [
"skin.color" => 0xcc5500,
]));
register(new Style("icon.green", [
"skin.color" => 0x00ff00,
]));
register(new Style("icon.control", [
"geometry.hAlign" => HAlign.RIGHT,
"geometry.vAlign" => VAlign.TOP,

View File

@@ -10,26 +10,24 @@ import ru.m.puzzlez.core.Id;
import ru.m.puzzlez.proto.core.User;
import ru.m.puzzlez.proto.event.GameAction;
import ru.m.puzzlez.proto.event.GameEvent;
import ru.m.puzzlez.proto.game.GameItem;
import ru.m.puzzlez.proto.game.GamePreset;
import ru.m.puzzlez.proto.game.GameState;
import ru.m.puzzlez.proto.pack.AuthRequest;
import ru.m.puzzlez.proto.pack.GameActionRequest;
import ru.m.puzzlez.proto.pack.GameCreateRequest;
import ru.m.puzzlez.proto.pack.GameJoinRequest;
import ru.m.puzzlez.proto.pack.LoginRequest;
import ru.m.puzzlez.proto.pack.GameLeaveRequest;
import ru.m.puzzlez.proto.pack.GameListRequest;
import ru.m.puzzlez.proto.pack.GameListResponse;
import ru.m.puzzlez.proto.pack.NotificationResponse;
import ru.m.puzzlez.proto.pack.Request;
import ru.m.puzzlez.proto.pack.Response;
@:provide class Network implements IDataIndex<ImageId> {
public var user(default, null):User;
public var userSignal(default, null):Signal<User> = new Signal();
public var gameList(default, null):Array<GameItem>;
public var gameListSignal(default, null):Signal<Array<GameItem>> = new Signal();
public var game(default, null):GameItem;
public var gameSignal(default, null):Signal<GameItem> = new Signal();
public var gameEventSignal(default, null):Signal<GameEvent> = new Signal();
public var userSignal:Signal<User> = new Signal();
public var notificationSignal:Signal<NotificationResponse> = new Signal();
public var listSignal:Signal<GameListResponse> = new Signal();
public var joinSignal:Signal<GameState> = new Signal();
public var gameEventSignal:Signal<GameEvent> = new Signal();
private var connection:IConnection<Request, Response>;
private var storage:SharedObjectStorage;
@@ -37,41 +35,49 @@ import ru.m.puzzlez.proto.pack.Response;
private static var USER_KEY = "user";
public function new() {
gameList = [];
storage = new SharedObjectStorage("network/2");
if (storage.exists(USER_KEY)) {
user = storage.read(USER_KEY);
} else {
user = new User().setName('Anonimus #${IdUtil.generate()}');
storage.write(USER_KEY, user);
}
connection = ConnectionFactory.buildClientConnection("127.0.0.1", 5000, Response);
connection.handler.connect(onConnectionChange);
connection.receiveHandler.connect(onReceivePacket);
connection.connect().catchError(_ -> {});
}
public function login():Void {
connection.send(new Request().setLogin(new LoginRequest().setUser(user)));
private function restoreUser():User {
if (storage.exists(USER_KEY)) {
return storage.read(USER_KEY);
} else {
return new User().setName('Anonimus #${IdUtil.generate()}');
}
}
public function startGame(state:GameState):Void {
connection.send(new Request().setGameCreate(new GameCreateRequest().setImageId(state.preset.imageId)));
public function auth():Promise<User> {
connection.send(new Request().setAuth(new AuthRequest().setUser(restoreUser())));
return userSignal.next();
}
public function joinGame(state:GameState):Void {
connection.send(new Request().setGameJoin(new GameJoinRequest().setGameId(state.id)));
public function createGame(preset:GamePreset):Promise<GameState> {
connection.send(new Request().setJoin(new GameJoinRequest().setPreset(preset)));
return joinSignal.next();
}
public function joinGame(gameId:String):Promise<GameState> {
connection.send(new Request().setJoin(new GameJoinRequest().setGameId(gameId)));
return joinSignal.next();
}
public function leaveGame():Void {
connection.send(new Request().setLeave(new GameLeaveRequest()));
}
public function sendGameAction(action:GameAction):Void {
connection.send(new Request().setGameAction(new GameActionRequest().setActions([action])));
connection.send(new Request().setAction(new GameActionRequest().setActions([action])));
}
private function onConnectionChange(event:ConnectionEvent):Void {
L.i("network", '${event}');
switch event {
case CONNECTED:
login();
auth().then(user -> storage.write(USER_KEY, user));
case DISCONNECTED:
//
case ERROR(error):
@@ -80,34 +86,32 @@ import ru.m.puzzlez.proto.pack.Response;
}
private function onReceivePacket(packet:Response):Void {
if (packet.hasLogin()) {
user = packet.login.user;
storage.write(USER_KEY, user);
userSignal.emit(user);
} else if (packet.hasLogout()) {
user = null;
userSignal.emit(user);
} else if (packet.hasGameList()) {
gameList = packet.gameList.games;
gameListSignal.emit(gameList);
} else if (packet.hasGame()) {
game = packet.game.game;
gameSignal.emit(game);
} else if (packet.hasGameEvent()) {
for (event in packet.gameEvent.events) {
// ToDo: convert event?
//gameEventSignal.emit(Unserializer.run(event.event));
if (packet.hasAuth()) {
userSignal.emit(packet.auth.user);
} else if (packet.hasNotification()) {
notificationSignal.emit(packet.notification);
} else if (packet.hasList()) {
listSignal.emit(packet.list);
} else if (packet.hasJoin()) {
joinSignal.emit(packet.join.game);
} else if (packet.hasEvent()) {
for (event in packet.event.events) {
gameEventSignal.emit(event);
}
}
}
public function getIndexPage(page:Page):Promise<DataPage<ImageId>> {
// ToDo: return gameList
var data = {
page: page,
total: 1,
data: [new ImageId('asset', 'resources/image/raccoon.jpg')],
}
return Promise.promise(data);
connection.send(new Request().setList(new GameListRequest().setCount(page.count).setPage(page.index)));
return listSignal.next().then((list:GameListResponse) -> ({
page: {
index: list.page,
count: list.count,
filter: null,
order: null,
},
total: list.total,
data: list.games.map(item -> ImageId.fromString(item.preset.imageId)),
}));
}
}

View File

@@ -1,11 +1,11 @@
package ru.m.puzzlez.net;
import ru.m.puzzlez.proto.event.GameStart;
import hw.signal.Signal;
import ru.m.puzzlez.core.IGame;
import ru.m.puzzlez.proto.event.GameAction;
import ru.m.puzzlez.proto.event.GameEvent;
import ru.m.puzzlez.proto.game.GameState;
import ru.m.puzzlez.proto.game.GameStatus;
class NetworkGame implements IGame {
public var state(default, null):GameState;
@@ -23,17 +23,13 @@ class NetworkGame implements IGame {
}
public function start():Void {
events.emit(new GameEvent().setStart(new GameStart().setState(state)));
network.gameEventSignal.connect(onEvent);
switch state.status {
case GameStatus.READY:
network.startGame(state);
case _:
network.joinGame(state);
}
}
public function stop():Void {
network.gameEventSignal.disconnect(onEvent);
network.leaveGame();
}
public function dispose():Void {

View File

@@ -35,7 +35,8 @@ class Render extends SpriteView implements IRender {
private var playerId(get, never):PlayerId;
private function get_playerId():PlayerId {
return network.user.hasUuid() ? network.user.uuid : "local";
// ToDo: network user
return "local";
}
private function get_scale():Float {

View File

@@ -56,8 +56,11 @@ import ru.m.puzzlez.view.common.PresetView;
}
private function start(online:Bool = false):Void {
imageView.state.online = online;
switcher.change(GameFrame.ID, imageView.state);
if (online) {
network.createGame(imageView.state.preset).then(state -> switcher.change(GameFrame.ID, state));
} else {
switcher.change(GameFrame.ID, imageView.state);
}
}
private function back():Void {

View File

@@ -1,7 +1,5 @@
package ru.m.puzzlez.view;
import ru.m.puzzlez.proto.game.GameStatus;
import ru.m.puzzlez.net.Network;
import hw.view.data.DataView;
import hw.view.form.ButtonView;
import hw.view.frame.FrameSwitcher;
@@ -9,6 +7,9 @@ import hw.view.frame.FrameView;
import ru.m.data.IDataSource;
import ru.m.pixabay.PixabayApi;
import ru.m.puzzlez.core.ImageListSource;
import ru.m.puzzlez.net.Network;
import ru.m.puzzlez.proto.game.GameStatus;
import ru.m.puzzlez.proto.pack.NotificationResponse;
import ru.m.puzzlez.source.AssetSource;
import ru.m.puzzlez.source.FileSource;
import ru.m.puzzlez.source.PixabaySource;
@@ -22,6 +23,7 @@ import ru.m.update.Updater;
@:view var sources:DataView<ImageListSource, ButtonView>;
@:view("load") var loadButton:ButtonView;
@:view("complete") var completeButton:ButtonView;
@:view("network") var networkButton:ButtonView;
@:view("update") var updateButton:ButtonView;
@:provide var storage:ImageStorage;
@@ -55,6 +57,13 @@ import ru.m.update.Updater;
completeButton.text = 'Complete (${total})';
completeButton.disabled = total == 0;
});
network.notificationSignal.next().then(onNotification);
}
private function onNotification(notification:NotificationResponse):Void {
var total = notification.games;
networkButton.text = 'Network (${total})';
networkButton.disabled = total == 0;
}
override public function onShow(data:Dynamic):Void {
@@ -65,6 +74,11 @@ import ru.m.update.Updater;
updateButton.text = 'Update ${info.version}';
}
}).catchError(error -> L.w('Update', 'failed: ${error}'));
network.notificationSignal.connect(onNotification);
}
override public function onHide():Void {
network.notificationSignal.disconnect(onNotification);
}
private function sourceViewFactory(index:Int, source:ImageListSource):ButtonView {

View File

@@ -1,5 +1,6 @@
package ru.m.puzzlez.view.common;
import ru.m.puzzlez.net.Network;
import hw.view.data.DataView;
import hw.view.form.ToggleButtonView;
import hw.view.frame.FrameSwitcher;
@@ -24,6 +25,7 @@ import ru.m.puzzlez.view.common.PuzzleImageView;
@:provide var switcher:FrameSwitcher;
@:provide var gameStorage:GameStorage;
@:provide var imageStorage:ImageStorage;
@:provide var network:Network;
public var data(default, set):DataPage<ImageId>;
@@ -74,6 +76,8 @@ import ru.m.puzzlez.view.common.PuzzleImageView;
gameStorage.delete(imageId).then(_ -> refresh());
}
});
case JOIN:
// ToDo:
}
}

View File

@@ -1,5 +1,6 @@
package ru.m.puzzlez.view.common;
import ru.m.puzzlez.net.Network;
import hw.view.data.DataView;
import hw.view.form.ButtonView;
import hw.view.form.LabelView;
@@ -14,6 +15,7 @@ import ru.m.puzzlez.storage.ImageStorage;
enum Action {
CLEAN;
REMOVE;
JOIN;
}
@:template class PuzzleImageView extends GroupView {
@@ -42,14 +44,17 @@ enum Action {
@:view("label") var labelView:LabelView;
@:view("clean") var cleanButton:ButtonView;
@:view("remove") var removeButton:ButtonView;
@:view("join") var joinButton:ButtonView;
@:provide static var imageStorage:ImageStorage;
@:provide static var gameStorage:GameStorage;
@:provide static var network:Network;
private var loading:LoadingWrapper;
public function new() {
super();
cleanButton.visible = false;
removeButton.visible = false;
joinButton.visible = false;
loading = new LoadingWrapper(this);
}
@@ -73,6 +78,8 @@ enum Action {
result.removeButton.visible = true;
}
});
// ToDo:
result.joinButton.visible = false;
return result;
}
}

View File

@@ -18,3 +18,8 @@ views:
propagation: false
style: icon.control.small.close.orange
+onPress: ~emit(Action.CLEAN)
- id: join
$type: hw.view.form.ButtonView
propagation: false
style: icon.control.small.close.green
+onPress: ~emit(Action.JOIN)

View File

@@ -26,16 +26,26 @@ message GameAction {
ru.m.puzzlez.proto.core.Point position = 4;
}
message GamePlayer {
ru.m.puzzlez.proto.core.User player = 1;
enum GamePlayerAction {
JOIN = 0;
LEAVE = 1;
}
GamePlayerAction action = 2;
}
message GameChange {
int32 partId = 1;
ru.m.puzzlez.proto.core.Point position = 2;
ru.m.puzzlez.proto.game.PartLocation location = 3;
string playerId = 4;
string playerId = 1;
int32 partId = 2;
ru.m.puzzlez.proto.core.Point position = 3;
ru.m.puzzlez.proto.game.PartLocation location = 4;
}
message GameEvent {
int32 time = 1;
oneof event {
GamePlayer player = 9;
GameStart start = 10;
GameComplete complete = 11;
GameAction action = 12;

View File

@@ -55,11 +55,7 @@ message GameState {
string id = 1;
GameStatus status = 2;
GamePreset preset = 3;
repeated Part parts = 4;
bool online = 5;
}
message GameItem {
GameState state = 2;
repeated ru.m.puzzlez.proto.core.User users = 3;
repeated ru.m.puzzlez.proto.core.User users = 4;
repeated Part parts = 5;
bool online = 6;
}

View File

@@ -11,38 +11,47 @@ message ErrorResponse {
string message = 2;
}
message LoginRequest {
message NotificationResponse {
int32 games = 1;
}
message AuthRequest {
ru.m.puzzlez.proto.core.User user = 1;
}
message LoginResponse {
message AuthResponse {
ru.m.puzzlez.proto.core.User user = 1;
}
message LogoutRequest {}
message LogoutResponse {}
message GameCreateRequest {
string imageId = 1;
}
message GameJoinRequest {
string gameId = 1;
oneof content {
ru.m.puzzlez.proto.game.GamePreset preset = 1;
string gameId = 2;
}
}
message GameLeaveRequest {}
message GameJoinResponse {
ru.m.puzzlez.proto.game.GameState game = 1;
}
message GameLeaveRequest {
}
message GameLeaveResponse {
message GameResponse {
ru.m.puzzlez.proto.game.GameItem game = 1;
}
message GameListRequest {
bool subscribe = 1;
int32 page = 1;
int32 count = 2;
}
message GameListResponse {
repeated ru.m.puzzlez.proto.game.GameItem games = 1;
int32 page = 1;
int32 count = 2;
int32 total = 3;
repeated ru.m.puzzlez.proto.game.GameState games = 4;
}
message GameActionRequest {
@@ -54,31 +63,25 @@ message GameEventResponse {
}
message Request {
string rid = 1;
oneof content {
LoginRequest login = 1;
LogoutRequest logout = 2;
GameCreateRequest gameCreate = 10;
GameJoinRequest gameJoin = 11;
GameLeaveRequest gameLeave = 12;
GameListRequest gameList = 20;
GameActionRequest gameAction = 100;
AuthRequest auth = 10;
GameListRequest list = 20;
GameJoinRequest join = 30;
GameLeaveRequest leave = 40;
GameActionRequest action = 100;
}
}
message Response {
string rid = 1;
oneof content {
LoginResponse login = 1;
LogoutResponse logout = 2;
GameResponse game = 10;
GameListResponse gameList = 20;
GameEventResponse gameEvent = 100;
ErrorResponse error = 999;
AuthResponse auth = 10;
GameListResponse list = 20;
GameJoinResponse join = 30;
GameLeaveResponse leave = 40;
GameEventResponse event = 100;
NotificationResponse notification = 900;
ErrorResponse error = 1000;
}
}

View File

@@ -1,49 +1,53 @@
package ru.m.puzzlez;
import ru.m.puzzlez.proto.pack.GameListResponse;
import ru.m.puzzlez.proto.pack.GameListRequest;
import hw.connect.session.ProtoSession;
import hw.log.BaseLogger.LoggerUtil;
import ru.m.puzzlez.game.IGameManager;
import ru.m.puzzlez.game.ServerGame;
import ru.m.puzzlez.core.Game;
import ru.m.puzzlez.core.GameUtil;
import ru.m.puzzlez.proto.core.User;
import ru.m.puzzlez.proto.event.GameAction;
import ru.m.puzzlez.proto.event.GameEvent;
import ru.m.puzzlez.proto.event.gameplayer.GamePlayerAction;
import ru.m.puzzlez.proto.event.GamePlayer;
import ru.m.puzzlez.proto.game.GamePreset;
import ru.m.puzzlez.proto.pack.AuthRequest;
import ru.m.puzzlez.proto.pack.AuthResponse;
import ru.m.puzzlez.proto.pack.ErrorResponse;
import ru.m.puzzlez.proto.pack.GameEventResponse;
import ru.m.puzzlez.proto.pack.GameListResponse;
import ru.m.puzzlez.proto.pack.GameResponse;
import ru.m.puzzlez.proto.pack.LoginResponse;
import ru.m.puzzlez.proto.pack.LogoutResponse;
import ru.m.puzzlez.proto.pack.GameJoinResponse;
import ru.m.puzzlez.proto.pack.GameLeaveResponse;
import ru.m.puzzlez.proto.pack.NotificationResponse;
import ru.m.puzzlez.proto.pack.Request;
import ru.m.puzzlez.proto.pack.Response;
import sys.net.Socket;
class GameSession extends ProtoSession<Response, Request> implements GameManagerListener {
class GameSession extends ProtoSession<Response, Request> {
private static inline var TAG = "Session";
@:provide static var gameManager:IGameManager;
public static var gamesById(default, null):Map<String, Game> = new Map();
public static var sessionsById(default, null):Map<Int, GameSession> = new Map();
public var user(default, null):User;
public var gameId(default, null):String;
public var game(default, null):Game;
private var subscribed:Bool;
private var tag(get, never):String;
private function get_tag():String {
return '[${id}|${user == null ? '-' : user.name}|${gameId == null ? '-' : gameId}]';
return '[${id}|${user == null ? '-' : user.uuid}|${game == null ? '-' : game.state.id}]';
}
public function new(socket:Socket) {
super(socket, Request);
sessionsById.set(id, this);
}
private function sendError(code:Int, message:String):Void {
send(new Response().setError(new ErrorResponse().setCode(code).setMessage(message)));
}
private function listGame():GameListResponse {
var games = gameManager.games;
return new GameListResponse().setGames([for (game in games) game.proto]);
}
override public function send(packet:Response):Void {
#if proto_debug L.d(TAG, '$tag send: ${packet}'); #end
try {
@@ -53,61 +57,71 @@ class GameSession extends ProtoSession<Response, Request> implements GameManager
}
}
private function logout(leave:Bool = true):Void {
gameId = null;
gameManager.disconnect(this);
if (user != null && leave) {
gameManager.leave(user);
user = null;
private function auth(auth:AuthRequest):Void {
user = auth.hasUser() ? auth.user : new User().setUuid(IdUtil.generate()).setName('Anonim #${IdUtil.generate()}');
send(new Response().setAuth(new AuthResponse().setUser(user)));
send(new Response().setNotification(new NotificationResponse().setGames(Lambda.count(gamesById))));
}
private function list(list:GameListRequest):Void {
var games = Lambda.array(gamesById);
var pageGames = games.slice(list.page * list.count, list.count);
send(new Response().setList(new GameListResponse()
.setPage(list.page)
.setCount(list.count)
.setTotal(games.length)
.setGames(pageGames.map(game -> game.state))
));
}
private function create(preset:GamePreset):Void {
var game = new Game(GameUtil.buildState(preset).setOnline(true));
game.start();
gamesById.set(game.state.id, game);
join(game.state.id);
for (session in sessionsById) {
session.send(new Response().setNotification(new NotificationResponse().setGames(Lambda.count(gamesById))));
}
}
private function join(gameId:String):Void {
this.gameId = gameId;
gameManager.join(gameId, user);
var game = gameManager.gamesById[gameId];
game = gamesById.get(gameId);
game.events.connect(onGameEvent);
game.events.emit(new GameEvent().setPlayer(new GamePlayer().setPlayer(user).setAction(GamePlayerAction.JOIN)));
send(new Response().setJoin(new GameJoinResponse().setGame(game.state)));
}
private function leave():Void {
game.events.disconnect(onGameEvent);
game.events.emit(new GameEvent().setPlayer(new GamePlayer().setPlayer(user).setAction(GamePlayerAction.LEAVE)));
send(new Response().setLeave(new GameLeaveResponse()));
}
private function action(action:GameAction):Void {
game.events.emit(new GameEvent().setAction(action));
}
override private function onRequest(request:Request):Void {
#if proto_debug L.d(TAG, '$tag onRequest: ${request}'); #end
try {
if (!request.hasLogin() && user == null) {
if (!request.hasAuth() && user == null) {
throw "Not Authorized";
}
// login
if (request.hasLogin()) {
user = new User()
.setUuid(request.login.user.uuid != null ? request.login.user.uuid : IdUtil.generate())
.setName(request.login.user.name);
gameManager.connect(this);
send(new Response().setLogin(new LoginResponse().setUser(user)));
if (gameManager.gamesByUser.exists(user.uuid)) {
join(gameManager.gamesByUser[user.uuid].id);
if (request.hasAuth()) {
auth(request.auth);
} else if (request.hasList()) {
list(request.list);
} else if (request.hasJoin()) {
if (request.join.hasPreset()) {
create(request.join.preset);
} else if (request.join.hasGameId()) {
join(request.join.gameId);
}
// logout
} else if (request.hasLogout()) {
logout();
send(new Response().setLogout(new LogoutResponse()));
// game
} else if (request.hasGameCreate()) {
var game = gameManager.create(user, request.gameCreate.imageId);
gameId = game.id;
send(new Response().setGame(new GameResponse().setGame(game.proto)));
} else if (request.hasGameJoin()) {
join(request.gameJoin.gameId);
} else if (request.hasGameLeave()) {
gameManager.leave(user);
// game list
} else if (request.hasGameList()) {
subscribed = request.gameList.subscribe;
if (subscribed) {
send(new Response().setGameList(listGame()));
}
} else if (request.hasGameAction()) {
if (gameManager.gamesById.exists(gameId)) {
for (action in request.gameAction.actions) {
gameManager.gamesById[gameId].events.emit(new GameEvent().setAction(action));
}
} else if (request.hasLeave()) {
leave();
} else if (request.hasAction()) {
for (action in request.action.actions) {
this.action(action);
}
}
} catch (error:Dynamic) {
@@ -116,49 +130,18 @@ class GameSession extends ProtoSession<Response, Request> implements GameManager
}
}
private function onGameEvent(event:GameEvent):Void {
send(new Response().setEvent(new GameEventResponse().setEvents([event])));
}
override public function disconnect():Void {
L.d(TAG, '$tag disconnect');
logout(false);
if (game != null) {
leave();
game = null;
}
user = null;
sessionsById.remove(id);
super.disconnect();
}
public function onCreate(game:ServerGame):Void {
if (subscribed) {
send(new Response().setGameList(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 = null;
send(new Response().setGame(new GameResponse()));
return;
}
case _:
}
send(new Response().setGame(new GameResponse().setGame(game.proto)));
}
if (subscribed) {
send(new Response().setGameList(listGame()));
}
}
public function onDelete(game:ServerGame):Void {
if (gameId == game.id) {
gameId = null;
send(new Response().setGame(new GameResponse()));
}
if (subscribed) {
send(new Response().setGameList(listGame()));
}
}
public function onEvent(game:ServerGame, event:GameEvent):Void {
if (gameId == game.id) {
send(new Response().setGameEvent(new GameEventResponse().setEvents([event])));
}
}
}

View File

@@ -1,4 +0,0 @@
package ru.m.puzzlez.game;
interface GameListener {
}

View File

@@ -1,99 +0,0 @@
package ru.m.puzzlez.game;
import ru.m.puzzlez.core.Id;
import ru.m.puzzlez.game.IGameManager;
import ru.m.puzzlez.proto.core.User;
import ru.m.puzzlez.proto.event.GameEvent;
import ru.m.puzzlez.proto.game.GameItem;
import ru.m.puzzlez.proto.game.GamePreset;
import ru.m.puzzlez.proto.game.GameState;
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);
if (event.hasComplete()) {
dispatcher.delete(game.id);
dispose();
}
}
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<String, ServerGame>;
public var gamesByUser(default, null):Map<String, ServerGame>;
public function new() {
games = [];
gamesById = new Map();
gamesByUser = new Map();
}
public function create(user:User, imageId:ImageId):ServerGame {
var gameProto = new GameItem().setState(
new GameState()
.setId(IdUtil.generate())
.setPreset(new GamePreset().setImageId(imageId))
);
var game = new ServerGame(gameProto);
games.push(game);
gamesById[game.id] = game;
createSignal.emit(game);
join(game.id, user);
return game;
}
public function join(gameId:String, user:User):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:String):Void {
if (gamesById.exists(gameId)) {
var game = gamesById[gameId];
games.remove(game);
gamesById.remove(game.id);
deleteSignal.emit(game);
}
}
public function leave(user:User):Void {
if (gamesByUser.exists(user.uuid)) {
var game = gamesByUser[user.uuid];
gamesByUser.remove(user.uuid);
game.leave(user);
changeSignal.emit(game, LEAVE(user));
}
}
public function start(gameId:String):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);
}
}

View File

@@ -1,41 +0,0 @@
package ru.m.puzzlez.game;
import hw.signal.Signal;
import ru.m.puzzlez.core.Id;
import ru.m.puzzlez.proto.core.User;
import ru.m.puzzlez.proto.event.GameEvent;
enum GameChange {
JOIN(user:User);
LEAVE(user:User);
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<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:User, imageId:ImageId):ServerGame;
public function delete(gameId:String):Void;
public function join(gameId:String, user:User):Void;
public function leave(user:User):Void;
public function start(gameId:String):Void;
}

View File

@@ -1,44 +0,0 @@
package ru.m.puzzlez.game;
import haxe.Timer;
import ru.m.puzzlez.core.Game;
import ru.m.puzzlez.core.GameUtil;
import ru.m.puzzlez.proto.core.User;
import ru.m.puzzlez.proto.game.GameItem;
@:dispatcher(GameListener) class ServerGame extends Game {
public var id(get, null):String;
public var proto(default, default):GameItem;
private var timer:Timer;
public function new(proto:GameItem) {
// ToDo:
super(GameUtil.buildState(GameUtil.buildPreset(proto.state.preset.imageId)));
this.proto = proto;
}
private inline function get_id():String {
return proto.state.id;
}
public function contains(user:User):Bool {
for (user in proto.users) {
if (user.uuid == user.uuid) {
return true;
}
}
return false;
}
public function join(user:User):Void {
if (!contains(user)) {
proto.users.push(user);
}
}
public function leave(user:User):Void {
proto.setUsers(proto.users.filter(function(u:User) return u.uuid != user.uuid));
}
}