This commit is contained in:
syuilo
2018-03-07 17:48:32 +09:00
parent 6c495268ae
commit 161fd4afab
37 changed files with 747 additions and 219 deletions

View File

@@ -233,6 +233,26 @@ const endpoints: Endpoint[] = [
kind: 'notification-read'
},
{
name: 'othello/match',
withCredential: true
},
{
name: 'othello/match/cancel',
withCredential: true
},
{
name: 'othello/invitations',
withCredential: true
},
{
name: 'othello/games',
withCredential: true
},
{
name: 'mute/create',
withCredential: true,

View File

@@ -0,0 +1,22 @@
import $ from 'cafy';
import Game, { pack } from '../../models/othello-game';
module.exports = (params, user) => new Promise(async (res, rej) => {
// Get 'my' parameter
const [my = false, myErr] = $(params.my).boolean().$;
if (myErr) return rej('invalid my param');
const q = my ? {
$or: [{
black_user_id: user._id
}, {
white_user_id: user._id
}]
} : {};
// Fetch games
const games = await Game.find(q);
// Reponse
res(Promise.all(games.map(async (g) => await pack(g, user))));
});

View File

@@ -0,0 +1,11 @@
import Matching, { pack as packMatching } from '../../models/othello-matching';
module.exports = (params, user) => new Promise(async (res, rej) => {
// Find session
const invitations = await Matching.find({
child_id: user._id
});
// Reponse
res(Promise.all(invitations.map(async (i) => await packMatching(i, user))));
});

View File

@@ -1,6 +1,6 @@
import $ from 'cafy';
import Matching from '../../models/othello-matchig';
import Game, { pack } from '../../models/othello-game';
import Matching, { pack as packMatching } from '../../models/othello-matching';
import Game, { pack as packGame } from '../../models/othello-game';
import User from '../../models/user';
import { publishOthelloStream } from '../../event';
@@ -33,17 +33,14 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
created_at: new Date(),
black_user_id: parentIsBlack ? exist.parent_id : user._id,
white_user_id: parentIsBlack ? user._id : exist.parent_id,
turn_user_id: parentIsBlack ? exist.parent_id : user._id,
logs: []
});
const packedGame = await pack(game);
// Reponse
res(packedGame);
res(await packGame(game, user));
publishOthelloStream(exist.parent_id, 'matched', {
game
});
publishOthelloStream(exist.parent_id, 'matched', await packGame(game, exist.parent_id));
} else {
// Fetch child
const child = await User.findOne({
@@ -64,17 +61,16 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
});
// セッションを作成
await Matching.insert({
const matching = await Matching.insert({
created_at: new Date(),
parent_id: user._id,
child_id: child._id
});
// Reponse
res(204);
res();
// 招待
publishOthelloStream(child._id, 'invited', {
user_id: user._id
});
publishOthelloStream(child._id, 'invited', await packMatching(matching, child));
}
});

View File

@@ -0,0 +1,9 @@
import Matching from '../../../models/othello-matching';
module.exports = (params, user) => new Promise(async (res, rej) => {
await Matching.remove({
parent_id: user._id
});
res();
});

View File

@@ -42,6 +42,10 @@ class MisskeyEvent {
this.publish(`othello-stream:${userId}`, type, typeof value === 'undefined' ? null : value);
}
public publishOthelloGameStream(gameId: ID, type: string, value?: any): void {
this.publish(`othello-game-stream:${gameId}`, type, typeof value === 'undefined' ? null : value);
}
public publishChannelStream(channelId: ID, type: string, value?: any): void {
this.publish(`channel-stream:${channelId}`, type, typeof value === 'undefined' ? null : value);
}
@@ -71,4 +75,6 @@ export const publishMessagingIndexStream = ev.publishMessagingIndexStream.bind(e
export const publishOthelloStream = ev.publishOthelloStream.bind(ev);
export const publishOthelloGameStream = ev.publishOthelloGameStream.bind(ev);
export const publishChannelStream = ev.publishChannelStream.bind(ev);

View File

@@ -1,6 +1,7 @@
import * as mongo from 'mongodb';
import deepcopy = require('deepcopy');
import db from '../../db/mongodb';
import { IUser, pack as packUser } from './user';
const Game = db.get<IGame>('othello_games');
export default Game;
@@ -15,19 +16,30 @@ export interface IGame {
/**
* Pack an othello game for API response
*
* @param {any} game
* @return {Promise<any>}
*/
export const pack = (
game: any
game: any,
me?: string | mongo.ObjectID | IUser
) => new Promise<any>(async (resolve, reject) => {
// Me
const meId: mongo.ObjectID = me
? mongo.ObjectID.prototype.isPrototypeOf(me)
? me as mongo.ObjectID
: typeof me === 'string'
? new mongo.ObjectID(me)
: (me as IUser)._id
: null;
const _game = deepcopy(game);
// Rename _id to id
_game.id = _game._id;
delete _game._id;
// Populate user
_game.black_user = await packUser(_game.black_user_id, meId);
_game.white_user = await packUser(_game.white_user_id, meId);
resolve(_game);
});

View File

@@ -1,11 +1,42 @@
import * as mongo from 'mongodb';
import deepcopy = require('deepcopy');
import db from '../../db/mongodb';
import { IUser, pack as packUser } from './user';
const Matching = db.get<IMatching>('othello_matchings');
export default Matching;
export interface IMatching {
_id: mongo.ObjectID;
created_at: Date;
parent_id: mongo.ObjectID;
child_id: mongo.ObjectID;
}
/**
* Pack an othello matching for API response
*/
export const pack = (
matching: any,
me?: string | mongo.ObjectID | IUser
) => new Promise<any>(async (resolve, reject) => {
// Me
const meId: mongo.ObjectID = me
? mongo.ObjectID.prototype.isPrototypeOf(me)
? me as mongo.ObjectID
: typeof me === 'string'
? new mongo.ObjectID(me)
: (me as IUser)._id
: null;
const _matching = deepcopy(matching);
delete _matching._id;
// Populate user
_matching.parent = await packUser(_matching.parent_id, meId);
_matching.child = await packUser(_matching.child_id, meId);
resolve(_matching);
});

View File

@@ -1,12 +1,69 @@
import * as websocket from 'websocket';
import * as redis from 'redis';
import Game from '../models/othello-game';
import { publishOthelloGameStream } from '../event';
import Othello from '../../common/othello';
export default function(request: websocket.request, connection: websocket.connection, subscriber: redis.RedisClient): void {
const game = request.resourceURL.query.game;
export default function(request: websocket.request, connection: websocket.connection, subscriber: redis.RedisClient, user: any): void {
const gameId = request.resourceURL.query.game;
// Subscribe game stream
subscriber.subscribe(`misskey:othello-game-stream:${game}`);
subscriber.subscribe(`misskey:othello-game-stream:${gameId}`);
subscriber.on('message', (_, data) => {
connection.send(data);
});
connection.on('message', async (data) => {
const msg = JSON.parse(data.utf8Data);
switch (msg.type) {
case 'set':
if (msg.pos == null) return;
const pos = msg.pos;
const game = await Game.findOne({ _id: gameId });
const o = new Othello();
game.logs.forEach(log => {
o.set(log.color, log.pos);
});
const myColor = game.black_user_id.equals(user._id) ? 'black' : 'white';
const opColor = myColor == 'black' ? 'white' : 'black';
if (!o.canReverse(myColor, pos)) return;
o.set(myColor, pos);
let turn;
if (o.getPattern(opColor).length > 0) {
turn = myColor == 'black' ? game.white_user_id : game.black_user_id;
} else {
turn = myColor == 'black' ? game.black_user_id : game.white_user_id;
}
const log = {
at: new Date(),
color: myColor,
pos
};
await Game.update({
_id: gameId
}, {
$set: {
turn_user_id: turn
},
$push: {
logs: log
}
});
publishOthelloGameStream(gameId, 'set', {
color: myColor,
pos
});
break;
}
});
}

View File

@@ -2,10 +2,8 @@ import * as websocket from 'websocket';
import * as redis from 'redis';
export default function(request: websocket.request, connection: websocket.connection, subscriber: redis.RedisClient, user: any): void {
const otherparty = request.resourceURL.query.otherparty;
// Subscribe matching stream
subscriber.subscribe(`misskey:othello-matching:${user._id}-${otherparty}`);
// Subscribe othello stream
subscriber.subscribe(`misskey:othello-stream:${user._id}`);
subscriber.on('message', (_, data) => {
connection.send(data);
});

View File

@@ -11,7 +11,7 @@ import driveStream from './stream/drive';
import messagingStream from './stream/messaging';
import messagingIndexStream from './stream/messaging-index';
import othelloGameStream from './stream/othello-game';
import othelloMatchingStream from './stream/othello-matching';
import othelloStream from './stream/othello';
import serverStream from './stream/server';
import requestsStream from './stream/requests';
import channelStream from './stream/channel';
@@ -65,7 +65,7 @@ module.exports = (server: http.Server) => {
request.resourceURL.pathname === '/messaging' ? messagingStream :
request.resourceURL.pathname === '/messaging-index' ? messagingIndexStream :
request.resourceURL.pathname === '/othello-game' ? othelloGameStream :
request.resourceURL.pathname === '/othello-matching' ? othelloMatchingStream :
request.resourceURL.pathname === '/othello' ? othelloStream :
null;
if (channel !== null) {