Merge pull request #78 from aguibert/queue-bugfix-2

Smooth out requeueing after a game is over
This commit is contained in:
Liam Westby 2018-05-07 08:09:27 -05:00 committed by GitHub
commit 0b033ed5ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 115 additions and 57 deletions

View File

@ -3,6 +3,9 @@ import { Router } from '@angular/router';
import { GameService } from '../game/game.service';
import { Triangle } from '../geom/triangle';
import { Point } from '../geom/point';
import { LoginComponent } from '../login/login.component';
import * as EventSource from 'eventsource';
import { environment } from './../../environments/environment';
@Component({
selector: 'app-controls',
@ -346,14 +349,31 @@ export class ControlsComponent implements OnInit, OnDestroy {
}
requeue() {
if (sessionStorage.getItem('isSpectator') === 'true' ||
sessionStorage.getItem('partyId') === null) {
let partyId = sessionStorage.getItem('partyId');
if (sessionStorage.getItem('isSpectator') === 'true' || partyId === null) {
this.gameService.send({ message: 'GAME_REQUEUE' });
} else {
sessionStorage.setItem('requeueRequested', 'true');
this.ngZone.run(() => {
this.router.navigate(['/login']);
});
let queueCallback = new EventSource(`${environment.API_URL_PARTY}/${partyId}/queue`);
queueCallback.onmessage = msg => {
let queueMsg = JSON.parse(msg.data);
if (queueMsg.queuePosition) {
// go to login page, reuse the same EventSource
LoginComponent.queueCallback = queueCallback;
sessionStorage.setItem('queuePosition', queueMsg.queuePosition);
this.ngZone.run(() => {
this.router.navigate(['/login']);
});
} else if (queueMsg.requeue) {
console.log(`ready to join game! Joining round ${queueMsg.requeue}`);
queueCallback.close();
this.processRequeue(queueMsg.requeue);
} else {
console.log('Error: unrecognized message ' + msg.data);
}
}
queueCallback.onerror = msg => {
console.log('Error showing queue position: ' + JSON.stringify(msg.data));
}
}
}

View File

@ -2,6 +2,9 @@ import { Component, OnInit, OnDestroy, NgZone } from '@angular/core';
import { Router } from '@angular/router';
import { Meta } from '@angular/platform-browser';
import { GameService } from './game.service';
import { LoginComponent } from '../login/login.component';
import * as EventSource from 'eventsource';
import { environment } from './../../environments/environment';
@Component({
selector: 'app-game',
@ -111,14 +114,31 @@ export class GameComponent implements OnInit, OnDestroy {
}
requeue() {
if (sessionStorage.getItem('isSpectator') === 'true' ||
sessionStorage.getItem('partyId') === null) {
let partyId = sessionStorage.getItem('partyId');
if (sessionStorage.getItem('isSpectator') === 'true' || partyId === null) {
this.gameService.send({ message: 'GAME_REQUEUE' });
} else {
sessionStorage.setItem('requeueRequested', 'true');
this.ngZone.run(() => {
this.router.navigate(['/login']);
});
let queueCallback = new EventSource(`${environment.API_URL_PARTY}/${partyId}/queue`);
queueCallback.onmessage = msg => {
let queueMsg = JSON.parse(msg.data);
if (queueMsg.queuePosition) {
// go to login page, reuse the same EventSource
LoginComponent.queueCallback = queueCallback;
sessionStorage.setItem('queuePosition', queueMsg.queuePosition);
this.ngZone.run(() => {
this.router.navigate(['/login']);
});
} else if (queueMsg.requeue) {
console.log(`ready to join game! Joining round ${queueMsg.requeue}`);
queueCallback.close();
this.processRequeue(queueMsg.requeue);
} else {
console.log('Error: unrecognized message ' + msg.data);
}
}
queueCallback.onerror = msg => {
console.log('Error showing queue position: ' + JSON.stringify(msg.data));
}
}
}

View File

@ -1,4 +1,4 @@
import { Component, OnInit, NgZone, HostBinding, Injectable } from '@angular/core';
import { Component, OnInit, OnDestroy, NgZone, HostBinding, Injectable } from '@angular/core';
import { Meta } from '@angular/platform-browser';
import { Router, ActivatedRoute } from '@angular/router';
import { HttpClient, HttpHeaders } from '@angular/common/http';
@ -14,13 +14,13 @@ import { Player } from '../game/player/player';
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {
export class LoginComponent implements OnInit, OnDestroy {
pane: PaneType = sessionStorage.getItem('username') === null ? 'left' : 'right';
username: string;
party: string;
queuePosition: number;
player = new Player('PLAYER NAME HERE', 'none', '#FFFFFF');
queueCallback: EventSource;
static queueCallback: EventSource;
isFullDevice: boolean = !/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
constructor(
@ -39,8 +39,7 @@ export class LoginComponent implements OnInit {
this.meta.addTag({name: 'viewport', content: `width=${viewWidth}, height=${viewHeight}, initial-scale=1.0`}, true);
this.route.params.subscribe( params =>
{
this.route.params.subscribe( params => {
sessionStorage.setItem("jwt", params['jwt']);
});
if (sessionStorage.getItem('jwt')) {
@ -52,15 +51,21 @@ export class LoginComponent implements OnInit {
this.player.name = this.username;
}
if (sessionStorage.getItem('partyId') !== null &&
sessionStorage.getItem('requeueRequested') === 'true') {
sessionStorage.removeItem('requeueRequested');
this.party = sessionStorage.getItem('partyId');
console.log(`User already associated with party ${this.party}, entering queue`);
this.enterQueue();
// If a player has participated in a game and requeued and the next round is full, they will be redirected back to the login page.
// Re-use the EventSource initiated on the game page, but change the onMessage() function to match the context of this page
var queuePosition = sessionStorage.getItem('queuePosition');
if (queuePosition) {
sessionStorage.removeItem('queuePosition')
console.log(`User already associated with party, entering queue`);
this.setQueueOnMessage();
this.showQueue(queuePosition);
}
}
ngOnDestroy() {
this.cancelQueue();
}
loginGoogle() {
window.location.href = `http://${environment.API_URL_AUTH}/auth-service/GoogleAuth`;
}
@ -107,9 +112,9 @@ export class LoginComponent implements OnInit {
if (data.gameState !== 'OPEN') {
if (this.party === null) {
if (data.gameState === 'FULL')
alert('All games are full! Try again in a few seconds.');
else
if (data.gameState === 'FULL')
alert('All games are full! Try again in a few seconds.');
else
alert('Game has already begun! Try again later.');
} else {
this.enterQueue();
@ -140,7 +145,7 @@ export class LoginComponent implements OnInit {
let ngZone = this.ngZone;
let router = this.router;
try {
let party: any = await this.http.post(`${environment.API_URL_PARTY}/create`, '', { responseType: 'json' }).toPromise();
let party: any = await this.http.post(`${environment.API_URL_PARTY}/create`, '', { responseType: 'json' }).toPromise();
console.log(`Created round with id=${party}`);
sessionStorage.setItem('isSpectator', 'true');
@ -158,36 +163,44 @@ export class LoginComponent implements OnInit {
this.pane = 'center';
}
enterQueue() {
console.log(`enering queue for party ${this.party}`);
if (this.queueCallback)
this.queueCallback.close();
this.queueCallback = new EventSource(`${environment.API_URL_PARTY}/${this.party}/queue`);
this.queueCallback.onmessage = msg => {
setQueueOnMessage() {
LoginComponent.queueCallback.onmessage = msg => {
let queueMsg = JSON.parse(msg.data);
if (queueMsg.queuePosition) {
this.ngZone.run(() => {
this.queuePosition = queueMsg.queuePosition;
console.log(`Still waiting in queue at position ${this.queuePosition}`);
this.pane = 'queue';
});
this.showQueue(queueMsg.queuePosition);
} else if (queueMsg.requeue) {
console.log(`ready to join game! Joining round ${queueMsg.requeue}`);
this.queueCallback.close();
LoginComponent.queueCallback.close();
this.joinRoundById(queueMsg.requeue);
} else {
console.log('Error: unrecognized message ' + msg.data);
console.log('Error: unrecognized message ' + msg.data);
}
}
this.queueCallback.onerror = msg => {
}
enterQueue() {
console.log(`enering queue for party ${this.party}`);
if (LoginComponent.queueCallback)
LoginComponent.queueCallback.close();
LoginComponent.queueCallback = new EventSource(`${environment.API_URL_PARTY}/${this.party}/queue`);
this.setQueueOnMessage();
LoginComponent.queueCallback.onerror = msg => {
console.log('Error showing queue position: ' + JSON.stringify(msg.data));
}
}
showQueue(queuePosition) {
this.ngZone.run(() => {
this.queuePosition = queuePosition;
console.log(`Still waiting in queue at position ${this.queuePosition}`);
this.pane = 'queue';
});
}
cancelQueue() {
if (this.queueCallback)
if (LoginComponent.queueCallback)
try {
this.queueCallback.close();
LoginComponent.queueCallback.close();
this.pane = 'right';
} catch (ignore) {
}

View File

@ -371,8 +371,18 @@ public class GameRound implements Runnable {
broadcastPlayerList();
long start = System.nanoTime();
updatePlayerStats();
lifecycleCallbacks.forEach(c -> c.gameEnding());
try {
ManagedScheduledExecutorService exec = InitialContext.doLookup("java:comp/DefaultManagedScheduledExecutorService");
exec.submit(() -> {
updatePlayerStats();
});
exec.submit(() -> {
lifecycleCallbacks.forEach(c -> c.gameEnding());
});
} catch (NamingException e) {
log("Unable to perform post game processing");
e.printStackTrace();
}
// Wait for 5 seconds, but subtract the amount of time it took to update player stats
long nanoWait = TimeUnit.SECONDS.toNanos(5) - (System.nanoTime() - start);

View File

@ -11,18 +11,13 @@ public class OutboundMessage {
public final Player[] playerlist;
public PlayerList(Set<Player> players) {
if (players.size() > 0) {
// Send players in proper order, padding out empty slots with "Bot Player"
playerlist = new Player[Player.MAX_PLAYERS];
for (Player p : players)
playerlist[p.playerNum] = p;
for (int i = 0; i < Player.MAX_PLAYERS; i++)
if (playerlist[i] == null)
playerlist[i] = new Player("", "Bot Player", (short) i, 0, 0);
} else {
// If no players are in the game yet, do not show all bot players
playerlist = new Player[0];
}
// Send players in proper order, padding out empty slots with "Bot Player"
playerlist = new Player[Player.MAX_PLAYERS];
for (Player p : players)
playerlist[p.playerNum] = p;
for (int i = 0; i < Player.MAX_PLAYERS; i++)
if (playerlist[i] == null)
playerlist[i] = new Player("", "Bot Player", (short) i, 0, 0);
}
}